001: /*****************************************************************************
002: * Copyright (C) NanoContainer Organization. All rights reserved. *
003: * ------------------------------------------------------------------------- *
004: * The software in this package is published under the terms of the BSD *
005: * style license a copy of which has been included with this distribution in *
006: * the LICENSE.txt file. *
007: * *
008: *****************************************************************************/package org.nanocontainer;
009:
010: import java.io.File;
011: import java.io.IOException;
012: import java.net.URL;
013:
014: import org.apache.commons.cli.CommandLine;
015: import org.apache.commons.cli.CommandLineParser;
016: import org.apache.commons.cli.Options;
017: import org.apache.commons.cli.ParseException;
018: import org.apache.commons.cli.PosixParser;
019: import org.nanocontainer.script.ScriptedContainerBuilderFactory;
020: import org.picocontainer.defaults.ObjectReference;
021: import org.picocontainer.defaults.SimpleReference;
022:
023: /**
024: * Standalone offers a command line interface to NanoContainer.
025: * Standalone options are: -c <composition-file> [-q|-n|-h|-v]
026: * <ul>
027: * <li>-c: specifies composition file</li>
028: * <li>-q: quite mode</li>
029: * <li>-n: forces ScriptedContainerBuilderFactory to exit after start</li>
030: * <li>-h: print usage</li>
031: * <li>-v: print version</li>
032: * </ul>
033: */
034: public class Standalone {
035:
036: private static final char HELP_OPT = 'h';
037: private static final char VERSION_OPT = 'v';
038: private static final char COMPOSITION_OPT = 'c';
039: private static final char RESOURCE_OPT = 'r';
040: private static final char QUIET_OPT = 'q';
041: private static final char NOWAIT_OPT = 'n';
042:
043: private static final String DEFAULT_COMPOSITION_FILE = "composition.groovy";
044:
045: static final Options createOptions() {
046: Options options = new Options();
047: options.addOption(String.valueOf(HELP_OPT), "help", false,
048: "print this message and exit");
049: options.addOption(String.valueOf(VERSION_OPT), "version",
050: false, "print the version information and exit");
051: options.addOption(String.valueOf(COMPOSITION_OPT),
052: "composition", true, "specify the composition file");
053: options
054: .addOption(
055: String.valueOf(RESOURCE_OPT),
056: "resource",
057: true,
058: "specify the composition file (as a resource read from classpath - like inside a jar)");
059: options.addOption(String.valueOf(QUIET_OPT), "quiet", false,
060: "forces ScriptedContainerBuilderFactory to be quiet");
061: options
062: .addOption(String.valueOf(NOWAIT_OPT), "nowait", false,
063: "forces ScriptedContainerBuilderFactory to exit after start");
064: return options;
065: }
066:
067: public static void main(String[] args) throws IOException,
068: ClassNotFoundException {
069: new Standalone(args);
070: }
071:
072: public Standalone(String[] args) throws IOException,
073: ClassNotFoundException {
074: File defaultCompositionFile = new File(DEFAULT_COMPOSITION_FILE);
075: CommandLine cl = null;
076: Options options = createOptions();
077: if (args.length == 0 && !defaultCompositionFile.exists()) {
078: printUsage(options);
079: System.exit(-1);
080: }
081: try {
082: cl = getCommandLine(args, options);
083: } catch (ParseException e) {
084: System.out
085: .println("NanoContainer Standalone: Error in parsing arguments: ");
086: e.printStackTrace();
087: System.exit(-1);
088: }
089:
090: if (cl.hasOption(HELP_OPT)) {
091: printUsage(options);
092: System.exit(0);
093: }
094: if (cl.hasOption(VERSION_OPT)) {
095: printVersion();
096: System.exit(0);
097: }
098:
099: boolean quiet = cl.hasOption(QUIET_OPT);
100: boolean nowait = cl.hasOption(NOWAIT_OPT);
101: try {
102: String compositionFile = cl.getOptionValue(COMPOSITION_OPT);
103: String compositionResource = cl
104: .getOptionValue(RESOURCE_OPT);
105: Thread.currentThread().setContextClassLoader(
106: this .getClass().getClassLoader());
107: if (compositionFile != null) {
108: buildAndStartContainer(new File(compositionFile),
109: quiet, nowait);
110: } else if (compositionResource != null) {
111: buildAndStartContainer(Standalone.class
112: .getResource(compositionResource), quiet,
113: nowait);
114: } else {
115: if (defaultCompositionFile.exists()) {
116: buildAndStartContainer(defaultCompositionFile,
117: quiet, nowait);
118: } else {
119: printUsage(options);
120: System.exit(10);
121: }
122: }
123: } catch (RuntimeException e) {
124: System.err
125: .println("NanoContainer Standalone: Failed to start application. Cause : "
126: + e.getMessage());
127: e.printStackTrace();
128: throw e;
129: } catch (ClassNotFoundException e) {
130: System.err
131: .println("NanoContainer Standalone: Failed to start application. A Class was not found. Exception message : "
132: + e.getMessage());
133: e.printStackTrace();
134: throw e;
135: }
136: if (!quiet) {
137: System.out
138: .println("NanoContainer Standalone: Exiting main method.");
139: }
140: }
141:
142: /*
143: Now that the breadth/depth-first traversal of "child" containers, we should consider adding support
144: for "monitors" at a higher level of abstraction.
145:
146: I think that ideally this should be done on the multicaster level, so that we can get monitor
147: events whenever *any* method is called via the multicaster. That way we could easily intercept lifecycle
148: methods on individual components, not only on the container level.
149:
150: The most elegant way to deal with this is perhaps via Nanning, or we could add support for it
151: directly in the MulticastInvoker class. (It could be constructed with an additional argument
152: called InvocationInterceptor. MulticastInvoker would then call methods on this object in addition
153: to the subject. The InvocationInterceptor would serve the same purpose as this NanoContainerMonitor,
154: but at a much higher level of abstraction. It would be more reusable, since it would enable monitoring
155: outside the scope of nano. It could be useful in e.g. WebWork or other environments.
156:
157: I think it should be up to the ContainerComposer instances (in integrationkit) to decide what kind of
158: monitor/InvocationInterceptor to use.
159:
160: AH
161: */
162: private static void buildAndStartContainer(URL composition,
163: final boolean quiet, boolean nowait)
164: throws ClassNotFoundException {
165: final ScriptedContainerBuilderFactory scriptedContainerBuilderFactory = new ScriptedContainerBuilderFactory(
166: composition);
167: buildContainer(scriptedContainerBuilderFactory, nowait, quiet);
168: }
169:
170: private static void buildAndStartContainer(File composition,
171: boolean quiet, boolean nowait) throws IOException,
172: ClassNotFoundException {
173: final ScriptedContainerBuilderFactory scriptedContainerBuilderFactory = new ScriptedContainerBuilderFactory(
174: composition);
175: buildContainer(scriptedContainerBuilderFactory, nowait, quiet);
176: }
177:
178: private static void buildContainer(
179: final ScriptedContainerBuilderFactory scriptedContainerBuilderFactory,
180: boolean nowait, final boolean quiet) {
181: final ObjectReference containerRef = new SimpleReference();
182: scriptedContainerBuilderFactory.getContainerBuilder()
183: .buildContainer(containerRef, null, null, true);
184:
185: if (nowait == false) {
186: setShutdownHook(quiet, scriptedContainerBuilderFactory,
187: containerRef);
188: } else {
189: // shuttingDown(quiet, scriptedContainerBuilderFactory, containerRef);
190: }
191: }
192:
193: private static void setShutdownHook(
194: final boolean quiet,
195: final ScriptedContainerBuilderFactory scriptedContainerBuilderFactory,
196: final ObjectReference containerRef) {
197: // add a shutdown hook that will tell the builder to kill it.
198: Runnable shutdownHook = new Runnable() {
199: public void run() {
200: shuttingDown(quiet, scriptedContainerBuilderFactory,
201: containerRef);
202: }
203: };
204: Runtime.getRuntime().addShutdownHook(new Thread(shutdownHook));
205: }
206:
207: private static void shuttingDown(
208: final boolean quiet,
209: final ScriptedContainerBuilderFactory scriptedContainerBuilderFactory,
210: final ObjectReference containerRef) {
211: try {
212: scriptedContainerBuilderFactory.getContainerBuilder()
213: .killContainer(containerRef);
214: } catch (RuntimeException e) {
215: e.printStackTrace();
216: } finally {
217: if (!quiet) {
218: System.out
219: .println("NanoContainer Standalone: Exiting Virtual Machine");
220: }
221: }
222: }
223:
224: static CommandLine getCommandLine(String[] args, Options options)
225: throws ParseException {
226: CommandLineParser parser = new PosixParser();
227: return parser.parse(options, args);
228: }
229:
230: private static void printUsage(Options options) {
231: final String lineSeparator = System
232: .getProperty("line.separator");
233:
234: final StringBuffer usage = new StringBuffer();
235: usage.append(lineSeparator);
236: usage
237: .append("NanoContainer Standalone: -c <composition-file> [-q|-n|-h|-v]");
238: usage.append(options.getOptions());
239: System.out.println(usage.toString());
240: }
241:
242: private static void printVersion() {
243: System.out.println("1.1");
244: }
245:
246: }
|