001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package org.apache.geronimo.deployment.cli;
019    
020    import java.io.BufferedInputStream;
021    import java.io.File;
022    import java.io.FileInputStream;
023    import java.io.FileNotFoundException;
024    import java.io.IOException;
025    import java.io.InputStream;
026    import java.io.PrintWriter;
027    import java.io.Serializable;
028    import java.util.Properties;
029    import java.util.jar.JarFile;
030    
031    import javax.enterprise.deploy.shared.factories.DeploymentFactoryManager;
032    import javax.enterprise.deploy.spi.DeploymentManager;
033    import javax.enterprise.deploy.spi.exceptions.DeploymentManagerCreationException;
034    import javax.enterprise.deploy.spi.factories.DeploymentFactory;
035    
036    import org.apache.geronimo.cli.deployer.DeployerCLParser;
037    import org.apache.geronimo.common.DeploymentException;
038    import org.apache.geronimo.deployment.plugin.factories.AuthenticationFailedException;
039    import org.apache.geronimo.deployment.plugin.jmx.JMXDeploymentManager;
040    import org.apache.geronimo.deployment.plugin.jmx.LocalDeploymentManager;
041    import org.apache.geronimo.kernel.Kernel;
042    import org.apache.geronimo.util.SimpleEncryption;
043    
044    /**
045     * Supports online connections to the server, via JSR-88, valid only
046     * when the server is online.
047     *
048     * @version $Rev: 549455 $ $Date: 2007-06-21 08:12:27 -0400 (Thu, 21 Jun 2007) $
049     */
050    public class ServerConnection {
051    
052        private final static String DEFAULT_URI = "deployer:geronimo:jmx";
053    
054        private final DeploymentFactory geronimoDeploymentFactory;
055        
056        private DeploymentManager manager;
057        private PrintWriter out;
058        private InputStream in;
059        private SavedAuthentication auth;
060        private boolean logToSysErr;
061        private boolean verboseMessages;
062    
063        public ServerConnection(DeployerCLParser parser, PrintWriter out, InputStream in, Kernel kernel, DeploymentFactory geronimoDeploymentFactory) throws DeploymentException {
064            if (null == kernel) {
065                throw new IllegalArgumentException("kernel is required");
066            }
067            this.geronimoDeploymentFactory = geronimoDeploymentFactory;
068    
069            this.out = out;
070            this.in = in;
071            boolean offline = false;
072            
073            String uri = parser.getURI();
074            String driver = parser.getDriver();
075            String user = parser.getUser();
076            String password = parser.getPassword();
077            String host = parser.getHost();
078            Integer port = parser.getPort();
079            verboseMessages = parser.isVerbose();
080            logToSysErr = parser.isSyserr();
081            offline = parser.isOffline();
082    
083            if ((driver != null) && uri == null) {
084                throw new DeploymentSyntaxException("A custom driver requires a custom URI");
085            }
086            if (host != null || port != null) {
087                uri = DEFAULT_URI + "://" + (host == null ? "" : host) + (port == null ? "" : ":" + port);
088            }
089            if (offline) {
090                startOfflineDeployer(kernel);
091                manager = new LocalDeploymentManager(kernel);
092            } else {
093                tryToConnect(uri, driver, user, password, true);
094            }
095            if (manager == null) {
096                throw new DeploymentException("Unexpected error; connection failed.");
097            }
098        }
099    
100        protected void startOfflineDeployer(Kernel kernel) throws DeploymentException {
101            OfflineDeployerStarter offlineDeployerStarter = new OfflineDeployerStarter(kernel);
102            offlineDeployerStarter.start();
103        }
104    
105        public void close() throws DeploymentException {
106            if (manager != null) {
107                manager.release();
108            }
109        }
110    
111        Serializable getAuthentication() {
112            return auth;
113        }
114    
115        String getServerURI() {
116            return auth.uri;
117        }
118    
119        private void tryToConnect(String argURI, String driver, String user, String password, boolean authPrompt) throws DeploymentException {
120            DeploymentFactoryManager mgr = DeploymentFactoryManager.getInstance();
121            if (driver != null) {
122                loadDriver(driver, mgr);
123            } else {
124                mgr.registerDeploymentFactory(geronimoDeploymentFactory);
125            }
126            String useURI = argURI == null ? DEFAULT_URI : argURI;
127    
128            if (authPrompt && user == null && password == null) {
129                InputStream in;
130                // First check for .geronimo-deployer on class path (e.g. packaged in deployer.jar)
131                in = ServerConnection.class.getResourceAsStream("/.geronimo-deployer");
132                // If not there, check in home directory
133                if (in == null) {
134                    File authFile = new File(System.getProperty("user.home"), ".geronimo-deployer");
135                    if (authFile.exists() && authFile.canRead()) {
136                        try {
137                            in = new BufferedInputStream(new FileInputStream(authFile));
138                        } catch (FileNotFoundException e) {
139                            // ignore
140                        }
141                    }
142                }
143                if (in != null) {
144                    try {
145                        Properties props = new Properties();
146                        props.load(in);
147                        String encryped = props.getProperty("login." + useURI);
148                        if (encryped != null) {
149                            if (encryped.startsWith("{Standard}")) {
150                                SavedAuthentication auth = (SavedAuthentication) SimpleEncryption.decrypt(encryped.substring(10));
151                                if (auth.uri.equals(useURI)) {
152                                    user = auth.user;
153                                    password = new String(auth.password);
154                                }
155                            } else if (encryped.startsWith("{Plain}")) {
156                                int pos = encryped.indexOf("/");
157                                user = encryped.substring(7, pos);
158                                password = encryped.substring(pos + 1);
159                            } else {
160                                System.out.print(DeployUtils.reformat("Unknown encryption used in saved login file", 4, 72));
161                            }
162                        }
163                    } catch (IOException e) {
164                        System.out.print(DeployUtils.reformat("Unable to read authentication from saved login file: " + e.getMessage(), 4, 72));
165                    } finally {
166                        try {
167                            in.close();
168                        } catch (IOException e) {
169                            // ingore
170                        }
171                    }
172                }
173            }
174    
175            if (authPrompt && !useURI.equals(DEFAULT_URI) && user == null && password == null) {
176                // Non-standard URI, but no authentication information
177                doAuthPromptAndRetry(useURI, user, password);
178                return;
179            } else { // Standard URI with no auth, Non-standard URI with auth, or else this is the 2nd try already
180                try {
181                    manager = mgr.getDeploymentManager(useURI, user, password);
182                    auth = new SavedAuthentication(useURI, user, password == null ? null : password.toCharArray());
183                } catch (AuthenticationFailedException e) { // server's there, you just can't talk to it
184                    if (authPrompt) {
185                        doAuthPromptAndRetry(useURI, user, password);
186                        return;
187                    } else {
188                        throw new DeploymentException("Login Failed");
189                    }
190                } catch (DeploymentManagerCreationException e) {
191                    throw new DeploymentException("Unable to connect to server at " + useURI + " -- " + e.getMessage(), e);
192                }
193            }
194    
195            if (manager instanceof JMXDeploymentManager) {
196                JMXDeploymentManager deploymentManager = (JMXDeploymentManager) manager;
197                deploymentManager.setLogConfiguration(logToSysErr, verboseMessages);
198            }
199        }
200    
201        private void loadDriver(String driver, DeploymentFactoryManager mgr) throws DeploymentException {
202            File file = new File(driver);
203            if (!file.exists() || !file.canRead() || !DeployUtils.isJarFile(file)) {
204                throw new DeploymentSyntaxException("Driver '" + file.getAbsolutePath() + "' is not a readable JAR file");
205            }
206            String className = null;
207            try {
208                JarFile jar = new JarFile(file);
209                className = jar.getManifest().getMainAttributes().getValue("J2EE-DeploymentFactory-Implementation-Class");
210                if (className == null) {
211                    throw new DeploymentException("The driver JAR " + file.getAbsolutePath() + " does not specify a J2EE-DeploymentFactory-Implementation-Class; cannot load driver.");
212                }
213                jar.close();
214                DeploymentFactory factory = (DeploymentFactory) Class.forName(className).newInstance();
215                mgr.registerDeploymentFactory(factory);
216            } catch (DeploymentException e) {
217                throw e;
218            } catch (Exception e) {
219                throw new DeploymentSyntaxException("Unable to load driver class " + className + " from JAR " + file.getAbsolutePath(), e);
220            }
221        }
222    
223        private void doAuthPromptAndRetry(String uri, String user, String password) throws DeploymentException {
224            try {
225                InputPrompt prompt = new InputPrompt(in, out);
226                if (user == null) {
227                    user = prompt.getInput("Username: ");
228                }
229                if (password == null) {
230                    password = prompt.getPassword("Password: ");
231                }
232            } catch (IOException e) {
233                throw new DeploymentException("Unable to prompt for login", e);
234            }
235            tryToConnect(uri, null, user, password, false);
236        }
237    
238        public DeploymentManager getDeploymentManager() {
239            return manager;
240        }
241    
242        public boolean isGeronimo() {
243            return manager.getClass().getName().startsWith("org.apache.geronimo.");
244        }
245    
246        private final static class SavedAuthentication implements Serializable {
247            private String uri;
248            private String user;
249            private char[] password;
250    
251            public SavedAuthentication(String uri, String user, char[] password) {
252                this.uri = uri;
253                this.user = user;
254                this.password = password;
255            }
256        }
257    }