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