View Javadoc

1   /**
2    *
3    *  Licensed to the Apache Software Foundation (ASF) under one or more
4    *  contributor license agreements.  See the NOTICE file distributed with
5    *  this work for additional information regarding copyright ownership.
6    *  The ASF licenses this file to You under the Apache License, Version 2.0
7    *  (the "License"); you may not use this file except in compliance with
8    *  the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing, software
13   *  distributed under the License is distributed on an "AS IS" BASIS,
14   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   *  See the License for the specific language governing permissions and
16   *  limitations under the License.
17   */
18  
19  package org.apache.geronimo.deployment.cli;
20  
21  import java.io.BufferedInputStream;
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.PrintWriter;
27  import java.io.Serializable;
28  import java.io.FileNotFoundException;
29  import java.util.LinkedHashMap;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Properties;
33  import java.util.jar.JarFile;
34  
35  import javax.enterprise.deploy.shared.factories.DeploymentFactoryManager;
36  import javax.enterprise.deploy.spi.DeploymentManager;
37  import javax.enterprise.deploy.spi.exceptions.DeploymentManagerCreationException;
38  import javax.enterprise.deploy.spi.factories.DeploymentFactory;
39  
40  import org.apache.geronimo.common.DeploymentException;
41  import org.apache.geronimo.deployment.plugin.factories.AuthenticationFailedException;
42  import org.apache.geronimo.deployment.plugin.factories.DeploymentFactoryImpl;
43  import org.apache.geronimo.deployment.plugin.jmx.JMXDeploymentManager;
44  import org.apache.geronimo.deployment.plugin.jmx.LocalDeploymentManager;
45  import org.apache.geronimo.util.SimpleEncryption;
46  import org.apache.geronimo.kernel.Kernel;
47  import org.apache.geronimo.kernel.config.ConfigurationManager;
48  import org.apache.geronimo.kernel.config.ConfigurationUtil;
49  import org.apache.geronimo.system.main.LocalServer;
50  
51  /**
52   * Supports online connections to the server, via JSR-88, valid only
53   * when the server is online.
54   *
55   * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
56   */
57  public class ServerConnection {
58      private final static Map OPTION_HELP = new LinkedHashMap(9);
59  
60      static {
61          OPTION_HELP.put("--uri", "A URI to contact the server.  If not specified, the deployer defaults to " +
62                  "operating on a Geronimo server running on the standard port on localhost.\n" +
63                  "A URI to connect to Geronimo (including optional host and port parameters) has the form: " +
64                  "deployer:geronimo:jmx[://host[:port]] (though you could also just use --host and --port instead).");
65          OPTION_HELP.put("--host", "The host name of a Geronimo server to deploy to.  This option is " +
66                  "not compatible with --uri, but is often used with --port.");
67          OPTION_HELP.put("--port", "The RMI listen port of a Geronimo server to deploy to.  This option is " +
68                  "not compatible with --uri, but is often used with --host.  The default port is 1099.");
69          OPTION_HELP.put("--driver", "If you want to use this tool with a server other than Geronimo, " +
70                  "then you must provide the path to its driver JAR.  Currently, manifest " +
71                  "Class-Path entries in that JAR are ignored.");
72          OPTION_HELP.put("--user", "If the deployment operation requires authentication, then you can " +
73                  "specify the username to use to connect.  If no password is specified, the " +
74                  "deployer will attempt to connect to the server with no password, and if " +
75                  "that fails, will prompt you for a password.");
76          OPTION_HELP.put("--password", "Specifies a password to use to authenticate to the server.");
77          OPTION_HELP.put("--syserr", "Enables error logging to syserr.  Disabled by default.");
78          OPTION_HELP.put("--verbose", "Enables verbose execution mode.  Disabled by default.");
79          OPTION_HELP.put("--offline", "Deploy offline to a local server, using whatever deployers are available in the local server");
80      }
81  
82      public static Map getOptionHelp() {
83          return OPTION_HELP;
84      }
85  
86      /**
87       * Checks whether the stated command-line argument is a general argument (which
88       * may be the general argument itself, or a required parameter after the general
89       * argument).  For example, if the arguments were "--user bob foo" then
90       * this should return true for "--user" and "bob" and false for "foo".
91       *
92       * @param args   The previous arguments on the command line
93       * @param option The argument we're checking at the moment
94       * @return True if the argument we're checking is part of a general argument
95       */
96      public static boolean isGeneralOption(List args, String option) {
97          if (OPTION_HELP.containsKey(option) || option.equals("--url")) {
98              return true;
99          }
100         if (args.size() == 0) {
101             return false;
102         }
103         String last = (String) args.get(args.size() - 1);
104         return last.equals("--uri") || last.equals("--url") || last.equals("--driver") || last.equals("--user") ||
105                 last.equals("--password") || last.equals("--host") || last.equals("--port");
106     }
107 
108     private final static String DEFAULT_URI = "deployer:geronimo:jmx";
109 
110     private DeploymentManager manager;
111     private PrintWriter out;
112     private InputStream in;
113     private SavedAuthentication auth;
114     private boolean logToSysErr;
115     private boolean verboseMessages;
116 
117     public ServerConnection(String[] args, PrintWriter out, InputStream in) throws DeploymentException {
118         String uri = null, driver = null, user = null, password = null, host = null;
119         Integer port = null;
120         this.out = out;
121         this.in = in;
122         boolean offline = false;
123         for (int i = 0; i < args.length; i++) {
124             String arg = args[i];
125             if (arg.equals("--uri") || arg.equals("--url")) {
126                 if (uri != null) {
127                     throw new DeploymentSyntaxException("Cannot specify more than one URI");
128                 } else if (i >= args.length - 1) {
129                     throw new DeploymentSyntaxException("Must specify a URI (e.g. --uri deployer:...)");
130                 }
131                 if (host != null || port != null) {
132                     throw new DeploymentSyntaxException("Cannot specify a URI as well as a host/port");
133                 }
134                 uri = args[++i];
135             } else if (arg.equals("--host")) {
136                 if (host != null) {
137                     throw new DeploymentSyntaxException("Cannot specify more than one host");
138                 } else if (i >= args.length - 1) {
139                     throw new DeploymentSyntaxException("Must specify a hostname (e.g. --host localhost)");
140                 }
141                 if (uri != null) {
142                     throw new DeploymentSyntaxException("Cannot specify a URI as well as a host/port");
143                 }
144                 host = args[++i];
145             } else if (arg.equals("--port")) {
146                 if (port != null) {
147                     throw new DeploymentSyntaxException("Cannot specify more than one port");
148                 } else if (i >= args.length - 1) {
149                     throw new DeploymentSyntaxException("Must specify a port (e.g. --port 1099)");
150                 }
151                 if (uri != null) {
152                     throw new DeploymentSyntaxException("Cannot specify a URI as well as a host/port");
153                 }
154                 try {
155                     port = new Integer(args[++i]);
156                 } catch (NumberFormatException e) {
157                     throw new DeploymentSyntaxException("Port must be a number (" + e.getMessage() + ")");
158                 }
159             } else if (arg.equals("--driver")) {
160                 if (driver != null) {
161                     throw new DeploymentSyntaxException("Cannot specify more than one driver");
162                 } else if (i >= args.length - 1) {
163                     throw new DeploymentSyntaxException("Must specify a driver JAR (--driver jarfile)");
164                 }
165                 driver = args[++i];
166             } else if (arg.equals("--offline")) {
167                 //throw new DeploymentSyntaxException("This tool no longer handles offline deployment");
168                 offline = true;
169             } else if (arg.equals("--user")) {
170                 if (user != null) {
171                     throw new DeploymentSyntaxException("Cannot specify more than one user name");
172                 } else if (i >= args.length - 1) {
173                     throw new DeploymentSyntaxException("Must specify a username (--user username)");
174                 }
175                 user = args[++i];
176             } else if (arg.equals("--password")) {
177                 if (password != null) {
178                     throw new DeploymentSyntaxException("Cannot specify more than one password");
179                 } else if (i >= args.length - 1) {
180                     throw new DeploymentSyntaxException("Must specify a password (--password password)");
181                 }
182                 password = args[++i];
183             } else if (arg.equals("--verbose")) {
184                 verboseMessages = true;
185             } else if (arg.equals("--syserr")) {
186                 logToSysErr = true;
187             } else {
188                 throw new DeploymentException("Invalid option " + arg);
189             }
190         }
191         if ((driver != null) && uri == null) {
192             throw new DeploymentSyntaxException("A custom driver requires a custom URI");
193         }
194         if (host != null || port != null) {
195             uri = DEFAULT_URI + "://" + (host == null ? "" : host) + (port == null ? "" : ":" + port);
196         }
197         if (offline) {
198             LocalServer localServer;
199             try {
200                 localServer = new LocalServer("org.apache.geronimo.configs/j2ee-system//car", "var/config/offline-deployer-list");
201             } catch (Exception e) {
202                 throw new DeploymentException("Could not start local server", e);
203             }
204             Kernel kernel = localServer.getKernel();
205             ConfigurationManager configurationManager = ConfigurationUtil.getConfigurationManager(kernel);
206             configurationManager.setOnline(false);
207 
208             manager = new LocalDeploymentManager(localServer.getKernel());
209         } else {
210             tryToConnect(uri, driver, user, password, true);
211         }
212         if (manager == null) {
213             throw new DeploymentException("Unexpected error; connection failed.");
214         }
215     }
216 
217     public void close() throws DeploymentException {
218         if (manager != null) {
219             manager.release();
220         }
221     }
222 
223     Serializable getAuthentication() {
224         return auth;
225     }
226 
227     String getServerURI() {
228         return auth.uri;
229     }
230 
231     private void tryToConnect(String argURI, String driver, String user, String password, boolean authPrompt) throws DeploymentException {
232         DeploymentFactoryManager mgr = DeploymentFactoryManager.getInstance();
233         if (driver != null) {
234             loadDriver(driver, mgr);
235         } else {
236             mgr.registerDeploymentFactory(new DeploymentFactoryImpl());
237         }
238         String useURI = argURI == null ? DEFAULT_URI : argURI;
239 
240         if (authPrompt && user == null && password == null) {
241             InputStream in;
242             // First check for .geronimo-deployer on class path (e.g. packaged in deployer.jar)
243             in = ServerConnection.class.getResourceAsStream("/.geronimo-deployer");
244             // If not there, check in home directory
245             if (in == null) {
246                 File authFile = new File(System.getProperty("user.home"), ".geronimo-deployer");
247                 if (authFile.exists() && authFile.canRead()) {
248                     try {
249                         in = new BufferedInputStream(new FileInputStream(authFile));
250                     } catch (FileNotFoundException e) {
251                         // ignore
252                     }
253                 }
254             }
255             if (in != null) {
256                 try {
257                     Properties props = new Properties();
258                     props.load(in);
259                     String encryped = props.getProperty("login." + useURI);
260                     if (encryped != null) {
261                         if (encryped.startsWith("{Standard}")) {
262                             SavedAuthentication auth = (SavedAuthentication) SimpleEncryption.decrypt(encryped.substring(10));
263                             if (auth.uri.equals(useURI)) {
264                                 user = auth.user;
265                                 password = new String(auth.password);
266                             }
267                         } else if (encryped.startsWith("{Plain}")) {
268                             int pos = encryped.indexOf("/");
269                             user = encryped.substring(7, pos);
270                             password = encryped.substring(pos + 1);
271                         } else {
272                             System.out.print(DeployUtils.reformat("Unknown encryption used in saved login file", 4, 72));
273                         }
274                     }
275                 } catch (IOException e) {
276                     System.out.print(DeployUtils.reformat("Unable to read authentication from saved login file: " + e.getMessage(), 4, 72));
277                 } finally {
278                     try {
279                         in.close();
280                     } catch (IOException e) {
281                         // ingore
282                     }
283                 }
284             }
285         }
286 
287         if (authPrompt && !useURI.equals(DEFAULT_URI) && user == null && password == null) {
288             // Non-standard URI, but no authentication information
289             doAuthPromptAndRetry(useURI, user, password);
290             return;
291         } else { // Standard URI with no auth, Non-standard URI with auth, or else this is the 2nd try already
292             try {
293                 manager = mgr.getDeploymentManager(useURI, user, password);
294                 auth = new SavedAuthentication(useURI, user, password == null ? null : password.toCharArray());
295             } catch (AuthenticationFailedException e) { // server's there, you just can't talk to it
296                 if (authPrompt) {
297                     doAuthPromptAndRetry(useURI, user, password);
298                     return;
299                 } else {
300                     throw new DeploymentException("Login Failed");
301                 }
302             } catch (DeploymentManagerCreationException e) {
303                 throw new DeploymentException("Unable to connect to server at " + useURI + " -- " + e.getMessage());
304             }
305         }
306 
307         if (manager instanceof JMXDeploymentManager) {
308             JMXDeploymentManager deploymentManager = (JMXDeploymentManager) manager;
309             deploymentManager.setLogConfiguration(logToSysErr, verboseMessages);
310         }
311     }
312 
313     private void loadDriver(String driver, DeploymentFactoryManager mgr) throws DeploymentException {
314         File file = new File(driver);
315         if (!file.exists() || !file.canRead() || !DeployUtils.isJarFile(file)) {
316             throw new DeploymentSyntaxException("Driver '" + file.getAbsolutePath() + "' is not a readable JAR file");
317         }
318         String className = null;
319         try {
320             JarFile jar = new JarFile(file);
321             className = jar.getManifest().getMainAttributes().getValue("J2EE-DeploymentFactory-Implementation-Class");
322             if (className == null) {
323                 throw new DeploymentException("The driver JAR " + file.getAbsolutePath() + " does not specify a J2EE-DeploymentFactory-Implementation-Class; cannot load driver.");
324             }
325             jar.close();
326             DeploymentFactory factory = (DeploymentFactory) Class.forName(className).newInstance();
327             mgr.registerDeploymentFactory(factory);
328         } catch (DeploymentException e) {
329             throw e;
330         } catch (Exception e) {
331             throw new DeploymentSyntaxException("Unable to load driver class " + className + " from JAR " + file.getAbsolutePath(), e);
332         }
333     }
334 
335     private void doAuthPromptAndRetry(String uri, String user, String password) throws DeploymentException {
336         try {
337             InputPrompt prompt = new InputPrompt(in, out);
338             if (user == null) {
339                 user = prompt.getInput("Username: ");
340             }
341             if (password == null) {
342                 password = prompt.getPassword("Password: ");
343             }
344         } catch (IOException e) {
345             throw new DeploymentException("Unable to prompt for login", e);
346         }
347         tryToConnect(uri, null, user, password, false);
348     }
349 
350     public DeploymentManager getDeploymentManager() {
351         return manager;
352     }
353 
354     public boolean isGeronimo() {
355         return manager.getClass().getName().startsWith("org.apache.geronimo.");
356     }
357 
358     private final static class SavedAuthentication implements Serializable {
359         private String uri;
360         private String user;
361         private char[] password;
362 
363         public SavedAuthentication(String uri, String user, char[] password) {
364             this.uri = uri;
365             this.user = user;
366             this.password = password;
367         }
368     }
369 }