    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