1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jboss;
23
24 import gnu.getopt.Getopt;
25 import gnu.getopt.LongOpt;
26
27 import java.io.File;
28 import java.io.FilenameFilter;
29 import java.net.MalformedURLException;
30 import java.net.URL;
31 import java.net.URLDecoder;
32 import java.util.LinkedList;
33 import java.util.List;
34 import java.util.Properties;
35
36 import org.jboss.system.server.Server;
37 import org.jboss.system.server.ServerConfig;
38 import org.jboss.system.server.ServerConfigUtil;
39 import org.jboss.system.server.ServerLoader;
40
41 /**
42 * Provides a command line interface to start the JBoss server.
43 *
44 * <p>
45 * To enable debug or trace messages durring boot change the Log4j
46 * configuration to use either <tt>log4j-debug.properties</tt>
47 * <tt>log4j-trace.properties</tt> by setting the system property
48 * <tt>log4j.configuration</tt>:
49 *
50 * <pre>
51 * ./run.sh -Dlog4j.configuration=log4j-debug.properties
52 * </pre>
53 * TODO: Should jdk logging be the default
54 *
55 * @author <a href="mailto:marc.fleury@jboss.org">Marc Fleury</a>
56 * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
57 * @author <a href="mailto:adrian.brock@happeningtimes.com">Adrian Brock</a>
58 * @author <a href="mailto:scott.stark@jboss.org">Scott Stark</a>
59 * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
60 * @version $Revision: 63730 $
61 */
62 public class Main
63 {
64 /** EDU.oswego.cs.dl.util.concurrent */
65 private String concurrentLib = "concurrent.jar";
66
67 /** A URL for obtaining microkernel patches */
68 private URL bootURL;
69
70 /** Extra jars from the /lib location that are added to the start of the boot
71 classpath. This can be used to override jboss /lib boot classes.
72 */
73 private List<String> bootLibraries = new LinkedList<String>();
74
75 /** Extra libraries to load the server with .*/
76 private List<String> extraLibraries = new LinkedList<String>();
77
78 /** Extra classpath URLS to load the server with .*/
79 private List<URL> extraClasspath = new LinkedList<URL>();
80
81 /**
82 * Server properties. This object holds all of the required
83 * information to get the server up and running. Use System
84 * properties for defaults.
85 */
86 private Properties props = new Properties(System.getProperties());
87
88 /** The booted server instance */
89 private Server server;
90
91 /**
92 * Explicit constructor.
93 */
94 public Main()
95 {
96 super();
97 }
98
99 /**
100 * Access the booted server.
101 * @return the Server instance.
102 */
103 public Server getServer()
104 {
105 return server;
106 }
107
108 /**
109 * Boot up JBoss.
110 *
111 * @param args The command line arguments.
112 *
113 * @throws Exception Failed to boot.
114 */
115 public void boot(final String[] args) throws Exception
116 {
117 // First process the command line to pickup custom props/settings
118 processCommandLine(args);
119
120 // Auto set HOME_DIR to ../bin/run.jar if not set
121 String homeDir = props.getProperty(ServerConfig.HOME_DIR);
122 if (homeDir == null)
123 {
124 String path = Main.class.getProtectionDomain().getCodeSource().getLocation().getFile();
125 /* The 1.4 JDK munges the code source file with URL encoding so run
126 * this path through the decoder so that is JBoss starts in a path with
127 * spaces we don't come crashing down.
128 */
129 path = URLDecoder.decode(path, "UTF-8");
130 File runJar = new File(path);
131 File homeFile = runJar.getParentFile().getParentFile();
132 homeDir = homeFile.getCanonicalPath();
133 }
134 props.setProperty(ServerConfig.HOME_DIR, homeDir);
135
136 // Setup HOME_URL too, ServerLoader needs this
137 String homeURL = props.getProperty(ServerConfig.HOME_URL);
138 if (homeURL == null)
139 {
140 File file = new File(homeDir);
141 homeURL = file.toURL().toString();
142 props.setProperty(ServerConfig.HOME_URL, homeURL);
143 }
144
145 // Load the server instance
146 ServerLoader loader = new ServerLoader(props);
147
148 /* If there is a patch dir specified make it the first element of the
149 loader bootstrap classpath. If its a file url pointing to a dir, then
150 add the dir and its contents.
151 */
152 if (bootURL != null)
153 {
154 if (bootURL.getProtocol().equals("file"))
155 {
156 File dir = new File(bootURL.getFile());
157 if (dir.exists())
158 {
159 // Add the local file patch directory
160 loader.addURL(dir.toURL());
161
162 // Add the contents of the directory too
163 File[] jars = dir.listFiles(new JarFilter());
164
165 for (int j = 0; jars != null && j < jars.length; j++)
166 {
167 loader.addURL(jars[j].getCanonicalFile().toURL());
168 }
169 }
170 }
171 else
172 {
173 loader.addURL(bootURL);
174 }
175 }
176
177 // Add any extra libraries
178 for (int i = 0; i < bootLibraries.size(); i++)
179 {
180 loader.addLibrary(bootLibraries.get(i));
181 }
182
183 // Add the jars from the endorsed dir
184 loader.addEndorsedJars();
185
186 // jmx UnifiedLoaderRepository needs a concurrent class...
187 loader.addLibrary(concurrentLib);
188
189 // Add any extra libraries after the boot libs
190 for (int i = 0; i < extraLibraries.size(); i++)
191 {
192 loader.addLibrary(extraLibraries.get(i));
193 }
194
195 // Add any extra classapth URLs
196 for (int i = 0; i < extraClasspath.size(); i++)
197 {
198 loader.addURL(extraClasspath.get(i));
199 }
200
201 // Load the server
202 ClassLoader parentCL = Thread.currentThread().getContextClassLoader();
203 server = loader.load(parentCL);
204
205 // Initialize the server
206 server.init(props);
207
208 // Start 'er up mate!
209 server.start();
210 }
211
212 /**
213 * Shutdown the booted Server instance.
214 *
215 */
216 public void shutdown()
217 {
218 server.shutdown();
219 }
220
221 private URL makeURL(String urlspec) throws MalformedURLException
222 {
223 urlspec = urlspec.trim();
224
225 URL url;
226
227 try
228 {
229 url = new URL(urlspec);
230 if (url.getProtocol().equals("file"))
231 {
232 // make sure the file is absolute & canonical file url
233 File file = new File(url.getFile()).getCanonicalFile();
234 url = file.toURL();
235 }
236 }
237 catch (Exception e)
238 {
239 // make sure we have a absolute & canonical file url
240 try
241 {
242 File file = new File(urlspec).getCanonicalFile();
243 url = file.toURL();
244 }
245 catch (Exception n)
246 {
247 throw new MalformedURLException(n.toString());
248 }
249 }
250
251 return url;
252 }
253
254 /** Process the command line... */
255 private void processCommandLine(final String[] args) throws Exception
256 {
257 // set this from a system property or default to jboss
258 String programName = System.getProperty("program.name", "jboss");
259 String sopts = "-:hD:d:p:n:c:Vj::B:L:C:P:b:g:u:m:l:";
260 LongOpt[] lopts =
261 {
262 new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h'),
263 new LongOpt("bootdir", LongOpt.REQUIRED_ARGUMENT, null, 'd'),
264 new LongOpt("patchdir", LongOpt.REQUIRED_ARGUMENT, null, 'p'),
265 new LongOpt("netboot", LongOpt.REQUIRED_ARGUMENT, null, 'n'),
266 new LongOpt("configuration", LongOpt.REQUIRED_ARGUMENT, null, 'c'),
267 new LongOpt("version", LongOpt.NO_ARGUMENT, null, 'V'),
268 new LongOpt("jaxp", LongOpt.REQUIRED_ARGUMENT, null, 'j'),
269 new LongOpt("bootlib", LongOpt.REQUIRED_ARGUMENT, null, 'B'),
270 new LongOpt("library", LongOpt.REQUIRED_ARGUMENT, null, 'L'),
271 new LongOpt("classpath", LongOpt.REQUIRED_ARGUMENT, null, 'C'),
272 new LongOpt("properties", LongOpt.REQUIRED_ARGUMENT, null, 'P'),
273 new LongOpt("host", LongOpt.REQUIRED_ARGUMENT, null, 'b'),
274 new LongOpt("partition", LongOpt.REQUIRED_ARGUMENT, null, 'g'),
275 new LongOpt("udp", LongOpt.REQUIRED_ARGUMENT, null, 'u'),
276 new LongOpt("mcast_port", LongOpt.REQUIRED_ARGUMENT, null, 'm'),
277 new LongOpt("log", LongOpt.REQUIRED_ARGUMENT, null, 'l'),
278 };
279
280 Getopt getopt = new Getopt(programName, args, sopts, lopts);
281 int code;
282 String arg;
283 props.setProperty(ServerConfig.SERVER_BIND_ADDRESS, "127.0.0.1");
284 System.setProperty(ServerConfig.SERVER_BIND_ADDRESS, "127.0.0.1");
285 while ((code = getopt.getopt()) != -1)
286 {
287 switch (code)
288 {
289 case ':':
290 case '?':
291 // for now both of these should exit with error status
292 System.exit(1);
293 break; // for completeness
294
295 case 1:
296 // this will catch non-option arguments
297 // (which we don't currently care about)
298 System.err.println(programName +
299 ": unused non-option argument: " +
300 getopt.getOptarg());
301 break; // for completeness
302
303 case 'h':
304 // show command line help
305 System.out.println("usage: " + programName + " [options]");
306 System.out.println();
307 System.out.println("options:");
308 System.out.println(" -h, --help Show this help message");
309 System.out.println(" -V, --version Show version information");
310 System.out.println(" -- Stop processing options");
311 System.out.println(" -D<name>[=<value>] Set a system property");
312 System.out.println(" -d, --bootdir=<dir> Set the boot patch directory; Must be absolute or url");
313 System.out.println(" -p, --patchdir=<dir> Set the patch directory; Must be absolute or url");
314 System.out.println(" -n, --netboot=<url> Boot from net with the given url as base");
315 System.out.println(" -c, --configuration=<name> Set the server configuration name");
316 System.out.println(" -B, --bootlib=<filename> Add an extra library to the front bootclasspath");
317 System.out.println(" -L, --library=<filename> Add an extra library to the loaders classpath");
318 System.out.println(" -C, --classpath=<url> Add an extra url to the loaders classpath");
319 System.out.println(" -P, --properties=<url> Load system properties from the given url");
320 System.out.println(" -b, --host=<host or ip> Bind address for all JBoss services");
321 System.out.println(" -g, --partition=<name> HA Partition name (default=DefaultDomain)");
322 System.out.println(" -m, --mcast_port=<ip> UDP multicast port; Only used by JGroups multiplexer");
323 System.out.println(" -u, --udp=<ip> UDP multicast address");
324 System.out.println(" -l, --log=<log4j|jdk> Specify the logger plugin type");
325 System.out.println();
326 System.exit(0);
327 break; // for completeness
328
329 case 'D':
330 {
331 // set a system property
332 arg = getopt.getOptarg();
333 String name, value;
334 int i = arg.indexOf("=");
335 if (i == -1)
336 {
337 name = arg;
338 value = "true";
339 }
340 else
341 {
342 name = arg.substring(0, i);
343 value = arg.substring(i + 1, arg.length());
344 }
345 System.setProperty(name, value);
346 // Ensure setting the old bind.address property also sets the new
347 // jgroups.bind_addr property, otherwise jgroups may ignore it
348 if ("bind.address".equals(name))
349 {
350 // Wildcard address is not valid for JGroups
351 String addr = ServerConfigUtil.fixRemoteAddress(value);
352 System.setProperty("jgroups.bind_addr", addr);
353 }
354 else if ("jgroups.bind_addr".equals(name))
355 {
356 // Wildcard address is not valid for JGroups
357 String addr = ServerConfigUtil.fixRemoteAddress(value);
358 System.setProperty("jgroups.bind_addr", addr);
359 }
360 break;
361 }
362
363 case 'd':
364 // set the boot patch URL
365 bootURL = makeURL(getopt.getOptarg());
366 break;
367
368 case 'p':
369 {
370 // set the patch URL
371 URL patchURL = makeURL(getopt.getOptarg());
372 props.put(ServerConfig.PATCH_URL, patchURL.toString());
373
374 break;
375 }
376
377 case 'n':
378 // set the net boot url
379 arg = getopt.getOptarg();
380
381 // make sure there is a trailing '/'
382 if (!arg.endsWith("/")) arg += "/";
383
384 props.put(ServerConfig.HOME_URL, new URL(arg).toString());
385 break;
386
387 case 'c':
388 // set the server name
389 arg = getopt.getOptarg();
390 props.put(ServerConfig.SERVER_NAME, arg);
391 break;
392
393 case 'V':
394 {
395 // Package information for org.jboss
396 Package jbossPackage = Package.getPackage("org.jboss");
397
398 // show version information
399 System.out.println("JBoss " + jbossPackage.getImplementationVersion());
400 System.out.println();
401 System.out.println("Distributable under LGPL license.");
402 System.out.println("See terms of license at gnu.org.");
403 System.out.println();
404 System.exit(0);
405 break; // for completness
406 }
407
408 case 'j':
409 // Show an error and exit
410 System.err.println(programName + ": option '-j, --jaxp' no longer supported");
411 System.exit(1);
412 break; // for completness
413
414 case 'B':
415 arg = getopt.getOptarg();
416 bootLibraries.add(arg);
417 break;
418
419 case 'L':
420 arg = getopt.getOptarg();
421 extraLibraries.add(arg);
422 break;
423
424 case 'C':
425 {
426 URL url = makeURL(getopt.getOptarg());
427 extraClasspath.add(url);
428 break;
429 }
430
431 case 'P':
432 {
433 // Set system properties from url/file
434 URL url = makeURL(getopt.getOptarg());
435 Properties props = System.getProperties();
436 props.load(url.openConnection().getInputStream());
437 break;
438 }
439 case 'b':
440 arg = getopt.getOptarg();
441 props.put(ServerConfig.SERVER_BIND_ADDRESS, arg);
442 System.setProperty(ServerConfig.SERVER_BIND_ADDRESS, arg);
443 // used by JGroups; only set if not set via -D so users
444 // can use a different interface for cluster communication
445 // There are 2 versions of this property, deprecated bind.address
446 // and the new version, jgroups.bind_addr
447 String bindAddress = System.getProperty("bind.address");
448 if (bindAddress == null)
449 {
450 // Wildcard address is not valid for JGroups
451 bindAddress = ServerConfigUtil.fixRemoteAddress(arg);
452 System.setProperty("bind.address", bindAddress);
453 }
454 bindAddress = System.getProperty("jgroups.bind_addr");
455 if (bindAddress == null)
456 {
457 // Wildcard address is not valid for JGroups
458 bindAddress = ServerConfigUtil.fixRemoteAddress(arg);
459 System.setProperty("jgroups.bind_addr", bindAddress);
460 }
461
462 // Set the java.rmi.server.hostname if not set
463 String rmiHost = System.getProperty("java.rmi.server.hostname");
464 if( rmiHost == null )
465 {
466 rmiHost = ServerConfigUtil.fixRemoteAddress(arg);
467 System.setProperty("java.rmi.server.hostname", rmiHost);
468 }
469 break;
470
471 case 'g':
472 arg = getopt.getOptarg();
473 props.put(ServerConfig.PARTITION_NAME_PROPERTY, arg);
474 System.setProperty(ServerConfig.PARTITION_NAME_PROPERTY, arg);
475 break;
476
477 case 'u':
478 arg = getopt.getOptarg();
479 props.put(ServerConfig.PARTITION_UDP_PROPERTY, arg);
480 System.setProperty(ServerConfig.PARTITION_UDP_PROPERTY, arg);
481 // the new jgroups property name
482 System.setProperty("jgroups.udp.mcast_addr", arg);
483 break;
484
485 case 'm':
486 arg = getopt.getOptarg();
487 props.put(ServerConfig.PARTITION_UDP_PORT_PROPERTY, arg);
488 System.setProperty(ServerConfig.PARTITION_UDP_PORT_PROPERTY, arg);
489 break;
490
491 case 'l':
492 {
493 arg = getopt.getOptarg();
494 String logPlugin = arg;
495 if( arg.equalsIgnoreCase("log4j") )
496 logPlugin = "org.jboss.logging.Log4jLoggerPlugin";
497 else if( arg.equalsIgnoreCase("jdk") )
498 {
499 logPlugin = "org.jboss.logging.JDK14LoggerPlugin";
500 // Also override the jdk LogManager
501 System.setProperty("java.util.logging.manager",
502 "org.jboss.logging.jdk.JDKLogManager");
503 }
504 System.setProperty("org.jboss.logging.Logger.pluginClass", logPlugin);
505 break;
506 }
507
508 default:
509 // this should not happen,
510 // if it does throw an error so we know about it
511 throw new Error("unhandled option code: " + code);
512 }
513 }
514
515 // Fix up other bind addresses
516 String bindAddress = System.getProperty(ServerConfig.SERVER_BIND_ADDRESS);
517 if (System.getProperty("java.rmi.server.hostname") == null)
518 System.setProperty("java.rmi.server.hostname", bindAddress);
519 if (System.getProperty("jgroups.bind_addr") == null)
520 System.setProperty("jgroups.bind_addr", bindAddress);
521
522 // Enable jboss.vfs.forceCopy by default, if unspecified
523 if (System.getProperty("jboss.vfs.forceCopy") == null)
524 System.setProperty("jboss.vfs.forceCopy", "true");
525 }
526
527 /**
528 * This is where the magic begins.
529 *
530 * <P>Starts up inside of a "jboss" thread group to allow better
531 * identification of JBoss threads.
532 *
533 * @param args The command line arguments.
534 * @throws Exception for any error
535 */
536 public static void main(final String[] args) throws Exception
537 {
538 Runnable worker = new Runnable() {
539 public void run()
540 {
541 try
542 {
543 Main main = new Main();
544 main.boot(args);
545 }
546 catch (Exception e)
547 {
548 System.err.println("Failed to boot JBoss:");
549 e.printStackTrace();
550 }
551 }
552
553 };
554
555 ThreadGroup threads = new ThreadGroup("jboss");
556 new Thread(threads, worker, "main").start();
557 }
558
559 /**
560 * This method is here so that if JBoss is running under
561 * Alexandria (An NT Service Installer), Alexandria can shutdown
562 * the system down correctly.
563 *
564 * @param argv the arguments
565 */
566 public static void systemExit(String argv[])
567 {
568 System.exit(0);
569 }
570
571 static class JarFilter implements FilenameFilter
572 {
573 public boolean accept(File dir, String name)
574 {
575 return name.endsWith(".jar");
576 }
577 }
578 }