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.io.Writer;
029    import java.util.Properties;
030    import java.util.jar.JarFile;
031    
032    import javax.enterprise.deploy.shared.factories.DeploymentFactoryManager;
033    import javax.enterprise.deploy.spi.DeploymentManager;
034    import javax.enterprise.deploy.spi.exceptions.DeploymentManagerCreationException;
035    import javax.enterprise.deploy.spi.factories.DeploymentFactory;
036    
037    import org.apache.geronimo.cli.deployer.ConnectionParams;
038    import org.apache.geronimo.common.DeploymentException;
039    import org.apache.geronimo.deployment.plugin.factories.AuthenticationFailedException;
040    import org.apache.geronimo.deployment.plugin.jmx.JMXDeploymentManager;
041    import org.apache.geronimo.deployment.plugin.jmx.LocalDeploymentManager;
042    import org.apache.geronimo.kernel.Kernel;
043    import org.apache.geronimo.crypto.EncryptionManager;
044    
045    /**
046     * Supports online connections to the server, via JSR-88, valid only
047     * when the server is online.
048     *
049     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
050     */
051    public class ServerConnection {
052    
053        private final static String DEFAULT_URI = "deployer:geronimo:jmx";
054        private final static String DEFAULT_SECURE_URI = "deployer:geronimo:jmxs";
055    
056        private final DeploymentFactory geronimoDeploymentFactory;
057    
058        private DeploymentManager manager;
059        private Writer out;
060        private InputStream in;
061        private SavedAuthentication auth;
062        private boolean logToSysErr;
063        private boolean verboseMessages;
064    
065        public ServerConnection(ConnectionParams params, PrintWriter out, InputStream in, Kernel kernel, DeploymentFactory geronimoDeploymentFactory) throws DeploymentException {
066            if (null == kernel) {
067                throw new IllegalArgumentException("kernel is required");
068            }
069            this.geronimoDeploymentFactory = geronimoDeploymentFactory;
070    
071            this.out = out;
072            this.in = in;        
073    
074            String uri = params.getURI();
075            String driver = params.getDriver();
076            String user = params.getUser();
077            String password = params.getPassword();
078            String host = params.getHost();
079            Integer port = params.getPort();
080            verboseMessages = params.isVerbose();
081            logToSysErr = params.isSyserr();
082            boolean offline = params.isOffline();
083            boolean secure = params.isSecure();
084            
085            if ((driver != null) && uri == null) {
086                throw new DeploymentSyntaxException("A custom driver requires a custom URI");
087            }
088            if (host != null || port != null) {
089                uri = getDefaultURI(secure) + "://" + (host == null ? "" : host) + (port == null ? "" : ":" + port);
090            }
091            if (offline) {
092                startOfflineDeployer(kernel);
093                manager = new LocalDeploymentManager(kernel);
094            } else {
095                tryToConnect(uri, driver, user, password, secure);
096            }
097            if (manager == null) {
098                throw new DeploymentException("Unexpected error; connection failed.");
099            }
100        }
101    
102        protected void startOfflineDeployer(Kernel kernel) throws DeploymentException {
103            OfflineDeployerStarter offlineDeployerStarter = new OfflineDeployerStarter(kernel);
104            offlineDeployerStarter.start();
105        }
106    
107        private static String getDefaultURI(boolean secure) {
108            return (secure) ? DEFAULT_SECURE_URI : DEFAULT_URI;
109        }
110        
111        public void close() throws DeploymentException {
112            if (manager != null) {
113                manager.release();
114            }
115        }
116    
117        Serializable getAuthentication() {
118            return auth;
119        }
120    
121        String getServerURI() {
122            return auth.uri;
123        }
124    
125        private void tryToConnect(String argURI, String driver, String user, String password, boolean secure) throws DeploymentException {
126            DeploymentFactoryManager mgr = DeploymentFactoryManager.getInstance();
127            if (driver != null) {
128                loadDriver(driver, mgr);
129            } else {
130                mgr.registerDeploymentFactory(geronimoDeploymentFactory);
131            }
132            String useURI = argURI == null ? getDefaultURI(secure) : argURI;
133    
134            if (user == null && password == null) {
135                InputStream in;
136                // First check for .geronimo-deployer on class path (e.g. packaged in deployer.jar)
137                in = ServerConnection.class.getResourceAsStream("/.geronimo-deployer");
138                // If not there, check in home directory
139                if (in == null) {
140                    File authFile = new File(System.getProperty("user.home"), ".geronimo-deployer");
141                    if (authFile.exists() && authFile.canRead()) {
142                        try {
143                            in = new BufferedInputStream(new FileInputStream(authFile));
144                        } catch (FileNotFoundException e) {
145                            // ignore
146                        }
147                    }
148                }
149                if (in != null) {
150                    try {
151                        Properties props = new Properties();
152                        props.load(in);
153                        String encrypted = props.getProperty("login." + useURI);
154                        if (encrypted != null) {
155    
156                            if (encrypted.startsWith("{Plain}")) {
157                                int pos = encrypted.indexOf("/");
158                                user = encrypted.substring(7, pos);
159                                password = encrypted.substring(pos + 1);
160                            } else {
161                                Object o = EncryptionManager.decrypt(encrypted);
162                                if (o == encrypted) {
163                                    System.out.print(DeployUtils.reformat("Unknown encryption used in saved login file", 4, 72));
164                                } else {
165                                    SavedAuthentication auth = (SavedAuthentication) o;
166                                    if (auth.uri.equals(useURI)) {
167                                        user = auth.user;
168                                        password = new String(auth.password);
169                                    }
170                                }
171                            }
172                        }
173                    } catch (IOException e) {
174                        System.out.print(DeployUtils.reformat("Unable to read authentication from saved login file: " + e.getMessage(), 4, 72));
175                    } finally {
176                        try {
177                            in.close();
178                        } catch (IOException e) {
179                            // ingore
180                        }
181                    }
182                }
183            }
184    
185            if (user == null || password == null) {
186                try {
187                    InputPrompt prompt = new InputPrompt(in, out);
188                    if (user == null) {
189                        user = prompt.getInput("Username: ");
190                    }
191                    if (password == null) {
192                        password = prompt.getPassword("Password: ");
193                    }
194                } catch (IOException e) {
195                    throw new DeploymentException("Unable to prompt for login", e);
196                }
197            }
198            try {
199                manager = mgr.getDeploymentManager(useURI, user, password);
200                auth = new SavedAuthentication(useURI, user, password == null ? null : password.toCharArray());
201            } catch (AuthenticationFailedException e) {
202                // server's there, you just can't talk to it
203                throw new DeploymentException("Login Failed");
204            } catch (DeploymentManagerCreationException e) {
205                throw new DeploymentException("Unable to connect to server at " + useURI + " -- " + e.getMessage(), e);
206            }
207    
208            if (manager instanceof JMXDeploymentManager) {
209                JMXDeploymentManager deploymentManager = (JMXDeploymentManager) manager;
210                deploymentManager.setLogConfiguration(logToSysErr, verboseMessages);
211            }
212        }
213    
214        private void loadDriver(String driver, DeploymentFactoryManager mgr) throws DeploymentException {
215            File file = new File(driver);
216            if (!file.exists() || !file.canRead() || !DeployUtils.isJarFile(file)) {
217                throw new DeploymentSyntaxException("Driver '" + file.getAbsolutePath() + "' is not a readable JAR file");
218            }
219            String className = null;
220            try {
221                JarFile jar = new JarFile(file);
222                className = jar.getManifest().getMainAttributes().getValue("J2EE-DeploymentFactory-Implementation-Class");
223                if (className == null) {
224                    throw new DeploymentException("The driver JAR " + file.getAbsolutePath() + " does not specify a J2EE-DeploymentFactory-Implementation-Class; cannot load driver.");
225                }
226                jar.close();
227                DeploymentFactory factory = (DeploymentFactory) Class.forName(className).newInstance();
228                mgr.registerDeploymentFactory(factory);
229            } catch (DeploymentException e) {
230                throw e;
231            } catch (Exception e) {
232                throw new DeploymentSyntaxException("Unable to load driver class " + className + " from JAR " + file.getAbsolutePath(), e);
233            }
234        }
235    
236        public DeploymentManager getDeploymentManager() {
237            return manager;
238        }
239    
240        public boolean isGeronimo() {
241            return manager.getClass().getName().startsWith("org.apache.geronimo.");
242        }
243    
244        private final static class SavedAuthentication implements Serializable {
245            private String uri;
246            private String user;
247            private char[] password;
248    
249            public SavedAuthentication(String uri, String user, char[] password) {
250                this.uri = uri;
251                this.user = user;
252                this.password = password;
253            }
254        }
255    }