001: // ========================================================================
002: // Copyright (c) 2003 Mort Bay Consulting (Australia) Pty. Ltd.
003: // $Id: Main.java 6177 2007-02-19 10:11:27Z aaime $
004: // ========================================================================
005: package org.mortbay.start;
006:
007: import java.io.BufferedReader;
008: import java.io.File;
009: import java.io.FileInputStream;
010: import java.io.FilenameFilter;
011: import java.io.IOException;
012: import java.io.InputStream;
013: import java.io.InputStreamReader;
014: import java.lang.reflect.InvocationTargetException;
015: import java.lang.reflect.Method;
016: import java.security.Policy;
017: import java.util.ArrayList;
018: import java.util.Hashtable;
019: import java.util.StringTokenizer;
020:
021: /*-------------------------------------------*/
022: /** Main start class.
023: * This class is intended to be the main class listed in the MANIFEST.MF of
024: * the start.jar archive. It allows an application to be started with the
025: * command "java -jar start.jar".
026: *
027: * The behaviour of Main is controlled by the "org/mortbay/start/start.config"
028: * file obtained as a resource or file. This can be overridden with the START
029: * system property.
030: *
031: * The format of each line in this file is:<PRE>
032: * SUBJECT [ [!] CONDITION [AND|OR] ]*
033: * </PRE>
034: * where SUBJECT:<PRE>
035: * ends with ".class" is the Main class to run.
036: * ends with ".xml" is a configuration file for the command line
037: * ends with "/" is a directory from which add all jar and zip files from.
038: * ends with "/*" is a directory from which add all unconsidered jar and zip files from.
039: * Containing = are used to assign system properties.
040: * all other subjects are treated as files to be added to the classpath.
041: * </PRE>
042: * Subjects may include system properties with $(propertyname) syntax.
043: * File subjects starting with "/" are considered absolute, all others are relative to
044: * the home directory.
045: * <P>
046: * CONDITION is one of:<PRE>
047: * always
048: * never
049: * available package.class
050: * java OPERATOR n.n
051: * nargs OPERATOR n
052: * OPERATOR := one of "<",">","<=",">=","==","!="
053: * </PRE>
054: * CONTITIONS can be combined with AND OR or !, with AND being the assume
055: * operator for a list of CONDITIONS.
056: * Classpath operations are evaluated on the fly, so once a class or jar is
057: * added to the classpath, subsequent available conditions will see that class.
058: *
059: * The system parameter CLASSPATH, if set is given to the start classloader before
060: * any paths from the configuration file.
061: *
062: * Programs started with start.jar may be stopped with the stop.jar, which connects
063: * via a local port to stop the server. The default port can be set with the
064: * STOP.PORT system property (a port of < 0 disables the stop mechanism). If the STOP.KEY
065: * system property is set, then a random key is generated and written to stdout. This key
066: * must be passed to the stop.jar.
067: *
068: * @author Jan Hlavaty (hlavac@code.cz)
069: * @author Greg Wilkins
070: * @version $Revision: 1.1 $
071: */
072: public class Main {
073: static boolean _debug = System.getProperty("DEBUG", null) != null;
074: private String _classname = null;
075: private Classpath _classpath = new Classpath();
076: private String _config = System.getProperty("START",
077: "org/mortbay/start/start.config");
078: private ArrayList _xml = new ArrayList();
079:
080: public static void main(String[] args) {
081: try {
082: new Main().start(args);
083: } catch (Exception e) {
084: e.printStackTrace();
085: }
086: }
087:
088: static File getDirectory(String name) {
089: try {
090: if (name != null) {
091: File dir = new File(name).getCanonicalFile();
092:
093: if (dir.isDirectory()) {
094: return dir;
095: }
096: }
097: } catch (IOException e) {
098: }
099:
100: return null;
101: }
102:
103: boolean isAvailable(String classname) {
104: try {
105: Class.forName(classname);
106:
107: return true;
108: } catch (ClassNotFoundException e) {
109: }
110:
111: ClassLoader loader = _classpath.getClassLoader();
112:
113: try {
114: loader.loadClass(classname);
115:
116: return true;
117: } catch (ClassNotFoundException e) {
118: }
119:
120: return false;
121: }
122:
123: public static void invokeMain(ClassLoader classloader,
124: String classname, String[] args)
125: throws IllegalAccessException, InvocationTargetException,
126: NoSuchMethodException, ClassNotFoundException {
127: Class invoked_class = null;
128: invoked_class = classloader.loadClass(classname);
129:
130: Class[] method_param_types = new Class[1];
131: method_param_types[0] = args.getClass();
132:
133: Method main = null;
134: main = invoked_class.getDeclaredMethod("main",
135: method_param_types);
136:
137: Object[] method_params = new Object[1];
138: method_params[0] = args;
139: main.invoke(null, method_params);
140: }
141:
142: /* ------------------------------------------------------------ */
143: String expand(String s) {
144: int i1 = 0;
145: int i2 = 0;
146:
147: while (s != null) {
148: i1 = s.indexOf("$(", i2);
149:
150: if (i1 < 0) {
151: break;
152: }
153:
154: i2 = s.indexOf(")", i1 + 2);
155:
156: if (i2 < 0) {
157: break;
158: }
159:
160: String property = System.getProperty(s
161: .substring(i1 + 2, i2), "");
162: s = s.substring(0, i1) + property + s.substring(i2 + 1);
163: }
164:
165: return s;
166: }
167:
168: /* ------------------------------------------------------------ */
169: void configure(InputStream config, String[] args) throws Exception {
170: BufferedReader cfg = new BufferedReader(new InputStreamReader(
171: config, "ISO-8859-1"));
172: Version java_version = new Version(System
173: .getProperty("java.version"));
174: Version ver = new Version();
175:
176: // JAR's already processed
177: java.util.Hashtable done = new Hashtable();
178:
179: // Initial classpath
180: String classpath = System.getProperty("CLASSPATH");
181:
182: if (classpath != null) {
183: StringTokenizer tok = new StringTokenizer(classpath,
184: File.pathSeparator);
185:
186: while (tok.hasMoreTokens())
187: _classpath.addComponent(tok.nextToken());
188: }
189:
190: // Handle line by line
191: String line = null;
192:
193: while (true) {
194: line = cfg.readLine();
195:
196: if (line == null) {
197: break;
198: }
199:
200: if ((line.length() == 0) || line.startsWith("#")) {
201: continue;
202: }
203:
204: try {
205: StringTokenizer st = new StringTokenizer(line);
206: String subject = st.nextToken();
207: boolean expression = true;
208: boolean not = false;
209: String condition = null;
210:
211: // Evaluate all conditions
212: while (st.hasMoreTokens()) {
213: condition = st.nextToken();
214:
215: if (condition.equalsIgnoreCase("!")) {
216: not = true;
217:
218: continue;
219: }
220:
221: if (condition.equalsIgnoreCase("OR")) {
222: if (expression) {
223: break;
224: }
225:
226: expression = true;
227:
228: continue;
229: }
230:
231: if (condition.equalsIgnoreCase("AND")) {
232: if (!expression) {
233: break;
234: }
235:
236: continue;
237: }
238:
239: boolean eval = true;
240:
241: if (condition.equals("true")
242: || condition.equals("always")) {
243: eval = true;
244: } else if (condition.equals("false")
245: || condition.equals("never")) {
246: eval = false;
247: } else if (condition.equals("available")) {
248: String class_to_check = st.nextToken();
249: eval = isAvailable(class_to_check);
250: } else if (condition.equals("exists")) {
251: try {
252: eval = false;
253:
254: File file = new File(expand(st.nextToken()));
255: eval = file.exists();
256: } catch (Exception e) {
257: if (_debug) {
258: e.printStackTrace();
259: }
260: }
261: } else if (condition.equals("java")) {
262: String operator = st.nextToken();
263: String version = st.nextToken();
264: ver.parse(version);
265: eval = (operator.equals("<") && (java_version
266: .compare(ver) < 0))
267: || (operator.equals(">") && (java_version
268: .compare(ver) > 0))
269: || (operator.equals("<=") && (java_version
270: .compare(ver) <= 0))
271: || (operator.equals("=<") && (java_version
272: .compare(ver) <= 0))
273: || (operator.equals("=>") && (java_version
274: .compare(ver) >= 0))
275: || (operator.equals(">=") && (java_version
276: .compare(ver) >= 0))
277: || (operator.equals("==") && (java_version
278: .compare(ver) == 0))
279: || (operator.equals("!=") && (java_version
280: .compare(ver) != 0));
281: } else if (condition.equals("nargs")) {
282: String operator = st.nextToken();
283: int number = Integer.parseInt(st.nextToken());
284: eval = (operator.equals("<") && (args.length < number))
285: || (operator.equals(">") && (args.length > number))
286: || (operator.equals("<=") && (args.length <= number))
287: || (operator.equals("=<") && (args.length <= number))
288: || (operator.equals("=>") && (args.length >= number))
289: || (operator.equals(">=") && (args.length >= number))
290: || (operator.equals("==") && (args.length == number))
291: || (operator.equals("!=") && (args.length != number));
292: } else {
293: System.err.println("ERROR: Unknown condition: "
294: + condition);
295: eval = false;
296: }
297:
298: expression &= (not ? (!eval) : eval);
299: not = false;
300: }
301:
302: String file = expand(subject).replace('/',
303: File.separatorChar);
304:
305: if (_debug) {
306: System.err.println((expression ? "T " : "F ")
307: + line);
308: }
309:
310: if (!expression) {
311: done.put(file, file);
312:
313: continue;
314: }
315:
316: // Handle the subject
317: if (subject.indexOf("=") > 0) {
318: int i = file.indexOf("=");
319: String property = file.substring(0, i);
320: String value = file.substring(i + 1);
321:
322: if (_debug) {
323: System.err.println(" " + property + "="
324: + value);
325: }
326:
327: System.setProperty(property, value);
328: } else if (subject.endsWith("/*")) {
329: // directory of JAR files
330: File extdir = new File(file.substring(0, file
331: .length() - 1));
332: File[] jars = extdir
333: .listFiles(new FilenameFilter() {
334: public boolean accept(File dir,
335: String name) {
336: String namelc = name.toLowerCase();
337:
338: return namelc.endsWith(".jar")
339: || name.endsWith(".zip");
340: }
341: });
342:
343: for (int i = 0; (jars != null) && (i < jars.length); i++) {
344: String jar = jars[i].getCanonicalPath();
345:
346: if (!done.containsKey(jar)) {
347: done.put(jar, jar);
348:
349: boolean added = _classpath
350: .addComponent(jar);
351:
352: if (_debug) {
353: System.err
354: .println((added ? " CLASSPATH+="
355: : " !")
356: + jar);
357: }
358: }
359: }
360: } else if (subject.endsWith("/")) {
361: // class directory
362: File cd = new File(file);
363: String d = cd.getCanonicalPath();
364:
365: if (!done.containsKey(d)) {
366: done.put(d, d);
367:
368: boolean added = _classpath.addComponent(d);
369:
370: if (_debug) {
371: System.err.println((added ? " CLASSPATH+="
372: : " !")
373: + d);
374: }
375: }
376: } else if (subject.toLowerCase().endsWith(".xml")) {
377: // Config file
378: File f = new File(file);
379:
380: if (f.exists()) {
381: _xml.add(f.getCanonicalPath());
382: }
383:
384: if (_debug) {
385: System.err.println(" ARGS+=" + f);
386: }
387: } else if (subject.toLowerCase().endsWith(".class")) {
388: // Class
389: String cn = expand(subject.substring(0, subject
390: .length() - 6));
391:
392: if ((cn != null) && (cn.length() > 0)) {
393: if (_debug) {
394: System.err.println(" CLASS=" + cn);
395: }
396:
397: _classname = cn;
398: }
399: } else {
400: // single JAR file
401: File f = new File(file);
402: String d = f.getCanonicalPath();
403:
404: if (!done.containsKey(d)) {
405: done.put(d, d);
406:
407: boolean added = _classpath.addComponent(d);
408:
409: if (!added) {
410: added = _classpath
411: .addClasspath(expand(subject));
412:
413: if (_debug) {
414: System.err
415: .println((added ? " CLASSPATH+="
416: : " !")
417: + d);
418: }
419: } else if (_debug) {
420: System.err.println((added ? " CLASSPATH+="
421: : " !")
422: + d);
423: }
424: }
425: }
426: } catch (Exception e) {
427: System.err.println("on line: '" + line + "'");
428: e.printStackTrace();
429: }
430: }
431: }
432:
433: /* ------------------------------------------------------------ */
434: public void start(String[] args) {
435: // set up classpath:
436: try {
437: Monitor.monitor();
438:
439: InputStream cpcfg = getClass().getClassLoader()
440: .getResourceAsStream(_config);
441:
442: if (_debug) {
443: System.err.println("config=" + _config);
444: }
445:
446: if (cpcfg == null) {
447: cpcfg = new FileInputStream(_config);
448: }
449:
450: configure(cpcfg, args);
451: cpcfg.close();
452:
453: File file = new File(System.getProperty("jetty.home"));
454: String canonical = file.getCanonicalPath();
455: System.setProperty("jetty.home", canonical);
456: } catch (Exception e) {
457: e.printStackTrace();
458: System.exit(1);
459: }
460:
461: // okay, classpath complete.
462: System.setProperty("java.class.path", _classpath.toString());
463:
464: ClassLoader cl = _classpath.getClassLoader();
465:
466: if (_debug) {
467: System.err.println("java.class.path="
468: + System.getProperty("java.class.path"));
469: }
470:
471: if (_debug) {
472: System.err.println("jetty.home="
473: + System.getProperty("jetty.home"));
474: }
475:
476: if (_debug) {
477: System.err.println("java.io.tmpdir="
478: + System.getProperty("java.io.tmpdir"));
479: }
480:
481: if (_debug) {
482: System.err.println("java.class.path=" + _classpath);
483: }
484:
485: if (_debug) {
486: System.err.println("classloader=" + cl);
487: }
488:
489: if (_debug) {
490: System.err.println("classloader.parent=" + cl.getParent());
491: }
492:
493: // Invoke main(args) using new classloader.
494: Thread.currentThread().setContextClassLoader(cl);
495:
496: // re-eval the policy now that env is set
497: try {
498: Policy policy = Policy.getPolicy();
499:
500: if (policy != null) {
501: policy.refresh();
502: }
503: } catch (Exception e) {
504: e.printStackTrace();
505: }
506:
507: try {
508: if (_xml.size() > 0) {
509: for (int i = 0; i < args.length; i++)
510: _xml.add(args[i]);
511:
512: args = (String[]) _xml.toArray(args);
513: }
514:
515: //check for override of start class
516: String serverOverride = System.getProperty("jetty.server");
517:
518: if (_debug) {
519: System.err.println("server.override=" + serverOverride);
520: }
521:
522: if (serverOverride != null) {
523: _classname = serverOverride;
524: }
525:
526: invokeMain(cl, _classname, args);
527: } catch (Exception e) {
528: e.printStackTrace();
529: }
530: }
531: }
|