0001: /*
0002: * <copyright>
0003: *
0004: * Copyright 2004 BBNT Solutions, LLC
0005: * under sponsorship of the Defense Advanced Research Projects
0006: * Agency (DARPA).
0007: *
0008: * You can redistribute this software and/or modify it under the
0009: * terms of the Cougaar Open Source License as published on the
0010: * Cougaar Open Source Website (www.cougaar.org).
0011: *
0012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
0015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
0016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0023: *
0024: * </copyright>
0025: */
0026: package org.cougaar.bootstrap;
0027:
0028: import java.io.CharArrayWriter;
0029: import java.io.File;
0030: import java.io.FileInputStream;
0031: import java.io.FileNotFoundException;
0032: import java.io.InputStream;
0033: import java.io.IOException;
0034: import java.io.Serializable;
0035: import java.net.URL;
0036: import java.util.ArrayList;
0037: import java.util.Collections;
0038: import java.util.HashMap;
0039: import java.util.Iterator;
0040: import java.util.LinkedHashMap;
0041: import java.util.List;
0042: import java.util.Map;
0043: import java.util.Properties;
0044: import javax.xml.transform.Source;
0045: import javax.xml.transform.TransformerException;
0046: import javax.xml.transform.TransformerFactory;
0047: import javax.xml.transform.URIResolver;
0048: import javax.xml.transform.sax.SAXResult;
0049: import javax.xml.transform.sax.SAXSource;
0050: import javax.xml.transform.sax.SAXTransformerFactory;
0051: import javax.xml.transform.stream.StreamSource;
0052: import org.xml.sax.Attributes;
0053: import org.xml.sax.EntityResolver;
0054: import org.xml.sax.InputSource;
0055: import org.xml.sax.SAXException;
0056: import org.xml.sax.SAXParseException;
0057: import org.xml.sax.XMLReader;
0058: import org.xml.sax.helpers.DefaultHandler;
0059:
0060: /**
0061: * This class is used by
0062: * <tt>$COUGAAR_INSTALL_PATH/bin/cougaar[.bat]</tt>
0063: * to read the configuration XML files and print the <b>java</b>
0064: * command line, which is then used to launch the Cougaar node
0065: * process.
0066: * <p>
0067: * For usage, see the {@link #USAGE} string, or run:<pre>
0068: * $COUGAAR_INSTALL_PATH/bin/cougaar --help
0069: * </pre>
0070: * The typical usage is:<pre>
0071: * # <i>start node X specified in "mySociety.xml":</i>
0072: * cd <i>your_config_directory</i>
0073: * $COUGAAR_INSTALL_PATH/bin/cougaar mySociety.xml X
0074: * </pre>
0075: * <p>
0076: * This class is intended to replace the old Cougaar scripts:<br>
0077: * Node, Node.bat, XMLNode.bat, XSLNode, XSLNode.bat<br>
0078: * The behavior is backwards-compatible with XSLNode.
0079: * <p>
0080: * For example, the input files and command line:<br>
0081: * <i>mySociety.xml:</i><pre>
0082: * <?xml version='1.0'?>
0083: * <society name='MinPing' ..>
0084: * <node name='NodeA'>
0085: * <vm_parameter>-Dx=y</vm_parameter>
0086: * <vm_parameter>-Dfoo=$bar</vm_parameter>
0087: * </node>
0088: * </society>
0089: * </pre>
0090: * <i>myRuntime.xml:</i><pre>
0091: * <?xml version='1.0'?>
0092: * <vm_parameters>
0093: * <vm_parameter>-Dx=z</vm_parameter>
0094: * ..
0095: * </vm_parameters>
0096: * </pre>
0097: * <i>command line:</i><pre>
0098: * $COUGAAR_INSTALL_PATH/bin/cougaar \
0099: * --society mySociety.xml\
0100: * --runtime myRuntime.xml\
0101: * NodeA
0102: * </pre>
0103: * would print a command line output similar to:<pre>
0104: * java -Dx=z -Dfoo=$bar ...
0105: * </pre>
0106: * and on Windows (based on the 'os.name' property) this would be:<pre>
0107: * java -Dx=z -Dfoo=%bar% ...
0108: * </pre>
0109: * <p>
0110: * This parser also adds several standard Cougaar defaults, which
0111: * add the minimal -Ds and options required to run a Cougaar node:
0112: * <ul>
0113: * <li>If the <command> is not specified in either the
0114: * society_xml or optional runtime_xml, "java" is used.</li>
0115: * <li>If the <class> is not specified in either xml,
0116: * "org.cougaar.bootstrap.Bootstrapper" is used. The
0117: * following "-Ds" rules are only executed if the "Bootstrapper"
0118: * class is specified:</li>
0119: * <li>If zero <prog_parameter>s are specified,
0120: * and "-Dorg.cougaar.bootstrap.application=<i>classname</i>"
0121: * is not specified in either xmls or command-line arguments,
0122: * then
0123: * "-Dorg.cougaar.bootstrap.application=org.cougaar.core.node.Node"
0124: * is used. The following "-D" rules are only executed if
0125: * the "Node" bootstrapped class is specified (plus the above
0126: * "Bootstrapper" class requirement):</li>
0127: * <li>If "-Dorg.cougaar.runtime.file=<i>filename</i>" is not
0128: * specified, and a runtime_xml was specified on the command line, then
0129: * "-Dorg.cougaar.runtime.file=<i>runtime_xml</i>
0130: * is used.</li>
0131: * <li>If "-Dorg.cougaar.society.file=<i>filename</i>" is not
0132: * specified,
0133: * "-Dorg.cougaar.society.file=<i>society_xml</i>
0134: * is used.</li>
0135: * <li>If "-Dorg.cougaar.node.name=<i>node_name</i> is not
0136: * specified, then the <node name='..'> of the
0137: * <b><u>first node</u></b> listed in the aplication_xml is
0138: * used, otherwise the first node name listed in the runtime_xml
0139: * is used.</li>
0140: * <li>If the "-Dorg.cougaar.runtime.path=<i>directory</i> is not
0141: * specified, then
0142: * "-Dorg.cougaar.runtime.path=$COUGAAR_RUNTIME_PATH"
0143: * is used.</li>
0144: * <li>If the "-Dorg.cougaar.society.path=<i>directory</i> is not
0145: * specified, then
0146: * "-Dorg.cougaar.society.path=$COUGAAR_SOCIETY_PATH"
0147: * is used.</li>
0148: * <li>If the "-Dorg.cougaar.install.path=<i>directory</i> is not
0149: * specified, then
0150: * "-Dorg.cougaar.install.path=$COUGAAR_INSTALL_PATH"
0151: * is used.</li>
0152: * <li>If a "-Xbootclasspath.." is not specified, then
0153: * "-Xbootclasspath/p:$COUGAAR_INSTALL_PATH/lib/javaiopatch.jar"
0154: * is used.</li>
0155: * <li>If
0156: * "-Dorg.cougaar.core.node.InitializationComponent=<i>type</i>"
0157: * is not specified,
0158: * "-Dorg.cougaar.core.node.InitializationComponent=XML"
0159: * is used.</li>
0160: * <li>If "-Djava.class.path=<i>jars</i>" is not specified,
0161: * "-Djava.class.path=$COUGAAR_INSTALL_PATH/lib/bootstrap.jar"
0162: * is used.</li>
0163: * </ul>
0164: */
0165: public final class CommandLine {
0166:
0167: public static final String USAGE = "Usage: cougaar [default...] [option...] FILE [override...] [NODE]\n"
0168: + "Start a Cougaar Node and Agents specified in an XML file.\n"
0169: + "\n"
0170: + " -s, --society STRING society XML FILE name\n"
0171: + " -r, --runtime STRING runtime XML file name, which defaults to the\n"
0172: + " society FILE\n"
0173: + " -n, --nameserver STRING equivalent to -Dorg.cougaar.name.server=STRING\n"
0174: + " -d, --defaults .. \\; default \"-D\" system properties until the \\;\n"
0175: + " -o, --overrides .. \\; override \"-D\" system properties until the \\;\n"
0176: + " --node STRING NODE name\n"
0177: + " -w, --windows Convert variables to Windows format, for example,\n"
0178: + " \"-Da=$x\" becomes \"-Da=%x%\"\n"
0179: + " -h, -?, --help print this help message\n"
0180: + " -v, --verbose also print the command line to stderr\n"
0181: + " -vv, --debug print debug output to stderr\n"
0182: + " -* if none of the above, if in a \"--defaults\" or\n"
0183: + " the society FILE is not set, add a default\n"
0184: + " \"-D\", otherwise add an override \"-D\"\n"
0185: + " * if none of the above, set the society FILE if\n"
0186: + " it has not been set, otherwise set the NODE if it\n"
0187: + " has not been set\n"
0188: + "\n"
0189: + "Java system properties are preferred in the following order:\n"
0190: + " 1) The \"--overrides\" from the command line, else\n"
0191: + " 2) The \"--runtime\" XML file's \"<vm_parameter>\"s, else\n"
0192: + " 3) The \"--society\" XML file's \"<vm_parameter>\"s, else\n"
0193: + " 4) The \"--defaults\" from the command line, else\n"
0194: + " 5) Standard Cougaar default \"-Ds\", such as:\n"
0195: + " \"-Dorg.cougaar.install.path=$COUGAAR_INSTALL_PATH\"\n"
0196: + " as documented in the "
0197: + CommandLine.class.getName()
0198: + " javadocs.\n"
0199: + "\n"
0200: + "Example usage:\n"
0201: + "\n"
0202: + " # start node X specified in \"mySociety.xml\":\n"
0203: + " cougaar mySociety.xml X\n"
0204: + "\n"
0205: + " # start the first node listed in the XML:\n"
0206: + " cougaar mySociety.xml\n"
0207: + "\n"
0208: + " # read -D's from soc.xml, override with -D's from rt.xml, force -Da=b:\n"
0209: + " cougaar soc.xml rt.xml -Da=b\n"
0210: + "\n"
0211: + "Report bugs to <bugs@cougaar.org>.";
0212:
0213: // optional -D specified *in the xml* to expand the bootstrapper
0214: // jar path.
0215: private static final String EXPAND_JAR_PATH_PROP = "org.cougaar.bootstrap.commandLine.expandJarPath";
0216:
0217: // args
0218: private final String[] args;
0219:
0220: // from args
0221: private boolean verbose;
0222: private boolean debug;
0223: private boolean windows;
0224: private String society_xml;
0225: private String runtime_xml;
0226: private String node_name;
0227:
0228: // from command-line args "--overrides"
0229: private CommandData override_data;
0230:
0231: // from runtime xml file (e.g. "myRuntime.xml")
0232: private CommandData runtime_data;
0233:
0234: // from society xml file (e.g. "mySociety.xml")
0235: private CommandData society_data;
0236:
0237: // from command-line args "--defaults"
0238: private CommandData default_data;
0239:
0240: /**
0241: * Parse an XML file and print the Java command line.
0242: */
0243: public static void main(String[] args) throws Exception {
0244: (new CommandLine(args)).run();
0245: }
0246:
0247: /**
0248: * @param args see {@link #USAGE}.
0249: * @return the parsed command line data structure.
0250: */
0251: public static CommandData parse(String[] args) {
0252: CommandLine cl = new CommandLine(args);
0253: if (!cl.parse_arguments()) {
0254: return null;
0255: }
0256: return cl.generate_command();
0257: }
0258:
0259: public CommandLine(String[] args) {
0260: this .args = args;
0261: }
0262:
0263: public void run() throws Exception {
0264: if (!parse_arguments()) {
0265: System.exit(-1);
0266: }
0267: CommandData command = generate_command();
0268: if (command == null) {
0269: System.exit(-1);
0270: }
0271: if (verbose) {
0272: System.err.println(command);
0273: }
0274: System.out.println(command);
0275: }
0276:
0277: /** @see #usage */
0278: private boolean parse_arguments() {
0279: List default_vm_parameters = null;
0280: List override_vm_parameters = null;
0281: boolean inDefaults = false;
0282: boolean inOverrides = false;
0283: for (int i = 0; i < args.length; i++) {
0284: String s = args[i];
0285: if (!s.startsWith("-")) {
0286: inDefaults = false;
0287: inOverrides = false;
0288: if (!s.equals(";") && !s.equals("\\;")) {
0289: if (society_xml == null) {
0290: society_xml = s;
0291: } else if (node_name == null) {
0292: node_name = s;
0293: } else if (runtime_xml == null) {
0294: runtime_xml = node_name;
0295: node_name = s;
0296: } else {
0297: // force usage
0298: society_xml = null;
0299: break;
0300: }
0301: }
0302: } else if (s.equals("-h") || s.equals("-?")
0303: || s.equals("--help")) {
0304: // force usage
0305: society_xml = null;
0306: break;
0307: } else if (s.equals("-v") || s.equals("--verbose")) {
0308: inDefaults = false;
0309: inOverrides = false;
0310: verbose = true;
0311: } else if (s.equals("-vv") || s.equals("--debug")) {
0312: inDefaults = false;
0313: inOverrides = false;
0314: debug = true;
0315: } else if (s.equals("-w") || s.equals("--windows")) {
0316: inDefaults = false;
0317: inOverrides = false;
0318: windows = true;
0319: } else if (s.equals("-s") || s.equals("--society")) {
0320: inDefaults = false;
0321: inOverrides = false;
0322: society_xml = args[++i];
0323: } else if (s.equals("-r") || s.equals("--runtime")) {
0324: inDefaults = false;
0325: inOverrides = false;
0326: runtime_xml = args[++i];
0327: } else if (s.equals("--node")) {
0328: inDefaults = false;
0329: inOverrides = false;
0330: node_name = args[++i];
0331: } else if (s.equals("-d") || s.equals("--defaults")) {
0332: inDefaults = true;
0333: inOverrides = false;
0334: } else if (s.equals("-o") || s.equals("--overrides")) {
0335: inDefaults = false;
0336: inOverrides = true;
0337: } else {
0338: if (s.equals("-n") || s.equals("--nameserver")) {
0339: String x = args[++i];
0340: s = "-Dorg.cougaar.name.server"
0341: + (x.indexOf('=') < 0 ? "=" : ".") + x;
0342: }
0343: if (inDefaults || (!inOverrides && society_xml == null)) {
0344: if (default_vm_parameters == null) {
0345: default_vm_parameters = new ArrayList();
0346: }
0347: default_vm_parameters.add(s);
0348: } else {
0349: if (override_vm_parameters == null) {
0350: override_vm_parameters = new ArrayList();
0351: }
0352: override_vm_parameters.add(s);
0353: }
0354: }
0355: }
0356: if (society_xml == null) {
0357: System.err.println(USAGE);
0358: return false;
0359: }
0360: if (default_vm_parameters != null) {
0361: default_data = new CommandData(default_vm_parameters, null);
0362: }
0363: if (override_vm_parameters != null) {
0364: override_data = new CommandData(override_vm_parameters,
0365: null);
0366: }
0367: return true;
0368: }
0369:
0370: /**
0371: * Parse the XML file(s), mix in command-line -Ds, and return
0372: * the merged command line.
0373: */
0374: private CommandData generate_command() {
0375: CommandData ret = null;
0376: try {
0377: XMLReader xml_reader = XMLReaderUtils.createXMLReader();
0378: xml_reader.setEntityResolver(new MyResolver());
0379:
0380: // if only node_name is set then this is ambiguous, since it might be:
0381: // bin/cougaar mySociety.xml myRuntime.xml
0382: // so we check to see if the node_name is a file, and prefer that file
0383: // over possible "mySociety.xml" entry for "<node name='myRuntime.xml'/>"
0384: if (node_name != null && runtime_xml == null
0385: && (new File(node_name)).isFile()) {
0386: if (debug) {
0387: System.err
0388: .println("changing node_name to runtime_xml: "
0389: + node_name);
0390: }
0391: runtime_xml = node_name;
0392: node_name = null;
0393: }
0394:
0395: society_data = parse(xml_reader, node_name, society_xml);
0396: if (society_data == null || !society_data.processedNode) {
0397: System.err.println("Unable to find "
0398: + (node_name == null ? "any nodes" : "node "
0399: + node_name) + " in file "
0400: + society_xml);
0401: return null;
0402: }
0403:
0404: if (runtime_xml != null && !runtime_xml.equals(society_xml)) {
0405: String sys_node_name = (node_name != null ? node_name
0406: : society_data != null ? society_data.node
0407: : null);
0408: runtime_data = parse(xml_reader, sys_node_name,
0409: runtime_xml);
0410: }
0411:
0412: if (debug) {
0413: // okay to print to stderr
0414: System.err.println("override: " + override_data);
0415: System.err.println("runtime: " + runtime_data);
0416: System.err.println("society: " + society_data);
0417: System.err.println("default: " + default_data);
0418: }
0419:
0420: ret = merge_data();
0421:
0422: if (debug) {
0423: System.err.println("result: " + ret);
0424: }
0425: } catch (FileNotFoundException fnfe) {
0426: String s = fnfe.getMessage();
0427: System.err.println("File not found: " + s);
0428: } catch (Exception e) {
0429: e.printStackTrace();
0430: }
0431: return ret;
0432: }
0433:
0434: /**
0435: * Merge the command data from the XML files and command line
0436: * "--defaults" and "--overrides", returning the merged command.
0437: * <p>
0438: * The preference order is:
0439: * <ol>
0440: * <li>--overrides "-Ds"</li>
0441: * <li>society_xml</li>
0442: * <li>runtime_xml</li>
0443: * <li>--defaults "-Ds"</li>
0444: * <li>cougaar default (if "<class>" == Bootstrapper)</li>
0445: * </ol>
0446: */
0447: private CommandData merge_data() {
0448:
0449: // iterate over xml/-Ds
0450: String command = null;
0451: String clazz = null;
0452: List prog_parameters = Collections.EMPTY_LIST;
0453: Map m = new LinkedHashMap();
0454: boolean hasBootpath = false;
0455: String node = node_name;
0456: for (int x = 0; x < 4; x++) {
0457: CommandData cd = (x == 0 ? default_data
0458: : x == 1 ? society_data : x == 2 ? runtime_data
0459: : x == 3 ? override_data : null);
0460: if (cd == null) {
0461: continue;
0462: }
0463: if (cd.command != null) {
0464: command = cd.command;
0465: }
0466: if (cd.clazz != null) {
0467: clazz = cd.clazz;
0468: }
0469: if (!cd.prog_parameters.isEmpty()) {
0470: prog_parameters = cd.prog_parameters;
0471: }
0472: if (cd.node != null) {
0473: node = cd.node;
0474: }
0475: List l = cd.vm_parameters;
0476: for (int i = 0, n = l.size(); i < n; i++) {
0477: String s = (String) l.get(i);
0478: if (!s.startsWith("-")) {
0479: throw new RuntimeException(
0480: "vm_parameter must start with \"-\", not "
0481: + s);
0482: }
0483: if (windows) {
0484: s = toWindows(s);
0485: }
0486: if (s.startsWith("-D")) {
0487: int j = s.indexOf('=');
0488: if (j < 0) {
0489: m.put(s, null);
0490: } else {
0491: String s1 = s.substring(0, j);
0492: String s2 = s.substring(j + 1);
0493: m.put(s1, s2);
0494: }
0495: } else {
0496: if (s.startsWith("-Xbootclasspath")) {
0497: hasBootpath = true;
0498: }
0499: m.put(s, null);
0500: }
0501: }
0502: }
0503:
0504: // add cougaar defaults
0505: if (command == null) {
0506: command = "java";
0507: }
0508: if (clazz == null) {
0509: clazz = "org.cougaar.bootstrap.Bootstrapper";
0510: }
0511: if (clazz.equals("org.cougaar.bootstrap.Bootstrapper")) {
0512: String p0 = (m
0513: .containsKey("-Dorg.cougaar.bootstrap.application") ? (String) m
0514: .get("-Dorg.cougaar.bootstrap.application")
0515: : (prog_parameters.isEmpty() ? (null)
0516: : (String) prog_parameters.get(0)));
0517: if (p0 == null) {
0518: p0 = "org.cougaar.core.node.Node";
0519: m.put("-Dorg.cougaar.bootstrap.application", p0);
0520: }
0521: if ("org.cougaar.core.node.Node".equals(p0)) {
0522: if (!m.containsKey("-Dorg.cougaar.society.file")
0523: && society_xml != null) {
0524: m.put("-Dorg.cougaar.society.file", society_xml);
0525: }
0526: if (!m.containsKey("-Dorg.cougaar.runtime.file")
0527: && runtime_xml != null) {
0528: m.put("-Dorg.cougaar.runtime.file", runtime_xml);
0529: }
0530: if (!m.containsKey("-Dorg.cougaar.node.name")
0531: && node != null) {
0532: m.put("-Dorg.cougaar.node.name", node);
0533: }
0534: String runtime_path = (String) m
0535: .get("-Dorg.cougaar.runtime.path");
0536: if (runtime_path == null) {
0537: runtime_path = "$COUGAAR_RUNTIME_PATH";
0538: if (windows) {
0539: runtime_path = toWindows(runtime_path);
0540: }
0541: m.put("-Dorg.cougaar.runtime.path", runtime_path);
0542: }
0543: String society_path = (String) m
0544: .get("-Dorg.cougaar.society.path");
0545: if (society_path == null) {
0546: society_path = "$COUGAAR_SOCIETY_PATH";
0547: if (windows) {
0548: society_path = toWindows(society_path);
0549: }
0550: m.put("-Dorg.cougaar.society.path", society_path);
0551: }
0552: String install_path = (String) m
0553: .get("-Dorg.cougaar.install.path");
0554: if (install_path == null) {
0555: install_path = "$COUGAAR_INSTALL_PATH";
0556: if (windows) {
0557: install_path = toWindows(install_path);
0558: }
0559: m.put("-Dorg.cougaar.install.path", install_path);
0560: }
0561: if (!hasBootpath) {
0562: m.put("-Xbootclasspath/p:" + install_path
0563: + "/lib/javaiopatch.jar", null);
0564: }
0565: if (!m
0566: .containsKey("-Dorg.cougaar.core.node.InitializationComponent")) {
0567: m
0568: .put(
0569: "-Dorg.cougaar.core.node.InitializationComponent",
0570: "XML");
0571: }
0572: if (!m.containsKey("-Djava.class.path")) {
0573: m.put("-Djava.class.path", install_path
0574: + "/lib/bootstrap.jar");
0575: }
0576: }
0577: }
0578:
0579: // optionally remove bootstrapper & jar path
0580: if (clazz.equals("org.cougaar.bootstrap.Bootstrapper")
0581: && "true".equals(m.get("-D" + EXPAND_JAR_PATH_PROP))) {
0582: try {
0583: expandJarPath(m);
0584: // success
0585: m.remove("-D" + EXPAND_JAR_PATH_PROP);
0586: clazz = (String) m
0587: .remove("-Dorg.cougaar.bootstrap.application");
0588: } catch (Exception e) {
0589: System.err
0590: .println("Warning: Unable to remove bootstrapper, ignoring -D"
0591: + EXPAND_JAR_PATH_PROP);
0592: e.printStackTrace();
0593: }
0594: }
0595:
0596: // flatten map to list
0597: List vm_parameters = new ArrayList(m.size());
0598: for (Iterator iter = m.entrySet().iterator(); iter.hasNext();) {
0599: Map.Entry me = (Map.Entry) iter.next();
0600: String key = (String) me.getKey();
0601: String value = (String) me.getValue();
0602: if (value == null) {
0603: vm_parameters.add(key);
0604: } else {
0605: vm_parameters.add(key + "=" + value);
0606: }
0607: }
0608:
0609: return new CommandData(command, vm_parameters, clazz,
0610: prog_parameters, node_name, true);
0611: }
0612:
0613: /**
0614: * Rewrite the -Ds to remove the bootstrapper, expand the jar path,
0615: * and resolve all system properties that contain "\${<i>name</i>}"
0616: * expansions (see {@link SystemProperties.expandProperties()}.
0617: */
0618: private void expandJarPath(Map m) {
0619: // Build a properties table containing all our map properties,
0620: // resolving environment variables as necessary.
0621: //
0622: // For example, suppose we have:
0623: // -Dorg.cougaar.install.path=$COUGAAR_INSTALL_PATH
0624: // The jar finder will use this to find jars, e.g.
0625: // <cip>/lib:<cip>/sys
0626: // Since we're bypassing the shell, we must resolve the
0627: // $COUGAAR_INSTALL_PATH here, otherwise the jar finder will
0628: // see:
0629: // $COUGAAR_INSTALL_PATH/lib:$COUGAAR_INSTALL_PATH/sys
0630: // instead of real filesystem paths.
0631: //
0632: // Note that we only save the resolved -Ds that are set back
0633: // in the properties table. The above example's -D won't be
0634: // saved in our "m" map, since the Bootstrapper won't call
0635: // "props.setProperty(..)" on it. Currently only "\${name}"
0636: // properties will be copied back, due to the SystemProperties
0637: // "expandProperties(props)" method.
0638: if (debug) {
0639: System.err
0640: .println("rewriting command to remove bootstrapper");
0641: }
0642: Properties p = new Properties(SystemProperties.getProperties());
0643: for (Iterator iter = m.entrySet().iterator(); iter.hasNext();) {
0644: Map.Entry me = (Map.Entry) iter.next();
0645: String key = (String) me.getKey();
0646: if (!key.startsWith("-D")) {
0647: continue;
0648: }
0649: key = key.substring(2);
0650: String originalValue = (String) me.getValue();
0651: String value = SystemProperties.resolveEnv(originalValue,
0652: windows);
0653: if (debug && originalValue != null
0654: && !originalValue.equals(value)) {
0655: System.err.println("resolved -D" + key + "=" + value);
0656: }
0657: p.setProperty(key, value);
0658: }
0659: // record any "setProperty(..)" changes
0660: final Map m2 = m;
0661: final Properties props = new Properties(p) {
0662: public Object put(Object key, Object value) {
0663: Object ret = super .put(key, value);
0664: if (debug) {
0665: System.err.println("overriding -D" + key + "="
0666: + value);
0667: }
0668: m2.put("-D" + key, value);
0669: return ret;
0670: }
0671: };
0672: // wrap the bootstrapper to use our props
0673: Bootstrapper b = new Bootstrapper() {
0674: protected String getProperty(String key) {
0675: if ("org.cougaar.bootstrap.excludeJars".equals(key)) {
0676: return "javaiopatch.jar";
0677: }
0678: return props.getProperty(key);
0679: }
0680:
0681: protected String getProperty(String key, String def) {
0682: return props.getProperty(key, def);
0683: }
0684:
0685: protected Properties getProperties() {
0686: return props;
0687: }
0688: };
0689: // use the bootstrapper to compute the jar url list
0690: b.readPropertiesFromURL(props
0691: .getProperty("org.cougaar.properties.url"));
0692: SystemProperties.expandProperties(props);
0693: List l = b.computeURLs();
0694: if (debug) {
0695: System.err.println("found jars[" + l.size() + "]=" + l);
0696: }
0697: // replace the java -classpath with the jars
0698: StringBuffer buf = new StringBuffer();
0699: for (int i = 0; i < l.size(); i++) {
0700: URL url = (URL) l.get(i);
0701: if (i > 0) {
0702: buf.append(File.pathSeparator);
0703: }
0704: buf.append(url);
0705: }
0706: m.put("-Djava.class.path", buf.toString());
0707: // remove other bootstrapper -Ds
0708: m.put("-Dorg.cougaar.useBootstrapper", "false");
0709: m.remove("-Dorg.cougaar.class.path");
0710: m.remove("-Dorg.cougaar.jar.path");
0711: // the caller should use the bootstrap.application
0712: }
0713:
0714: /** Convert "$x" to "%x%". */
0715: private static final String toWindows(String s) {
0716: int j = s.indexOf('$');
0717: if (j < 0) {
0718: return s;
0719: }
0720: int n = s.length();
0721: StringBuffer buf = new StringBuffer(n + 4);
0722: int i = 0;
0723: while (true) {
0724: boolean escape = false;
0725: for (int k = j - 1; k >= i && s.charAt(k) == '\\'; k--) {
0726: escape = !escape;
0727: }
0728: buf.append(s.substring(i, j));
0729: i = j + 1;
0730: if (escape) {
0731: buf.append('$');
0732: } else {
0733: buf.append('%');
0734: boolean paren = (s.charAt(i) == '{');
0735: if (paren) {
0736: i++;
0737: }
0738: j = i;
0739: for (j = i; j < n; j++) {
0740: char ch = s.charAt(j);
0741: if (!((ch >= 'a' && ch <= 'z')
0742: || (ch >= 'A' && ch <= 'Z')
0743: || (ch >= '0' && ch <= '9') || (ch == '_'))) {
0744: break;
0745: }
0746: }
0747: buf.append(s.substring(i, j));
0748: buf.append('%');
0749: i = j;
0750: if (paren && s.charAt(i) == '}') {
0751: i++;
0752: }
0753: }
0754: j = s.indexOf('$', i);
0755: if (j < 0) {
0756: if (i < n) {
0757: buf.append(s.substring(i));
0758: }
0759: break;
0760: }
0761: }
0762: return buf.toString();
0763: }
0764:
0765: /** Parse an XML file, return the contained command data */
0766: private CommandData parse(XMLReader xml_reader, String node,
0767: String filename) throws Exception {
0768: XMLConfigHandler handler = new XMLConfigHandler(node);
0769: xml_reader.setContentHandler(handler);
0770: try {
0771: xml_reader.parse(filename);
0772: } catch (Exception e) {
0773: // do backwards compatible ".ini" check
0774: if (e instanceof FileNotFoundException
0775: || (e instanceof SAXParseException && "Document root element is missing."
0776: .equals(e.getMessage()))) {
0777: CommandData iniData = parseINI(filename);
0778: if (iniData != null) {
0779: return iniData;
0780: }
0781: }
0782: throw e;
0783: }
0784: return handler.getCommandData();
0785: }
0786:
0787: /** backwards compatibility for INI files (bug 3881) */
0788: private CommandData parseINI(String filename) {
0789: try {
0790: File f = new File(filename);
0791: if (!f.exists()) {
0792: f = new File(filename + ".ini");
0793: if (!f.exists()) {
0794: return null;
0795: }
0796: }
0797: } catch (Exception e) {
0798: return null;
0799: }
0800: String node = filename;
0801: if (node.regionMatches(true, (node.length() - 4), ".ini", 0, 4)) {
0802: node = node.substring(0, node.length() - 4);
0803: }
0804: int sep = node.lastIndexOf('/');
0805: if (sep >= 0) {
0806: node = node.substring(sep + 1);
0807: }
0808: sep = node.lastIndexOf('\\');
0809: if (sep >= 0) {
0810: node = node.substring(sep + 1);
0811: }
0812: List vm_parameters = Collections
0813: .singletonList("-Dorg.cougaar.core.node.InitializationComponent=File");
0814: return new CommandData(null, vm_parameters, null, null, node,
0815: true);
0816: }
0817:
0818: /** Java command data, including the -Ds */
0819: public static final class CommandData implements Serializable {
0820:
0821: private final String command;
0822: private final List vm_parameters;
0823: private final String clazz;
0824: private final List prog_parameters;
0825: private final String node;
0826: private final boolean processedNode;
0827:
0828: public CommandData(List vm_parameters, String node) {
0829: this (null, vm_parameters, null, null, node, false);
0830: }
0831:
0832: public CommandData(String command, List vm_parameters,
0833: String clazz, List prog_parameters, String node,
0834: boolean processedNode) {
0835: this .command = command;
0836: this .vm_parameters = (vm_parameters == null
0837: || vm_parameters.isEmpty() ? Collections.EMPTY_LIST
0838: : Collections.unmodifiableList(vm_parameters));
0839: this .clazz = clazz;
0840: this .prog_parameters = (prog_parameters == null
0841: || prog_parameters.isEmpty() ? Collections.EMPTY_LIST
0842: : Collections.unmodifiableList(prog_parameters));
0843: this .node = node;
0844: this .processedNode = processedNode;
0845: }
0846:
0847: /** The command, which is usually "java" */
0848: public String getCommand() {
0849: return command;
0850: }
0851:
0852: /** The -Ds and -Xs in the order specified by the XML file */
0853: public List getProperties() {
0854: return vm_parameters;
0855: }
0856:
0857: /** The classname, which is null if "-jar" is used */
0858: public String getClassname() {
0859: return clazz;
0860: }
0861:
0862: /** The arguments after the classname */
0863: public List getArguments() {
0864: return prog_parameters;
0865: }
0866:
0867: public String toString() {
0868: StringBuffer buf = new StringBuffer();
0869: if (command != null) {
0870: buf.append(command);
0871: }
0872: toBuf(buf, vm_parameters);
0873: if (clazz != null) {
0874: buf.append(" ").append(clazz);
0875: }
0876: toBuf(buf, prog_parameters);
0877: return buf.toString();
0878: }
0879:
0880: private void toBuf(StringBuffer buf, List l) {
0881: int n = (l == null ? 0 : l.size());
0882: for (int i = 0; i < n; i++) {
0883: buf.append(" ").append(l.get(i));
0884: }
0885: }
0886: }
0887:
0888: /** An XML resolver API */
0889: private interface Resolver extends EntityResolver, URIResolver {
0890: InputSource resolveEntity(String publicId, String systemId)
0891: throws SAXException, IOException;
0892:
0893: Source resolve(String href, String base)
0894: throws TransformerException;
0895: }
0896:
0897: /** XML resolver that looks in the current directory */
0898: private static class MyResolver implements Resolver {
0899: public InputSource resolveEntity(String publicId,
0900: String systemId) throws SAXException, IOException {
0901: return new InputSource(open(systemId));
0902: }
0903:
0904: public Source resolve(String href, String base)
0905: throws TransformerException {
0906: try {
0907: return new StreamSource(open(href));
0908: } catch (Exception e) {
0909: throw new TransformerException("resolve(" + href + ")",
0910: e);
0911: }
0912: }
0913:
0914: private InputStream open(String s) throws IOException {
0915: return new FileInputStream(s);
0916: }
0917: }
0918:
0919: /**
0920: * SAX XML parser for the "mySociety.xml" and "myRuntime.xml".
0921: * <p>
0922: * The two XML files use the same content format. See the
0923: * top-level class javadocs for example XML files.
0924: */
0925: private static class XMLConfigHandler extends DefaultHandler {
0926:
0927: private final String node_name;
0928:
0929: private final CharArrayWriter argValueBuffer = new CharArrayWriter();
0930:
0931: private boolean inNode;
0932:
0933: private boolean this Node;
0934: private boolean processedNode;
0935:
0936: private boolean inString;
0937:
0938: private String command;
0939: private String node;
0940: private List vm_parameters;
0941: private String clazz;
0942: private List prog_parameters;
0943:
0944: public XMLConfigHandler(String node_name) {
0945: this .node_name = node_name;
0946: this .node = node_name;
0947: }
0948:
0949: public CommandData getCommandData() {
0950: return new CommandData(command, vm_parameters, clazz,
0951: prog_parameters, node, processedNode);
0952: }
0953:
0954: // begin element
0955: public void startElement(String namespaceURI, String localName,
0956: String qName, Attributes atts) throws SAXException {
0957:
0958: if (localName.equals("node") || localName.equals("runtime")
0959: || localName.equals("vm_parameters")) {
0960: startNode(atts);
0961: }
0962:
0963: if (inNode && !this Node) {
0964: return;
0965: }
0966:
0967: if (localName.equals("command")) {
0968: startCommand(atts);
0969: } else if (localName.equals("vm_parameter")) {
0970: startVmParameter(atts);
0971: } else if (localName.equals("class")) {
0972: startClass(atts);
0973: } else if (localName.equals("prog_parameter")) {
0974: startProgParameter(atts);
0975: }
0976: }
0977:
0978: // misc characters within an element, e.g. vm_parameter data
0979: public void characters(char[] ch, int start, int length)
0980: throws SAXException {
0981: if (inString) {
0982: // inside string tag (e.g. vm_parameter), so save characters
0983: argValueBuffer.write(ch, start, length);
0984: }
0985: }
0986:
0987: // end element
0988: public void endElement(String namespaceURI, String localName,
0989: String qName) throws SAXException {
0990: if (localName.equals("node") || localName.equals("runtime")
0991: || localName.equals("vm_parameters")) {
0992: endNode();
0993: return;
0994: }
0995:
0996: if (inNode && !this Node) {
0997: return;
0998: }
0999:
1000: if (localName.equals("command")) {
1001: endCommand();
1002: } else if (localName.equals("vm_parameter")) {
1003: endVmParameter();
1004: } else if (localName.equals("prog_parameter")) {
1005: endProgParameter();
1006: } else if (localName.equals("class")) {
1007: endClass();
1008: } else {
1009: // ignore
1010: }
1011: }
1012:
1013: // our element handlers:
1014:
1015: private void startNode(Attributes atts) {
1016: inNode = true;
1017: if (processedNode) {
1018: return;
1019: }
1020: String name = atts.getValue("name");
1021: boolean anyName = (name == null || name.equals("*"));
1022: boolean anyNode = (node_name == null || node_name
1023: .equals("*"));
1024: this Node = (anyName || anyNode || node_name.equals(name));
1025: if (this Node) {
1026: node = (anyName ? (anyNode ? null : node_name) : name);
1027: processedNode = true;
1028: }
1029: }
1030:
1031: private void endNode() {
1032: inNode = false;
1033: this Node = false;
1034: }
1035:
1036: private void startString() {
1037: if (inString) {
1038: throw new RuntimeException(
1039: "Already have a string value buffer? "
1040: + argValueBuffer);
1041: }
1042: inString = true;
1043: }
1044:
1045: private String endString() {
1046: if (!inString) {
1047: throw new RuntimeException("Not in a string tag?");
1048: }
1049: inString = false;
1050: String s = argValueBuffer.toString().trim();
1051: argValueBuffer.reset();
1052: return s;
1053: }
1054:
1055: private void startCommand(Attributes atts) {
1056: startString();
1057: }
1058:
1059: private void endCommand() {
1060: command = endString();
1061: }
1062:
1063: private void startVmParameter(Attributes atts) {
1064: startString();
1065:
1066: String name = atts.getValue("name");
1067: if (name != null) {
1068: if (name.indexOf('=') >= 0) {
1069: throw new RuntimeException(
1070: "Invalid '=' in attribute name: " + name);
1071: }
1072: argValueBuffer.append(name.trim());
1073: argValueBuffer.append("=");
1074: }
1075: String value = atts.getValue("value");
1076: if (value != null) {
1077: argValueBuffer.append(value.trim());
1078: }
1079: }
1080:
1081: private void endVmParameter() {
1082: String s = endString();
1083: if (vm_parameters == null) {
1084: vm_parameters = new ArrayList();
1085: }
1086: vm_parameters.add(s);
1087: }
1088:
1089: private void startClass(Attributes atts) {
1090: startString();
1091: }
1092:
1093: private void endClass() {
1094: clazz = endString();
1095: }
1096:
1097: private void startProgParameter(Attributes atts) {
1098: startString();
1099: }
1100:
1101: private void endProgParameter() {
1102: if (prog_parameters == null) {
1103: prog_parameters = new ArrayList();
1104: }
1105: prog_parameters.add(endString());
1106: }
1107:
1108: }
1109: }
|