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