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