001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.core.node;
028:
029: import java.io.InputStream;
030: import java.lang.reflect.Field;
031: import java.net.URL;
032: import java.util.ArrayList;
033: import java.util.Arrays;
034: import java.util.Date;
035: import java.util.Iterator;
036: import java.util.List;
037: import java.util.Properties;
038: import org.cougaar.bootstrap.Bootstrapper;
039: import org.cougaar.bootstrap.SystemProperties;
040: import org.cougaar.core.component.BindingSite;
041: import org.cougaar.core.component.BindingUtility;
042: import org.cougaar.core.component.ComponentDescription;
043: import org.cougaar.core.component.ComponentDescriptions;
044: import org.cougaar.core.component.ContainerSupport;
045: import org.cougaar.core.component.ServiceBroker;
046: import org.cougaar.core.component.ServiceBrokerSupport;
047: import org.cougaar.util.Configuration;
048:
049: /**
050: * This component is the root component of the
051: * <a href="http://www.cougaar.org">Cougaar Agent Architecture</a>,
052: * containing the {@link #main} method.
053: * <p>
054: * Usage:<pre>
055: * <tt>java [props] org.cougaar.core.node.Node [props] [--help]</tt>
056: * </pre> where the "props" are "-D" System Properties, such as:<pre>
057: * "-Dorg.cougaar.node.name=NAME" -- name of the Node
058: * </pre>
059: * <p>
060: * A node refers to a per-JVM component that contains the Cougaar
061: * agents and per-JVM services. The primary job of the node is to:
062: * <ul>
063: * <li>Provide the initial launch methods</li>
064: * <li>Initialize the system properties</li>
065: * <li>Create the root ServiceBroker for the component model</li>
066: * <li>Create the NodeIdentificationService</li>
067: * <li>Create the initial ComponentInitializerService</li>
068: * <li>Create the AgentManager</li>
069: * <li>Create the NodeAgent, which in turn creates the other
070: * agents for this node</li>
071: * </ul>
072: * <p>
073: *
074: * @property org.cougaar.node.name
075: * The (required) name for this Node.
076: *
077: * @property org.cougaar.core.node.InitializationComponent
078: * Used to specify which service component to use. Can be passed
079: * in short hand (<em>DB</em>, <em>XML</em>, <em>File</em>) or as
080: * a fully specified class:
081: * <em>org.cougaar.core.node.DBComponentInitializerServiceComponent</em>
082: *
083: * @property org.cougaar.filename
084: * The file name (.ini) for starting this Node, which defaults to
085: * (<em>org.cougaar.node.name</em>+".ini") if both
086: * <em>org.cougaar.filename</em> and
087: * <em>org.cougaar.experiment.id</em> are not specified. If this
088: * property is specified then <em>org.cougaar.experiment.id</em>
089: * must not be specified.
090: *
091: * @property org.cougaar.experiment.id
092: * The experiment identifier for running this Node; see
093: * <em>org.cougaar.filename</em> for details.
094: *
095: * @property org.cougaar.install.path
096: * The <em>base</em> path for finding jar and configuration files.
097: */
098: public class Node extends ContainerSupport {
099: public static final String INSERTION_POINT = "Node";
100:
101: private static final String FILENAME_PROP = "org.cougaar.filename";
102: private static final String EXPTID_PROP = "org.cougaar.experiment.id";
103: public static final String INITIALIZER_PROP = "org.cougaar.core.node.InitializationComponent";
104:
105: private List params;
106:
107: /**
108: * Node entry point.
109: * <p>
110: * If org.cougaar.useBootstrapper is true, this method will load all jars
111: * file on the jar path (typically "lib/" and "sys/"). Otherwise, this
112: * method will rely solely on the classpath.
113: *
114: * @see #launch(String[])
115: */
116: // @deprecated
117: public static void main(String[] args) {
118: boolean useBootstrapper;
119: try {
120: useBootstrapper = SystemProperties.getBoolean(
121: "org.cougaar.useBootstrapper", true);
122: } catch (Exception e) {
123: useBootstrapper = true;
124: }
125: if (useBootstrapper) {
126: System.err
127: .println("-Dorg.cougaar.useBootstrapper is deprecated."
128: + " Invoke Bootstrapper directly.");
129: Bootstrapper.launch(Node.class.getName(), args);
130: } else {
131: launch(args);
132: }
133: }
134:
135: /**
136: * The real entry-point for Node, which is generally invoked via the
137: * bootstrapper.
138: *
139: * @see org.cougaar.bootstrap.Bootstrapper
140: */
141: public static void launch(Object[] args) {
142: // create the root service broker and binding site
143: final ServiceBroker rootsb = new ServiceBrokerSupport() {
144: };
145: BindingSite rootbs = new BindingSite() {
146: public ServiceBroker getServiceBroker() {
147: return rootsb;
148: }
149:
150: public void requestStop() {
151: }
152: };
153:
154: // create and load our node
155: try {
156: Node myNode = new Node();
157: if (args != null) {
158: myNode.setParameter(args);
159: }
160: BindingUtility.activate(myNode, rootbs, rootsb);
161: } catch (Throwable e) {
162: // catch all exceptions and exit gracefully
163: System.out
164: .println("Caught an exception at the highest try block."
165: + " Exception is: " + e);
166: e.printStackTrace();
167: }
168:
169: // the node-internal threads keep it alive, until "shutdown()" is called
170: }
171:
172: protected String specifyContainmentPoint() {
173: return INSERTION_POINT;
174: }
175:
176: protected ServiceBroker createChildServiceBroker(BindingSite bs) {
177: // node uses the root service broker
178: return getServiceBroker();
179: }
180:
181: protected ComponentDescriptions findInitialComponentDescriptions() {
182: return null;
183: }
184:
185: /**
186: * Set our "Object[]" args, called by {@link #launch}.
187: */
188: public void setParameter(Object obj) {
189: Object o = obj;
190: if (o instanceof Object[]) {
191: o = Arrays.asList((Object[]) o);
192: }
193: if (!(o instanceof List)) {
194: throw new RuntimeException(
195: "Expecting an Object[] or List, not "
196: + (o == null ? "null" : o.getClass()
197: .getName()));
198: }
199: params = (List) o;
200: }
201:
202: /**
203: * This method initializes and loads the node.
204: */
205: public void load() {
206: super .load();
207:
208: // take params
209: List args = new ArrayList();
210: if (params != null) {
211: args.addAll(params);
212: params = null;
213: }
214:
215: // set any externally-defined system properties:
216: // 1) from a "SetPropertiesComponent" (e.g. see NodeApplet)
217: // 2) from a local ".properties" file (deprecated)
218: // 3) from the command line (also checks for "--help")
219: ComponentDescription set_props_desc = getPropertiesDescription(args);
220: if (set_props_desc != null) {
221: add(set_props_desc);
222: }
223: loadSystemProperties();
224: if (!setSystemProperties(args)) {
225: return; // must be "--help"
226: }
227:
228: // display the version info
229: printVersion(true);
230:
231: // add the component initializer service (i.e. configuration service)
232: ComponentDescription init_desc = getInitializerDescription();
233: if (init_desc != null) {
234: add(init_desc);
235: }
236:
237: // add the agent manager, which loads the node-agent
238: add(new ComponentDescription(
239: "org.cougaar.core.agent.AgentManager",
240: "Node.Component",
241: "org.cougaar.core.agent.AgentManager", null, //codebase
242: args, null, //certificate
243: null, //lease
244: null, //policy
245: ComponentDescription.PRIORITY_HIGH));
246: }
247:
248: /** Get and remove the {@link SetPropertiesComponent} from the list. */
249: private static ComponentDescription getPropertiesDescription(List l) {
250: // We must set our -Ds very early on, before any call to
251: // SystemProperties.get*"
252: // so we load this component here.
253: //
254: // In particular, we must set our -Ds before we attempt to access any
255: // properties in this class (e.g. in "printVersion") or our Logger.
256: //
257: // Long-term we should probably remove this component and have the external
258: // container call an equivalent library method.
259: List props_params = null;
260: for (int i = 0; i < l.size(); i++) {
261: Object o = l.get(i);
262: if (o instanceof Object[]) {
263: o = Arrays.asList((Object[]) o);
264: }
265: if (!(o instanceof List))
266: continue;
267: List p = (List) o;
268: if (p.size() != 4)
269: continue;
270: if (!"Node.AgentManager.Agent.Component".equals(p.get(0)))
271: continue;
272: if (!"HIGH".equals(p.get(1)))
273: continue;
274: Object c = p.get(2);
275: if (c instanceof Class) {
276: c = ((Class) c).getName();
277: }
278: if (!"org.cougaar.core.node.SetPropertiesComponent"
279: .equals(c))
280: continue;
281: l.remove(i--);
282:
283: props_params = p.subList(3, p.size());
284: }
285:
286: if (props_params == null)
287: return null;
288: return new ComponentDescription(
289: "org.cougaar.core.node.SetPropertiesComponent",
290: "Node.Component",
291: "org.cougaar.core.node.SetPropertiesComponent", null, //codebase
292: props_params, null, //certificate
293: null, //lease
294: null, //policy
295: ComponentDescription.PRIORITY_HIGH);
296: }
297:
298: /** Get the {@link ComponentInitializerService} component description. */
299: private ComponentDescription getInitializerDescription() {
300: // The ComponentInitializerService defines our configuration.
301: //
302: // We must load this component before or very early on in the AgentManager,
303: // since the AgentManager requires this service to find the Agent-level
304: // binders and configure the node-agent.
305: //
306: // We load this service in the Node, since its ServiceBroker is above the
307: // "root" AgentManager's ServiceBroker. This allows a node-agent component
308: // to override the "root" implementation of this service, as illustrated
309: // in ConfiguratorBase.
310: //
311: // We plan to refactor the initializer design. For future reference,
312: // here's a sketch of the proposed design:
313: //
314: // - The Node (or higher-level external container) will define an
315: // a) ApplicationConfigurationService (== XML parser), and an
316: // b) EnvironmentConfigurationService (== XSL template)
317: //
318: // - The ComponentInitializationService (CIS) will use the above
319: // services, and will be relatively trivial.
320: //
321: // - The ApplicationConfigurationService (ACS) will be analogous to our
322: // current XMLConfigHandler. It will provide the agent's application-
323: // specific component list and the name of its environment-specific
324: // template. The API will be:
325: // AppStruct get(String agentName, Map appOptions);
326: // where:
327: // class AppStruct {
328: // String agentName;
329: // String envName; // e.g. "SimpleAgent.xsl"
330: // Map envOptions; // usually null
331: // List appCompDescs; // e.g. domain-specific plugins
332: // }
333: // A null agentName will return the node-agent's entry.
334: // The appOptions will usually be null, but may be used in the future,
335: // (e.g. to support a high-level role-based agent configuration).
336: // The returned envOptions will typically be null, except for special
337: // cases where options must be passed from the application XML to
338: // the environment XSL, e.g.:
339: // 1) The WP Server marker component
340: // 2) The list of local agents, for the AgentLoader
341: // It will also be used to support per-agent template options, e.g.:
342: // <agent ...>
343: // <env_option name="servlets" value="false"/>
344: // ...
345: // </agent>
346: //
347: // - The EnvironmentConfigurationService (ECS) will be analogous to our
348: // current XSLTransformer. It will interpret an XSL file with
349: // name=value options to compute the list of infrastructure components.
350: // The API will be:
351: // EnvStruct get(String envName, Map envOptions);
352: // where:
353: // class EnvStruct {
354: // List envCompDescs; // e.g. "StandardBlackboard", etc.
355: // }
356: // Usually the envOptions will be null and will default to our XSL
357: // -Dorg.cougaar.society.xsl.param.$name=$value
358: // map entries.
359: // The envName will be an XSL filename, e.g.
360: // SimpleAgent.xsl
361: // The XSL template will be applied against an empty in-memory XML
362: // file. Instead of inlining the application XML components, the XSL
363: // template will inline "cutpoint" marker components, e.g.:
364: // <component class="cutpoint" name="HIGH" insertionpoint="..."/>
365: //
366: // - The CIS implementation will lookup an agent's AppStruct in the ACS,
367: // then it's EnvStruct in the ECS, then insert the appCompDescs in
368: // between the matching cutpoints in the envCompDescs. This merged
369: // result defines the complete agent's configuration and will be
370: // cached.
371: //
372: // The above design will allow "addAgent(...)" to specify the list of
373: // appCompDescs but still apply the template. The ConfiguratorBase will
374: // be replaced by a new ACS implementation.
375: String classname = SystemProperties
376: .getProperty(INITIALIZER_PROP);
377: if (classname == null) {
378: // get initializer, defaults to XML
379: classname = (SystemProperties.getProperty(FILENAME_PROP) != null ? "File"
380: : SystemProperties.getProperty(EXPTID_PROP) != null ? "DB"
381: : "XML");
382: SystemProperties.setProperty(INITIALIZER_PROP, classname);
383: }
384: if (classname.equals("null"))
385: return null;
386: if (classname.indexOf(".") < 0) {
387: // if full class name not specified, intuit it
388: classname = "org.cougaar.core.node." + classname
389: + "ComponentInitializerServiceComponent";
390: }
391: return new ComponentDescription(classname, Node.INSERTION_POINT
392: + ".Component", classname, null, //codebase
393: null, //params
394: null, //certificate
395: null, //lease
396: null, //policy
397: ComponentDescription.PRIORITY_HIGH);
398: }
399:
400: /**
401: * Convert any command-line args into System Properties.
402: * <p>
403: * System properties are preferred, since it simplifies the
404: * configuration to just a non-ordered Set of "-D" properties.
405: * <p>
406: * The only non "-D" command line arguments are:<pre>
407: * -n ARG equivalent to "-Dorg.cougaar.node.name=ARG"
408: * -c ignored, ancient "clear database" switch
409: * --?version display version information and exit
410: * --?info display terse version information and exit
411: * --?help display usage help and exit
412: * <i>other</i> display error message and exit
413: * </pre>
414: * <p>
415: * Also supported are post-classname "-D" command-line properties:
416: * "java .. classname -Darg .."
417: * which will override the usual "java -Darg .. classname .."
418: * properties. For example, "java -Dx=y classname -Dx=z" is
419: * equivalent to "java -Dx=z classname". This can be useful when
420: * writing scripts that simply append properties to a command-line.
421: * <p>
422: * @return false if node should exit
423: */
424: private static boolean setSystemProperties(List args) {
425: // separate the args into "-D" properties and normal arguments
426: for (int i = 0; i < args.size(); i++) {
427: Object oi = args.get(i);
428: if (!(oi instanceof String))
429: continue;
430: String argi = (String) oi;
431: if (argi.startsWith("-D")) {
432: // add a "late" system property
433: int sepIdx = argi.indexOf('=');
434: if (sepIdx < 0) {
435: SystemProperties.setProperty(argi.substring(2), "");
436: } else {
437: SystemProperties.setProperty(argi.substring(2,
438: sepIdx), argi.substring(sepIdx + 1));
439: }
440: } else if (argi.equals("-n")) {
441: // old "-n node" pattern
442: String name = (String) args.get(++i);
443: SystemProperties.setProperty("org.cougaar.node.name",
444: name);
445: } else if (argi.equals("-c")) {
446: // ignore
447: } else {
448: // some form of exit
449: if (argi.equals("-version") || argi.equals("--version")) {
450: printVersion(true);
451: } else if (argi.equals("-info")
452: || argi.equals("--info")) {
453: printVersion(false);
454: } else if (argi.equals("-help")
455: || argi.equals("--help")) {
456: System.out
457: .print("Usage: java [JVM_OPTIONS] [-D..] "
458: + Node.class.getName()
459: + " [-D..] [ARGS]"
460: + "\nA Node manages and executes Cougaar agents.\n"
461: + "\n -Dname=value set configuration property."
462: + "\n -version, --version output version information and exit."
463: + "\n -info, --info output terse version information and exit."
464: + "\n -help, --help display this help and exit.\n"
465: + "\nSee <http://www.cougaar.org> for further help and bug reports.\n");
466: } else {
467: System.err
468: .println("Node: unrecognized option `"
469: + argi
470: + "'"
471: + "\nTry `Node --help' for more information.");
472: }
473: return false;
474: }
475: }
476: return true;
477: }
478:
479: /**
480: * Parse and load system properties from a well-known file, as
481: * defined by a system property
482: * (default "$INSTALL/configs/common/system.properties")
483: * it will only load properties which do not already have a value.
484: * <p>
485: * Property values are interpreted with Configuration.resolveValue to
486: * do installation substitution.
487: *
488: * @property org.cougaar.core.node.properties
489: * @note The property org.cougaar.core.node.properties must be
490: * defined as a standard java -D argument, as it is evaluated
491: * extremely early in the Node boot process.
492: */
493: private static void loadSystemProperties() {
494: String u = SystemProperties
495: .getProperty("org.cougaar.core.node.properties");
496: if (u == null) {
497: u = "$INSTALL/configs/common/system.properties";
498: }
499:
500: try {
501: URL cip = Configuration.canonicalizeElement(u);
502: if (cip != null) {
503: Properties p = new Properties();
504: InputStream in = cip.openStream();
505: try {
506: p.load(in);
507: } finally {
508: in.close();
509: }
510: for (Iterator it = p.keySet().iterator(); it.hasNext();) {
511: String key = (String) it.next();
512: if (SystemProperties.getProperty(key) == null) {
513: try {
514: String value = p.getProperty(key);
515: value = Configuration.resolveValue(value);
516: SystemProperties.setProperty(key, value);
517: } catch (RuntimeException re) {
518: re.printStackTrace();
519: }
520: }
521: }
522: } // if cip not null
523: } catch (Exception e) {
524: // failed to open input stream
525: // or canonicalizeElement had a MalformedURLException
526: // or...
527:
528: // Failed to loadSystemProperties
529: //e.printStackTrace();
530: // System.err.println("Failed to loadSystemProperties from " + u, e);
531: }
532: }
533:
534: private static void printVersion(boolean fullFormat) {
535: String version = null;
536: long buildTime = -1;
537: String repositoryTag = null;
538: boolean repositoryModified = false;
539: long repositoryTime = -1;
540: try {
541: Class vc = Class.forName("org.cougaar.Version");
542: Field vf = vc.getField("version");
543: Field bf = vc.getField("buildTime");
544: version = (String) vf.get(null);
545: buildTime = bf.getLong(null);
546: Field tf = vc.getField("repositoryTag");
547: Field rmf = vc.getField("repositoryModified");
548: Field rtf = vc.getField("repositoryTime");
549: repositoryTag = (String) tf.get(null);
550: repositoryModified = rmf.getBoolean(null);
551: repositoryTime = rtf.getLong(null);
552: } catch (Exception e) {
553: // Failed to get version info, reflection problem
554: }
555:
556: if (!(fullFormat)) {
557: System.out.println("COUGAAR\t" + version + "\t" + buildTime
558: + "\t" + repositoryTag + "\t" + repositoryModified
559: + "\t" + repositoryTime);
560: return;
561: }
562:
563: synchronized (System.out) {
564: System.out.print("COUGAAR ");
565: if (version == null) {
566: System.out.println("(unknown version)");
567: } else {
568: System.out.println(version
569: + " built on "
570: + ((buildTime > 0) ? ((new Date(buildTime))
571: .toString()) : "(unknown time)"));
572: }
573: System.out
574: .println("Repository: "
575: + ((repositoryTag != null) ? (repositoryTag + (repositoryModified ? " (modified)"
576: : ""))
577: : "(unknown tag)")
578: + " on "
579: + ((repositoryTime > 0) ? ((new Date(
580: repositoryTime)).toString())
581: : "(unknown time)"));
582: String vminfo = SystemProperties
583: .getProperty("java.vm.info");
584: String vmv = SystemProperties
585: .getProperty("java.vm.version");
586: System.out.println("VM: JDK " + vmv + " (" + vminfo + ")");
587: String os = SystemProperties.getProperty("os.name");
588: String osv = SystemProperties.getProperty("os.version");
589: System.out.println("OS: " + os + " (" + osv + ")");
590: }
591: }
592: }
|