View Javadoc

1   // 
2   // Copyright (c) 2003, Caltha - Gajda, Krzewski, Mach, Potempski Sp.J. 
3   // All rights reserved. 
4   //   
5   // Redistribution and use in source and binary forms, with or without modification,  
6   // are permitted provided that the following conditions are met: 
7   //   
8   // * Redistributions of source code must retain the above copyright notice,  
9   //   this list of conditions and the following disclaimer. 
10  // * Redistributions in binary form must reproduce the above copyright notice,  
11  //   this list of conditions and the following disclaimer in the documentation  
12  //   and/or other materials provided with the distribution. 
13  // * Neither the name of the Caltha - Gajda, Krzewski, Mach, Potempski Sp.J.  
14  //   nor the names of its contributors may be used to endorse or promote products  
15  //   derived from this software without specific prior written permission. 
16  // 
17  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  
18  // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  
19  // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
20  // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,  
21  // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,  
22  // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
23  // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
24  // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)  
25  // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  
26  // POSSIBILITY OF SUCH DAMAGE. 
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 &lt;root&gt;</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 &lt;config&gt;</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>&lt;class-name&gt;</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         // static access only (commandline runnable class)
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         // add a shutdown hook that will tell the builder to kill it.
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 }