KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > sapia > util > ApplicationStarter


1 package org.sapia.util;
2
3
4 // Import of Sun's JDK classes
5
// ---------------------------
6
import java.io.File JavaDoc;
7
8 import java.lang.reflect.InvocationTargetException JavaDoc;
9 import java.lang.reflect.Method JavaDoc;
10
11 import java.net.MalformedURLException JavaDoc;
12 import java.net.URL JavaDoc;
13 import java.net.URLClassLoader JavaDoc;
14
15 import java.util.ArrayList JavaDoc;
16 import java.util.StringTokenizer JavaDoc;
17
18
19 /**
20  * <P>
21  * The application starter allows to execute the <CODE>main(String[])</CODE> method of a
22  * java class from a child classloader of the system classloader. This delegation of the
23  * execution of a java class to a child classloader allows an application to be independant
24  * in the classes that it loads (because they are not loaded by the system classloader)
25  * and give the opportunity to other java application to be loaded by other child classloader
26  * of the system classloader without any clash of class name.
27  * </P>
28  * <P>
29  * The application starter can be called with different options:
30  * <UL>
31  * <LI><B>-ashelp</B> This option prints a help message on the standard output and exit.</LI>
32  * <LI><B>-asdebug</B> This options will print on the standard output some debugging
33  * information about the execution of the application starter.</LI>
34  * <LI><B>-ascp {resources}</B> This option defines the resources that will be used by
35  * the child classloader to execute the main class. Each resource can be an absolute
36  * file name or a URL and each resource need to be seperated by the system path separator.</LI>
37  * <LI><B>class</B> The name of the java class to execute from the child classloader.</LI>
38  * <LI><B>arguments...</B> The list of application arguments that is passed to the execute class.</LI>
39  * </UL>
40  * </P>
41  * <P>
42  * Here is two examples of the usage of the application starter. The first example will show
43  * the online help message of the application starter and exit. The second example would create
44  * a new classloader that would contain the file C:\test.jar as only resource, create a new thread
45  * and, from that new thread, call the method <CODE>main(String[])</CODE> on the class MyMainClass
46  * passing the two arguments 'foo' and 'bar'.
47  * <UL>
48  * <LI><CODE>java org.sapia.util.ApplicationStarter -ashelp</CODE></LI>
49  * <LI><CODE>java org.sapia.util.ApplicationStarter -asdebug -ascp C:\test.jar MyMainClass foo bar</CODE></LI>
50  * </UL>
51  * </P>
52  * <P>
53  * Finally the application starter can be called programmatically using the <CODE>start</CODE> method.
54  * </P>
55  *
56  * @author Jean-Cedric Desrochers
57  * <dl>
58  * <dt><b>Copyright:</b><dd>Copyright &#169; 2002-2003 <a HREF="http://www.sapia-oss.org">Sapia Open Source Software</a>. All Rights Reserved.</dd></dt>
59  * <dt><b>License:</b><dd>Read the license.txt file of the jar or visit the
60  * <a HREF="http://www.sapia-oss.org/license.html">license page</a> at the Sapia OSS web site</dd></dt>
61  * </dl>
62  */

63 public class ApplicationStarter implements Runnable JavaDoc {
64   /////////////////////////////////////////////////////////////////////////////////////////
65
///////////////////////////////// INSTANCE ATTRIBUTES /////////////////////////////////
66
/////////////////////////////////////////////////////////////////////////////////////////
67

68   /** The main class to execute. */
69   private Class JavaDoc _theMainClass;
70
71   /** The array of arguments to pass to the main method. */
72   private String JavaDoc[] _theArguments;
73
74   /////////////////////////////////////////////////////////////////////////////////////////
75
//////////////////////////////////// CONSTRUCTORS /////////////////////////////////////
76
/////////////////////////////////////////////////////////////////////////////////////////
77

78   /**
79    * Creates a new ApplicationStarter with the passed in arguments.
80    *
81    * @param aMainClass The class on which to call the <CODE>main()</CODE> method.
82    * @param someArguments The array of argument to pass to the main class.
83    */

84   protected ApplicationStarter(Class JavaDoc aMainClass, String JavaDoc[] someArguments) {
85     _theMainClass = aMainClass;
86     _theArguments = someArguments;
87   }
88
89   /////////////////////////////////////////////////////////////////////////////////////////
90
/////////////////////////////////// STATIC METHODS ////////////////////////////////////
91
/////////////////////////////////////////////////////////////////////////////////////////
92

93   /**
94    * Main method of the ApplicationStarter class. It first parses the arguments passed in
95    * and then it creates a classloader for the main class to execute. Finally, it starts
96    * of a new thread that will call the <CODE>main(String[])</CODE> method of the class.
97    *
98    * @param args The arguments that define the options of the application starter.
99    */

100   public static void main(String JavaDoc[] args) {
101     try {
102       boolean isDebug = false;
103       String JavaDoc anApplicationClasspath = null;
104       String JavaDoc aClassName = null;
105       String JavaDoc[] someArguments = new String JavaDoc[0];
106
107       // Parse the arguments
108
for (int i = 0; i < args.length; i++) {
109         if (args[i].equals("-ashelp")) {
110           usage();
111           System.exit(0);
112         } else if (args[i].equals("-asdebug")) {
113           isDebug = true;
114         } else if (args[i].equals("-ascp")) {
115           anApplicationClasspath = args[++i];
116         } else if (aClassName == null) {
117           aClassName = args[i];
118         } else {
119           if (someArguments.length == 0) {
120             someArguments = new String JavaDoc[args.length - i];
121           }
122
123           someArguments[someArguments.length - (args.length - i)] = args[i];
124         }
125       }
126
127       start(anApplicationClasspath, aClassName, someArguments, isDebug);
128     } catch (ApplicationStarterException ase) {
129       ase.printStackTrace();
130       System.exit(1);
131     }
132   }
133
134   /**
135    * This start method allow the ApplicationStarter to start a new application programmatically
136    * passing in the required arguments.
137    *
138    * @param aClasspath The list of resources that defines the classpath use to start the application.
139    * Each resource of the classpath must be seperated by the system path seperator defined by
140    * the <CODE>path.separator</CODE> system proprety and by the static attribute
141    * <CODE>java.io.File.pathSeparator</CODE>.
142    * @param aClassName The qualified name of the class that contains the <CODE>main()</CODE> method to start.
143    * @param someArguments The arguments that would be passed to the <CODE>main()</CODE> method of the class.
144    * @param isDebug Indicates of it start the application in debug mode or not.
145    * @exception ApplicationStarterException If an error occurs while starting the application.
146    */

147   public static void start(String JavaDoc aClasspath, String JavaDoc aClassName,
148     String JavaDoc[] someArguments, boolean isDebug) throws ApplicationStarterException {
149     try {
150       // Logging the configuration
151
if (isDebug) {
152         logState(aClasspath, aClassName, someArguments);
153       }
154
155       // Create a class loader for the classpath
156
ClassLoader JavaDoc aClassLoader = createClassLoaderFor(aClasspath, isDebug);
157
158       // Logging the classloader
159
if (isDebug) {
160         System.out.println("\nCreated the classloader " + aClassLoader);
161       }
162
163       // Loading the main class and invoke the main class
164
Class JavaDoc aMainClass = aClassLoader.loadClass(aClassName);
165       ApplicationStarter aStarter = new ApplicationStarter(aMainClass,
166           someArguments);
167
168       // Create a new thread instance and start it
169
Thread JavaDoc aThread = new Thread JavaDoc(aStarter);
170       aThread.setName(aClassName.substring(aClassName.lastIndexOf(".") + 1));
171       aThread.setContextClassLoader(aClassLoader);
172       aThread.start();
173     } catch (ClassNotFoundException JavaDoc cnfe) {
174       if (!isDebug) {
175         logState(aClasspath, aClassName, someArguments);
176       }
177
178       StringBuffer JavaDoc aMessage = new StringBuffer JavaDoc();
179       aMessage.append("Unable to load the class ").append(aClassName)
180               .append(" - ").append(cnfe.getMessage());
181       throw new ApplicationStarterException(aMessage.toString(), cnfe);
182     } catch (RuntimeException JavaDoc re) {
183       if (!isDebug) {
184         logState(aClasspath, aClassName, someArguments);
185       }
186
187       StringBuffer JavaDoc aMessage = new StringBuffer JavaDoc();
188       aMessage.append("System error starting the class").append(aClassName);
189       throw new ApplicationStarterException(aMessage.toString(), re);
190     }
191   }
192
193   /**
194    * Prints out to the standard output the options passed to this application starter.
195    *
196    * @param anApplicationClasspath The classpath to use to load the main class.
197    * @param aClassName The name of the main class to execute.
198    * @param someArguments The application arguments to pass to the main class in
199    * the <CODE>main(String[])</CODE> method.
200    */

201   private static void logState(String JavaDoc anApplicationClasspath,
202     String JavaDoc aClassName, String JavaDoc[] someArguments) {
203     StringBuffer JavaDoc aBuffer = new StringBuffer JavaDoc(
204         "\nSAPIA Application Starter 1.0\n=============================");
205     aBuffer.append("\nStarting application with this configuration:");
206     aBuffer.append("\n appClasspath: ").append(anApplicationClasspath)
207            .append("\n className : ").append(aClassName);
208
209     if (aClassName != null) {
210       aBuffer.append("\n threadName : ").append(aClassName.substring(aClassName.lastIndexOf(
211             ".") + 1));
212     }
213
214     aBuffer.append("\n appArguments: ");
215
216     if (someArguments.length == 0) {
217       aBuffer.append("[]");
218     } else {
219       for (int i = 0; i < someArguments.length; i++) {
220         aBuffer.append("[").append(someArguments[i]).append("] ");
221       }
222     }
223
224     System.out.println(aBuffer.toString());
225   }
226
227   /**
228    * Prints out on the standard output the usage of the main method of this class.
229    */

230   public static void usage() {
231     StringBuffer JavaDoc aBuffer = new StringBuffer JavaDoc("SAPIA Application Starter 1.0\n");
232     aBuffer.append(
233       "Usage: java org.sapia.util.ApplicationStarter [options] {class} [args...]\n")
234            .append("Options:\n").append(" -ashelp\t\tprint this message\n")
235            .append(" -asdebug\t\tprint debugging information\n")
236            .append(" -ascp <resources>\tthe classpath of the classloader that will execute the main class\n")
237            .append("Example: java org.sapia.util.ApplicationStarter -ashelp\n")
238            .append("Example: java org.sapia.util.ApplicationStarter -asdebug -ascp test.jar MyMainClass foo bar\n");
239
240     System.out.println(aBuffer.toString());
241   }
242
243   /**
244    * Creates a class loader using the resources specified in the classpath string passed in. If the
245    * classpath passed in is null or empty, it returns the system classloader. Otherwise it returns
246    * the created class loader with the system class loader as its parent.
247    *
248    * @param aClasspath The classpath string (can be null);
249    * @return The created class loader or the system class loader if the classpath passed in is null or empty.
250    */

251   private static ClassLoader JavaDoc createClassLoaderFor(String JavaDoc aClasspath,
252     boolean isDebug) {
253     ClassLoader JavaDoc aClassLoader;
254
255     if ((aClasspath == null) || (aClasspath.length() == 0)) {
256       aClassLoader = ClassLoader.getSystemClassLoader();
257     } else {
258       try {
259         ArrayList JavaDoc someURLs = new ArrayList JavaDoc();
260         String JavaDoc aHomeDir = System.getProperty("user.dir");
261
262         for (StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(aClasspath,
263               File.pathSeparator); st.hasMoreTokens();) {
264           String JavaDoc aToken = st.nextToken();
265
266           if (!aToken.startsWith("file:/") || !aToken.startsWith("http:/")) {
267             File JavaDoc aFile = new File JavaDoc(aToken);
268
269             if (aFile.exists()) {
270               aToken = aFile.toURL().toExternalForm();
271             } else {
272               aFile = new File JavaDoc(aHomeDir, aToken);
273
274               if (aFile.exists()) {
275                 aToken = aFile.toURL().toExternalForm();
276               }
277             }
278           }
279
280           URL JavaDoc anURL = new URL JavaDoc(aToken);
281           someURLs.add(anURL);
282         }
283
284         if (isDebug) {
285           for (int i = 0; i < someURLs.size(); i++) {
286             System.out.println(((URL JavaDoc) someURLs.get(i)).toExternalForm());
287           }
288         }
289
290         aClassLoader = URLClassLoader.newInstance((URL JavaDoc[]) someURLs.toArray(
291               new URL JavaDoc[0]), ClassLoader.getSystemClassLoader());
292       } catch (MalformedURLException JavaDoc mue) {
293         throw new RuntimeException JavaDoc(
294           "Unable to create a class loader using the classpath: " + aClasspath);
295       }
296     }
297
298     return aClassLoader;
299   }
300
301   /////////////////////////////////////////////////////////////////////////////////////////
302
/////////////////////////////// INTERACE IMPLEMENTATION ///////////////////////////////
303
/////////////////////////////////////////////////////////////////////////////////////////
304

305   /**
306    * Run method of the Runnable interface that calls the main method of the main class.
307    */

308   public void run() {
309     try {
310       Method JavaDoc aMainMethod = _theMainClass.getDeclaredMethod("main",
311           new Class JavaDoc[] { String JavaDoc[].class });
312       aMainMethod.invoke(_theMainClass, new Object JavaDoc[] { _theArguments });
313     } catch (NoSuchMethodException JavaDoc nsme) {
314       StringBuffer JavaDoc aBuffer = new StringBuffer JavaDoc();
315       aBuffer.append("Method main(String[]) not found on class ")
316              .append(_theMainClass.getName()).append(" - ").append(nsme.getMessage());
317       throw new RuntimeException JavaDoc(aBuffer.toString());
318     } catch (InvocationTargetException JavaDoc ite) {
319       StringBuffer JavaDoc aBuffer = new StringBuffer JavaDoc();
320       aBuffer.append("Error calling main method on class ")
321              .append(_theMainClass.getName()).append(" - ");
322
323       if (ite.getTargetException() != null) {
324         aBuffer.append(ite.getTargetException().getMessage());
325       } else {
326         aBuffer.append(ite.getMessage());
327       }
328
329       throw new RuntimeException JavaDoc(aBuffer.toString());
330     } catch (IllegalAccessException JavaDoc iae) {
331       StringBuffer JavaDoc aBuffer = new StringBuffer JavaDoc();
332       aBuffer.append(
333         "main(String[]) method was found but is not accessible on class ")
334              .append(_theMainClass.getName()).append(" - ").append(iae.getMessage());
335       throw new RuntimeException JavaDoc(aBuffer.toString());
336     }
337   }
338 }
339
Popular Tags