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