001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss;
023:
024: import gnu.getopt.Getopt;
025: import gnu.getopt.LongOpt;
026:
027: import java.io.File;
028: import java.io.FilenameFilter;
029: import java.net.MalformedURLException;
030: import java.net.URL;
031: import java.net.URLDecoder;
032: import java.util.LinkedList;
033: import java.util.List;
034: import java.util.Properties;
035:
036: import org.jboss.system.server.Server;
037: import org.jboss.system.server.ServerConfig;
038: import org.jboss.system.server.ServerConfigUtil;
039: import org.jboss.system.server.ServerLoader;
040:
041: /**
042: * Provides a command line interface to start the JBoss server.
043: * <p>
044: * To enable debug or trace messages durring boot change the Log4j
045: * configuration to use either <tt>log4j-debug.properties</tt>
046: * <tt>log4j-trace.properties</tt> by setting the system property
047: * <tt>log4j.configuration</tt>:
048: *
049: * <pre>
050: * ./run.sh -Dlog4j.configuration=log4j-debug.properties
051: * </pre>
052: *
053: * @author <a href="mailto:marc.fleury@jboss.org">Marc Fleury</a>
054: * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
055: * @author <a href="mailto:adrian.brock@happeningtimes.com">Adrian Brock</a>
056: * @author <a href="mailto:scott.stark@jboss.org">Scott Stark</a>
057: * @version $Revision: 61145 $
058: */
059: public class Main {
060: /** The JMX library to use */
061: private String jmxLibs = "jboss-jmx.jar";
062:
063: /** EDU.oswego.cs.dl.util.concurrent */
064: private String concurrentLib = "concurrent.jar";
065:
066: /** A URL for obtaining microkernel patches */
067: private URL bootURL;
068:
069: /** Extra jars from the /lib location that are added to the start of the boot
070: classpath. This can be used to override jboss /lib boot classes.
071: */
072: private List bootLibraries = new LinkedList();
073:
074: /** Extra libraries to load the server with .*/
075: private List extraLibraries = new LinkedList();
076:
077: /** Extra classpath URLS to load the server with .*/
078: private List extraClasspath = new LinkedList();
079:
080: /**
081: * Server properties. This object holds all of the required
082: * information to get the server up and running. Use System
083: * properties for defaults.
084: */
085: private Properties props = new Properties(System.getProperties());
086:
087: /**
088: * Explicit constructor.
089: */
090: public Main() {
091: super ();
092: }
093:
094: /**
095: * Boot up JBoss.
096: *
097: * @param args The command line arguments.
098: *
099: * @throws Exception Failed to boot.
100: */
101: public void boot(final String[] args) throws Exception {
102: // First process the command line to pickup custom props/settings
103: processCommandLine(args);
104:
105: // Auto set HOME_DIR to ../bin/run.jar if not set
106: String homeDir = props.getProperty(ServerConfig.HOME_DIR);
107: if (homeDir == null) {
108: String path = Main.class.getProtectionDomain()
109: .getCodeSource().getLocation().getFile();
110: /* The 1.4 JDK munges the code source file with URL encoding so run
111: * this path through the decoder so that is JBoss starts in a path with
112: * spaces we don't come crashing down.
113: */
114: path = URLDecoder.decode(path, "UTF-8");
115: File runJar = new File(path);
116: File homeFile = runJar.getParentFile().getParentFile();
117: homeDir = homeFile.getCanonicalPath();
118: }
119: props.setProperty(ServerConfig.HOME_DIR, homeDir);
120:
121: // Setup HOME_URL too, ServerLoader needs this
122: String homeURL = props.getProperty(ServerConfig.HOME_URL);
123: if (homeURL == null) {
124: File file = new File(homeDir);
125: homeURL = file.toURL().toString();
126: props.setProperty(ServerConfig.HOME_URL, homeURL);
127: }
128:
129: // Load the server instance
130: ServerLoader loader = new ServerLoader(props);
131:
132: /* If there is a patch dir specified make it the first element of the
133: loader bootstrap classpath. If its a file url pointing to a dir, then
134: add the dir and its contents.
135: */
136: if (bootURL != null) {
137: if (bootURL.getProtocol().equals("file")) {
138: File dir = new File(bootURL.getFile());
139: if (dir.exists()) {
140: // Add the local file patch directory
141: loader.addURL(dir.toURL());
142:
143: // Add the contents of the directory too
144: File[] jars = dir.listFiles(new JarFilter());
145:
146: for (int j = 0; jars != null && j < jars.length; j++) {
147: loader.addURL(jars[j].getCanonicalFile()
148: .toURL());
149: }
150: }
151: } else {
152: loader.addURL(bootURL);
153: }
154: }
155:
156: // Add any extra libraries
157: for (int i = 0; i < bootLibraries.size(); i++) {
158: loader.addLibrary((String) bootLibraries.get(i));
159: }
160:
161: // Add the jars from the endorsed dir
162: loader.addEndorsedJars();
163:
164: // Add jmx libs
165: loader.addLibraries(jmxLibs);
166:
167: // jmx UnifiedLoaderRepository needs a concurrent class...
168: loader.addLibrary(concurrentLib);
169:
170: // Add any extra libraries after the boot libs
171: for (int i = 0; i < extraLibraries.size(); i++) {
172: loader.addLibrary((String) extraLibraries.get(i));
173: }
174:
175: // Add any extra classapth URLs
176: for (int i = 0; i < extraClasspath.size(); i++) {
177: loader.addURL((URL) extraClasspath.get(i));
178: }
179:
180: // Load the server
181: ClassLoader parentCL = Thread.currentThread()
182: .getContextClassLoader();
183: Server server = loader.load(parentCL);
184:
185: // Initialize the server
186: server.init(props);
187:
188: // Start 'er up mate!
189: server.start();
190: }
191:
192: private URL makeURL(String urlspec) throws MalformedURLException {
193: urlspec = urlspec.trim();
194:
195: URL url;
196:
197: try {
198: url = new URL(urlspec);
199: if (url.getProtocol().equals("file")) {
200: // make sure the file is absolute & canonical file url
201: File file = new File(url.getFile()).getCanonicalFile();
202: url = file.toURL();
203: }
204: } catch (Exception e) {
205: // make sure we have a absolute & canonical file url
206: try {
207: File file = new File(urlspec).getCanonicalFile();
208: url = file.toURL();
209: } catch (Exception n) {
210: throw new MalformedURLException(n.toString());
211: }
212: }
213:
214: return url;
215: }
216:
217: /** Process the command line... */
218: private void processCommandLine(final String[] args)
219: throws Exception {
220: // set this from a system property or default to jboss
221: String programName = System
222: .getProperty("program.name", "jboss");
223: String sopts = "-:hD:d:p:n:c:Vj::B:L:C:P:b:g:u:l:";
224: LongOpt[] lopts = {
225: new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h'),
226: new LongOpt("bootdir", LongOpt.REQUIRED_ARGUMENT, null,
227: 'd'),
228: new LongOpt("patchdir", LongOpt.REQUIRED_ARGUMENT,
229: null, 'p'),
230: new LongOpt("netboot", LongOpt.REQUIRED_ARGUMENT, null,
231: 'n'),
232: new LongOpt("configuration", LongOpt.REQUIRED_ARGUMENT,
233: null, 'c'),
234: new LongOpt("version", LongOpt.NO_ARGUMENT, null, 'V'),
235: new LongOpt("jaxp", LongOpt.REQUIRED_ARGUMENT, null,
236: 'j'),
237: new LongOpt("bootlib", LongOpt.REQUIRED_ARGUMENT, null,
238: 'B'),
239: new LongOpt("library", LongOpt.REQUIRED_ARGUMENT, null,
240: 'L'),
241: new LongOpt("classpath", LongOpt.REQUIRED_ARGUMENT,
242: null, 'C'),
243: new LongOpt("properties", LongOpt.REQUIRED_ARGUMENT,
244: null, 'P'),
245: new LongOpt("host", LongOpt.REQUIRED_ARGUMENT, null,
246: 'b'),
247: new LongOpt("partition", LongOpt.REQUIRED_ARGUMENT,
248: null, 'g'),
249: new LongOpt("udp", LongOpt.REQUIRED_ARGUMENT, null, 'u'),
250: new LongOpt("log", LongOpt.REQUIRED_ARGUMENT, null, 'l'), };
251:
252: Getopt getopt = new Getopt(programName, args, sopts, lopts);
253: int code;
254: String arg;
255:
256: // JBAS-4119, bind to localhost by default, instead of all NICs ("0.0.0.0")
257: props
258: .setProperty(ServerConfig.SERVER_BIND_ADDRESS,
259: "127.0.0.1");
260: System.setProperty(ServerConfig.SERVER_BIND_ADDRESS,
261: "127.0.0.1");
262:
263: while ((code = getopt.getopt()) != -1) {
264: switch (code) {
265: case ':':
266: case '?':
267: // for now both of these should exit with error status
268: System.exit(1);
269: break; // for completeness
270:
271: case 1:
272: // this will catch non-option arguments
273: // (which we don't currently care about)
274: System.err.println(programName
275: + ": unused non-option argument: "
276: + getopt.getOptarg());
277: break; // for completeness
278:
279: case 'h':
280: // show command line help
281: System.out.println("usage: " + programName
282: + " [options]");
283: System.out.println();
284: System.out.println("options:");
285: System.out
286: .println(" -h, --help Show this help message");
287: System.out
288: .println(" -V, --version Show version information");
289: System.out
290: .println(" -- Stop processing options");
291: System.out
292: .println(" -D<name>[=<value>] Set a system property");
293: System.out
294: .println(" -d, --bootdir=<dir> Set the boot patch directory; Must be absolute or url");
295: System.out
296: .println(" -p, --patchdir=<dir> Set the patch directory; Must be absolute or url");
297: System.out
298: .println(" -n, --netboot=<url> Boot from net with the given url as base");
299: System.out
300: .println(" -c, --configuration=<name> Set the server configuration name");
301: System.out
302: .println(" -B, --bootlib=<filename> Add an extra library to the front bootclasspath");
303: System.out
304: .println(" -L, --library=<filename> Add an extra library to the loaders classpath");
305: System.out
306: .println(" -C, --classpath=<url> Add an extra url to the loaders classpath");
307: System.out
308: .println(" -P, --properties=<url> Load system properties from the given url");
309: System.out
310: .println(" -b, --host=<host or ip> Bind address for all JBoss services");
311: System.out
312: .println(" -g, --partition=<name> HA Partition name (default=DefaultDomain)");
313: System.out
314: .println(" -u, --udp=<ip> UDP multicast address");
315: System.out
316: .println(" -l, --log=<log4j|jdk> Specify the logger plugin type");
317: System.out.println();
318: System.exit(0);
319: break; // for completeness
320:
321: case 'D': {
322: // set a system property
323: arg = getopt.getOptarg();
324: String name, value;
325: int i = arg.indexOf("=");
326: if (i == -1) {
327: name = arg;
328: value = "true";
329: } else {
330: name = arg.substring(0, i);
331: value = arg.substring(i + 1, arg.length());
332: }
333: System.setProperty(name, value);
334: // Ensure setting the old bind.address property also sets the new
335: // jgroups.bind_addr property, otherwise jgroups may ignore it
336: // In Branch_4_0 do the opposite as well, as JG < 2.4 will
337: // ignore the new property
338: if ("bind.address".equals(name)) {
339: // Wildcard address is not valid for JGroups
340: String addr = ServerConfigUtil
341: .fixRemoteAddress(value);
342: System.setProperty("jgroups.bind_addr", addr);
343: } else if ("jgroups.bind_addr".equals(name)) {
344: // Wildcard address is not valid for JGroups
345: String addr = ServerConfigUtil
346: .fixRemoteAddress(value);
347: System.setProperty("bind.address", addr);
348: }
349: break;
350: }
351:
352: case 'd':
353: // set the boot patch URL
354: bootURL = makeURL(getopt.getOptarg());
355: break;
356:
357: case 'p': {
358: // set the patch URL
359: URL patchURL = makeURL(getopt.getOptarg());
360: props.put(ServerConfig.PATCH_URL, patchURL.toString());
361:
362: break;
363: }
364:
365: case 'n':
366: // set the net boot url
367: arg = getopt.getOptarg();
368:
369: // make sure there is a trailing '/'
370: if (!arg.endsWith("/"))
371: arg += "/";
372:
373: props.put(ServerConfig.HOME_URL, new URL(arg)
374: .toString());
375: break;
376:
377: case 'c':
378: // set the server name
379: arg = getopt.getOptarg();
380: props.put(ServerConfig.SERVER_NAME, arg);
381: break;
382:
383: case 'V': {
384: // Package information for org.jboss
385: Package jbossPackage = Package.getPackage("org.jboss");
386:
387: // show version information
388: System.out.println("JBoss "
389: + jbossPackage.getImplementationVersion());
390: System.out.println();
391: System.out.println("Distributable under LGPL license.");
392: System.out.println("See terms of license at gnu.org.");
393: System.out.println();
394: System.exit(0);
395: break; // for completness
396: }
397:
398: case 'j':
399: // Show an error and exit
400: System.err.println(programName
401: + ": option '-j, --jaxp' no longer supported");
402: System.exit(1);
403: break; // for completness
404:
405: case 'B':
406: arg = getopt.getOptarg();
407: bootLibraries.add(arg);
408: break;
409:
410: case 'L':
411: arg = getopt.getOptarg();
412: extraLibraries.add(arg);
413: break;
414:
415: case 'C': {
416: URL url = makeURL(getopt.getOptarg());
417: extraClasspath.add(url);
418: break;
419: }
420:
421: case 'P': {
422: // Set system properties from url/file
423: URL url = makeURL(getopt.getOptarg());
424: Properties props = System.getProperties();
425: props.load(url.openConnection().getInputStream());
426: break;
427: }
428: case 'b':
429: arg = getopt.getOptarg();
430: props.put(ServerConfig.SERVER_BIND_ADDRESS, arg);
431: System.setProperty(ServerConfig.SERVER_BIND_ADDRESS,
432: arg);
433: // used by JGroups; only set if not set via -D so users
434: // can use a different interface for cluster communication
435: // There are 2 versions of this property, deprecated bind.address
436: // and the new version, jgroups.bind_addr
437: String bindAddress = System.getProperty("bind.address");
438: if (bindAddress == null) {
439: // Wildcard address is not valid for JGroups
440: bindAddress = ServerConfigUtil
441: .fixRemoteAddress(arg);
442: System.setProperty("bind.address", bindAddress);
443: }
444: bindAddress = System.getProperty("jgroups.bind_addr");
445: if (bindAddress == null) {
446: // Wildcard address is not valid for JGroups
447: bindAddress = ServerConfigUtil
448: .fixRemoteAddress(arg);
449: System
450: .setProperty("jgroups.bind_addr",
451: bindAddress);
452: }
453:
454: // Set the java.rmi.server.hostname if not set
455: String rmiHost = System
456: .getProperty("java.rmi.server.hostname");
457: if (rmiHost == null) {
458: rmiHost = ServerConfigUtil.fixRemoteAddress(arg);
459: System.setProperty("java.rmi.server.hostname",
460: rmiHost);
461: }
462: break;
463: case 'g':
464: arg = getopt.getOptarg();
465: props.put(ServerConfig.PARTITION_NAME_PROPERTY, arg);
466: System.setProperty(
467: ServerConfig.PARTITION_NAME_PROPERTY, arg);
468: break;
469: case 'u':
470: arg = getopt.getOptarg();
471: props.put(ServerConfig.PARTITION_UDP_PROPERTY, arg);
472: System.setProperty(ServerConfig.PARTITION_UDP_PROPERTY,
473: arg);
474: // the new jgroups property name
475: System.setProperty("jgroups.udp.mcast_addr", arg);
476: break;
477: case 'l': {
478: arg = getopt.getOptarg();
479: String logPlugin = arg;
480: if (arg.equalsIgnoreCase("log4j"))
481: logPlugin = "org.jboss.logging.Log4jLoggerPlugin";
482: else if (arg.equalsIgnoreCase("jdk"))
483: logPlugin = "org.jboss.logging.JDK14LoggerPlugin";
484: System.setProperty(
485: "org.jboss.logging.Logger.pluginClass",
486: logPlugin);
487: break;
488: }
489: default:
490: // this should not happen,
491: // if it does throw an error so we know about it
492: throw new Error("unhandled option code: " + code);
493: }
494: }
495:
496: // JBAS-4119, set the java.rmi.server.hostname if not set
497: if (System.getProperty("java.rmi.server.hostname") == null) {
498: System.setProperty("java.rmi.server.hostname", "127.0.0.1");
499: }
500: }
501:
502: /**
503: * This is where the magic begins.
504: *
505: * <P>Starts up inside of a "jboss" thread group to allow better
506: * identification of JBoss threads.
507: *
508: * @param args The command line arguments.
509: */
510: public static void main(final String[] args) throws Exception {
511: Runnable worker = new Runnable() {
512: public void run() {
513: try {
514: Main main = new Main();
515: main.boot(args);
516: } catch (Exception e) {
517: System.err.println("Failed to boot JBoss:");
518: e.printStackTrace();
519: }
520: }
521:
522: };
523:
524: ThreadGroup threads = new ThreadGroup("jboss");
525: new Thread(threads, worker, "main").start();
526: }
527:
528: /**
529: * This method is here so that if JBoss is running under
530: * Alexandria (An NT Service Installer), Alexandria can shutdown
531: * the system down correctly.
532: */
533: public static void systemExit(String argv[]) {
534: System.exit(0);
535: }
536:
537: static class JarFilter implements FilenameFilter {
538: public boolean accept(File dir, String name) {
539: return name.endsWith(".jar");
540: }
541: }
542: }
|