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