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