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
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
243 in = ServerConnection.class.getResourceAsStream("/.geronimo-deployer");
244
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
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
282 }
283 }
284 }
285 }
286
287 if (authPrompt && !useURI.equals(DEFAULT_URI) && user == null && password == null) {
288
289 doAuthPromptAndRetry(useURI, user, password);
290 return;
291 } else {
292 try {
293 manager = mgr.getDeploymentManager(useURI, user, password);
294 auth = new SavedAuthentication(useURI, user, password == null ? null : password.toCharArray());
295 } catch (AuthenticationFailedException e) {
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 }