001 /** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one or more 004 * contributor license agreements. See the NOTICE file distributed with 005 * this work for additional information regarding copyright ownership. 006 * The ASF licenses this file to You under the Apache License, Version 2.0 007 * (the "License"); you may not use this file except in compliance with 008 * the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package org.apache.geronimo.deployment.cli; 020 021 import java.io.BufferedInputStream; 022 import java.io.File; 023 import java.io.FileInputStream; 024 import java.io.IOException; 025 import java.io.InputStream; 026 import java.io.PrintWriter; 027 import java.io.Serializable; 028 import java.io.FileNotFoundException; 029 import java.util.LinkedHashMap; 030 import java.util.List; 031 import java.util.Map; 032 import java.util.Properties; 033 import java.util.jar.JarFile; 034 035 import javax.enterprise.deploy.shared.factories.DeploymentFactoryManager; 036 import javax.enterprise.deploy.spi.DeploymentManager; 037 import javax.enterprise.deploy.spi.exceptions.DeploymentManagerCreationException; 038 import javax.enterprise.deploy.spi.factories.DeploymentFactory; 039 040 import org.apache.geronimo.common.DeploymentException; 041 import org.apache.geronimo.deployment.plugin.factories.AuthenticationFailedException; 042 import org.apache.geronimo.deployment.plugin.factories.DeploymentFactoryImpl; 043 import org.apache.geronimo.deployment.plugin.jmx.JMXDeploymentManager; 044 import org.apache.geronimo.deployment.plugin.jmx.LocalDeploymentManager; 045 import org.apache.geronimo.util.SimpleEncryption; 046 import org.apache.geronimo.kernel.Kernel; 047 import org.apache.geronimo.kernel.config.ConfigurationManager; 048 import org.apache.geronimo.kernel.config.ConfigurationUtil; 049 import org.apache.geronimo.system.main.LocalServer; 050 051 /** 052 * Supports online connections to the server, via JSR-88, valid only 053 * when the server is online. 054 * 055 * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $ 056 */ 057 public class ServerConnection { 058 private final static Map OPTION_HELP = new LinkedHashMap(9); 059 060 static { 061 OPTION_HELP.put("--uri", "A URI to contact the server. If not specified, the deployer defaults to " + 062 "operating on a Geronimo server running on the standard port on localhost.\n" + 063 "A URI to connect to Geronimo (including optional host and port parameters) has the form: " + 064 "deployer:geronimo:jmx[://host[:port]] (though you could also just use --host and --port instead)."); 065 OPTION_HELP.put("--host", "The host name of a Geronimo server to deploy to. This option is " + 066 "not compatible with --uri, but is often used with --port."); 067 OPTION_HELP.put("--port", "The RMI listen port of a Geronimo server to deploy to. This option is " + 068 "not compatible with --uri, but is often used with --host. The default port is 1099."); 069 OPTION_HELP.put("--driver", "If you want to use this tool with a server other than Geronimo, " + 070 "then you must provide the path to its driver JAR. Currently, manifest " + 071 "Class-Path entries in that JAR are ignored."); 072 OPTION_HELP.put("--user", "If the deployment operation requires authentication, then you can " + 073 "specify the username to use to connect. If no password is specified, the " + 074 "deployer will attempt to connect to the server with no password, and if " + 075 "that fails, will prompt you for a password."); 076 OPTION_HELP.put("--password", "Specifies a password to use to authenticate to the server."); 077 OPTION_HELP.put("--syserr", "Enables error logging to syserr. Disabled by default."); 078 OPTION_HELP.put("--verbose", "Enables verbose execution mode. Disabled by default."); 079 OPTION_HELP.put("--offline", "Deploy offline to a local server, using whatever deployers are available in the local server"); 080 } 081 082 public static Map getOptionHelp() { 083 return OPTION_HELP; 084 } 085 086 /** 087 * Checks whether the stated command-line argument is a general argument (which 088 * may be the general argument itself, or a required parameter after the general 089 * argument). For example, if the arguments were "--user bob foo" then 090 * this should return true for "--user" and "bob" and false for "foo". 091 * 092 * @param args The previous arguments on the command line 093 * @param option The argument we're checking at the moment 094 * @return True if the argument we're checking is part of a general argument 095 */ 096 public static boolean isGeneralOption(List args, String option) { 097 if (OPTION_HELP.containsKey(option) || option.equals("--url")) { 098 return true; 099 } 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 }