1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 package org.objectledge.container;
30
31 import java.lang.reflect.InvocationTargetException;
32 import java.lang.reflect.Method;
33
34 import org.apache.commons.cli.CommandLine;
35 import org.apache.commons.cli.CommandLineParser;
36 import org.apache.commons.cli.HelpFormatter;
37 import org.apache.commons.cli.Options;
38 import org.apache.commons.cli.ParseException;
39 import org.apache.commons.cli.PosixParser;
40 import org.apache.log4j.BasicConfigurator;
41 import org.apache.log4j.Logger;
42 import org.objectledge.filesystem.FileSystem;
43 import org.picocontainer.PicoContainer;
44
45 /***
46 * Allows running Ledge applications from the command line.
47 *
48 * <p>
49 * After the container is started and components are composed and configured,
50 * a designated component is looked up and method
51 * <code>void main(String[])</code> is invoked on it.
52 * Note that it this mehtod should be non-static so it may take advantage of the actual
53 * component instance composed by the container.
54 * </p>
55 *
56 * <h3>Recognized commandline options</h3>
57 * <p>
58 * <table class="bodyTable">
59 * <tr class="b">
60 * <th>syntax</th>
61 * <th>required</th>
62 * <th>default</th>
63 * <th width="100%">description</th>
64 * </tr>
65 * <tr class="a">
66 * <td>-h</td>
67 * <td>no</td>
68 * <td>n/a</td>
69 * <td>Display usage information and exit</td>
70 * </tr>
71 * <tr class="b">
72 * <td>-v</td>
73 * <td>no</td>
74 * <td>n/a</td>
75 * <td>Display version information on startup</td>
76 * </tr>
77 * <tr class="a">
78 * <td>-r <root></td>
79 * <td>no</td>
80 * <td>current working directory</td>
81 * <td>Root directory of Ledge FileSystem</td>
82 * </tr>
83 * <tr class="b">
84 * <td>-c <config></td>
85 * <td>no</td>
86 * <td>/config</td>
87 * <td>Base directory of the system's configuration</td>
88 * </tr>
89 * <tr class="a">
90 * <td><class-name></td>
91 * <td>yes</td>
92 * <td>none</td>
93 * <td>Class name of the component to be invoked</td>
94 * </tr>
95 * </table>
96 *
97 * <p>
98 * Any command line arguments following the componen class name will be passed verbatim
99 * to the component's main method.
100 * </p>
101 *
102 * <h3>Dependencies</h3>
103 * <ul>
104 * <li><a href="http://jakarta.apache.org/commons/cli/">Jakarta Commons CLI</a></li>
105 * </ul>
106 *
107 * <p>Created on Dec 22, 2003</p>
108 * @author <a href="Rafal.Krzewski">rafal@caltha.pl</a>
109 * @version $Id: Main.java,v 1.11 2005/07/07 08:30:02 zwierzem Exp $
110 */
111 public class Main
112 {
113 /*** version string. */
114 protected static final String VERSION = "0.1-dev";
115
116 private static Options options = new Options();
117
118 static
119 {
120 options.addOption("r", "root", true, "set local file system root directory");
121 options.addOption("c", "config", true, "set config directory FS path");
122 options.addOption("v", "version", false, "print version information");
123 options.addOption("h", "help", false, "print usage information and exit");
124 }
125
126 /***
127 * A private constructor - this class should be used statically only.
128 */
129 private Main()
130 {
131
132 }
133
134 /***
135 * Command line entry point.
136 *
137 * @param args command line arguments.
138 */
139 public static void main(String[] args)
140 {
141 CommandLineParser parser = new PosixParser();
142 try
143 {
144 CommandLine line = parser.parse(options, args);
145 if(line.hasOption("v"))
146 {
147 printVersion();
148 }
149 if(line.hasOption("h"))
150 {
151 printUsage();
152 }
153 else
154 {
155 String root = line.getOptionValue("r", System.getProperty("user.dir"));
156 String config = line.getOptionValue("c", "/config");
157 int componentArgCount = line.getArgList().size()-1;
158 if(componentArgCount < 0)
159 {
160 System.err.println("component class name required");
161 }
162 else
163 {
164 String componentClassName = (String)line.getArgList().get(0);
165 String[] componentArgs = new String[componentArgCount];
166 line.getArgList().subList(1, componentArgCount).toArray(componentArgs);
167 run(root, config, componentClassName, componentArgs);
168 }
169 }
170 }
171 catch(ParseException exp)
172 {
173 System.err.println("Command line parsing failed: " + exp.getMessage());
174 }
175 }
176
177 /***
178 * Prints version information.
179 */
180 protected static void printVersion()
181 {
182 System.out.println("ObjectLedge "+VERSION);
183 }
184
185 /***
186 * Prints usage infomration and exits.
187 */
188 protected static void printUsage()
189 {
190 HelpFormatter formatter = new HelpFormatter();
191 formatter.printHelp("ledge", options);
192 }
193
194 /***
195 * Runns LedgeContainer.
196 *
197 * @param root the file system root.
198 * @param config the configuration base directory.
199 * @param componentClassName the component to invoke.
200 * @param componentArgs the component arguments.
201 */
202 public static void run(String root, String config, String componentClassName,
203 String[] componentArgs)
204 {
205 BasicConfigurator.configure();
206 Logger log = Logger.getLogger(Main.class);
207 PicoContainer container = null;
208 try
209 {
210 FileSystem fs = FileSystem.getStandardFileSystem(root);
211 LedgeContainer ledgeContainer = new LedgeContainer(fs, config,
212 Main.class.getClassLoader());
213 container = ledgeContainer.getContainer();
214 addShutdownHook(ledgeContainer);
215 }
216 catch(Exception e)
217 {
218 log.error("Container composition failed", e);
219 }
220 Class componentClass = null;
221 Object component = null;
222 if(container != null)
223 {
224 try
225 {
226 componentClass = Class.forName(componentClassName);
227 component = container.getComponentInstance(componentClass);
228 if(component == null)
229 {
230 log.error("Component "+componentClassName+" is missing from the assembly");
231 }
232 }
233 catch(ClassNotFoundException e)
234 {
235 log.error("Component class "+componentClassName+" cannot be loaded", e);
236 }
237 }
238 Method method = null;
239 if(componentClass != null)
240 {
241 try
242 {
243 method = componentClass.getMethod("main",
244 new Class[] { (new String[0]).getClass() });
245 }
246 catch(NoSuchMethodException e)
247 {
248 log.error("Component class "+componentClassName+
249 " does not declare main(String[]) method", e);
250 }
251 }
252 if(method != null)
253 {
254 try
255 {
256 method.invoke(component, new Object[] { componentArgs });
257 }
258 catch(InvocationTargetException e)
259 {
260 log.error("Invocation of "+componentClassName+".main(String[]) threw exception",
261 e.getTargetException());
262 }
263 catch(Exception e)
264 {
265 log.error("Failed to invoke "+componentClassName+".main(String[])", e);
266 }
267 }
268 }
269
270 private static void addShutdownHook(final LedgeContainer ledgeContainer)
271 {
272
273 Runnable shutdownHook = new Runnable() {
274 public void run() {
275 System.out.println("Shutting Down NanoContainer");
276 try {
277 ledgeContainer.killContainer();
278 } catch (Exception e) {
279 e.printStackTrace();
280 } finally {
281 System.out.println("Exiting VM");
282 }
283 }
284 };
285 Runtime.getRuntime().addShutdownHook(new Thread(shutdownHook));
286 }
287 }