1
2 package com.werken.forehead;
3
4 /*
5 $Id: Forehead.java,v 1.1.1.1 2002/06/19 13:50:59 werken Exp $
6
7 Copyright 2001 (C) The Werken Company. All Rights Reserved.
8
9 Redistribution and use of this software and associated documentation
10 ("Software"), with or without modification, are permitted provided
11 that the following conditions are met:
12
13 1. Redistributions of source code must retain copyright
14 statements and notices. Redistributions must also contain a
15 copy of this document.
16
17 2. Redistributions in binary form must reproduce the
18 above copyright notice, this list of conditions and the
19 following disclaimer in the documentation and/or other
20 materials provided with the distribution.
21
22 3. The name "Forehead" must not be used to endorse or promote
23 products derived from this Software without prior written
24 permission of The Werken Company. For written permission,
25 please contact bob@werken.com.
26
27 4. Products derived from this Software may not be called "Forehead"
28 nor may "Forehead" appear in their names without prior written
29 permission of The Werken Company. Forehead is a registered
30 trademark of The Werken Company.
31
32 5. Due credit should be given to the Forehead Project
33 (http://drools.org/).
34
35 THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS
36 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
37 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
38 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
39 THE WERKEN COMPANY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
40 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
41 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
42 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
44 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
45 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
46 OF THE POSSIBILITY OF SUCH DAMAGE.
47
48 */
49
50 import java.io.File;
51 import java.io.FilenameFilter;
52 import java.io.FileInputStream;
53 import java.io.InputStream;
54 import java.io.InputStreamReader;
55 import java.io.BufferedReader;
56 import java.io.Reader;
57 import java.io.IOException;
58 import java.lang.reflect.Method;
59 import java.lang.reflect.Modifier;
60 import java.lang.reflect.InvocationTargetException;
61 import java.net.URL;
62 import java.net.MalformedURLException;
63 import java.util.HashMap;
64 import java.util.Map;
65 import java.util.Properties;
66
67 /*** ClassLoader configurator and application launcher.
68 *
69 * <p>
70 * This is the main command-line entry-point into the
71 * <code>forehead</code> framework. Please see
72 * the <code>forehead</code> documentation for usage
73 * instructions.
74 * </p>
75 *
76 * @author <a href="mailto:bob@eng.werken.com">bob mcwhirter</a>
77 */
78 public class Forehead
79 {
80 // ------------------------------------------------------------
81 // Instance members
82 // ------------------------------------------------------------
83
84 /*** ClassLoaders indexed by name. */
85 private Map classLoaders;
86
87 /*** Root unnamed loader. */
88 private ForeheadClassLoader rootLoader;
89
90 /*** The loader to use for entry-point lookup. */
91 private ForeheadClassLoader entryLoader;
92
93 /*** The entry-point class. */
94 private Class entryClass;
95
96 /*** The entry-point method. */
97 private Method entryMethod;
98
99 // ------------------------------------------------------------
100 // Constructors
101 // ------------------------------------------------------------
102
103 /*** Construct.
104 */
105 public Forehead()
106 {
107 this.classLoaders = new HashMap();
108 }
109
110 // ------------------------------------------------------------
111 // Instance methods
112 // ------------------------------------------------------------
113
114 /*** Configure from an <code>InputStream</code>.
115 *
116 * @param in <code>InputStream</code> carrying configuration information.
117 *
118 * @throws ForeheadException If there is an error during configuration.
119 * @throws IOException If there is an error reading configuration information.
120 * @throws ClassNotFoundException If unable to locate entry-point class.
121 */
122 public void config(InputStream in) throws ForeheadException, IOException, ClassNotFoundException
123 {
124 config( new BufferedReader( new InputStreamReader( in ) ) );
125 }
126
127 /*** Configure from an <code>Reader</code>.
128 *
129 * @param in <code>Reader</code> carrying configuration information.
130 *
131 * @throws ForeheadException If there is an error during configuration.
132 * @throws IOException If there is an error reading configuration information.
133 * @throws ClassNotFoundException If unable to locate entry-point class.
134 */
135 public void config(Reader in) throws ForeheadException, IOException, ClassNotFoundException
136 {
137 if ( in instanceof BufferedReader )
138 {
139 config( in );
140 }
141 else
142 {
143 config( new BufferedReader( in ) );
144 }
145 }
146
147 /*** Configure from an <code>BufferedReader</code>.
148 *
149 * @param in <code>BufferedReader</code> carrying configuration information.
150 *
151 * @throws ForeheadException If there is an error during configuration.
152 * @throws IOException If there is an error reading configuration information.
153 * @throws ClassNotFoundException If unable to locate entry-point class.
154 */
155 public void config(BufferedReader in) throws ForeheadException, IOException, ClassNotFoundException
156 {
157 this.rootLoader = new ForeheadClassLoader( getClass().getClassLoader(),
158 "$forehead-root$");
159
160 String line = null;
161
162 ForeheadClassLoader currentLoader = this.rootLoader;
163
164 Properties props = new Properties();
165
166 String entryLine = null;
167
168 while ( ( line = in.readLine() ) != null )
169 {
170 line = line.trim();
171
172 if ( "".equals( line ) )
173 {
174 continue;
175 }
176
177 if ( line.startsWith( "#" ) )
178 {
179 continue;
180 }
181
182 if ( line.startsWith( "+" ) )
183 {
184 String propName = line.substring( 1 );
185 String propValue = System.getProperty( propName );
186
187 if ( propValue == null )
188 {
189 throw new NoSuchPropertyException( propName );
190 }
191
192 props.setProperty( propName,
193 propValue );
194
195 continue;
196 }
197
198 if ( line.startsWith( "=" ) )
199 {
200 entryLine = line;
201 continue;
202 }
203
204 ForeheadClassLoader parentLoader = null;
205
206 if ( line.startsWith( "[" )
207 &&
208 line.endsWith( "]" ) )
209 {
210 String loaderName = line.substring( 1,
211 line.length() - 1 );
212
213 int dotLoc = loaderName.lastIndexOf( "." );
214
215 if ( dotLoc > 0 )
216 {
217 String parentName = loaderName.substring( 0,
218 dotLoc );
219
220 parentLoader = getClassLoader( parentName );
221
222 if ( parentLoader == null )
223 {
224 throw new NoSuchClassLoaderException( parentName );
225 }
226 }
227 else
228 {
229 parentLoader = this.rootLoader;
230 }
231
232 currentLoader = createClassLoader( parentLoader, loaderName );
233 }
234 else
235 {
236 String resolvedLine = resolveProperties( line, props );
237
238 load( resolvedLine,
239 currentLoader );
240 }
241 }
242
243
244 if ( entryLine == null )
245 {
246 throw new NoEntryDescriptorException();
247 }
248
249 setupEntry( entryLine );
250 }
251
252 /*** Setup the entry-point.
253 *
254 * @param line The entry-point configuration line.
255 *
256 * @throws MalformedEntryDescriptorException If the entry-point descriptor is malformed.
257 * @throws NoSuchClassLoaderException If the entry-point descriptor references
258 * an unknown ClassLoader.
259 * @throws ClassNotFoundException If unable to locate the entry-point class.
260 */
261 protected void setupEntry(String line)
262 throws MalformedEntryDescriptorException, NoSuchClassLoaderException, ClassNotFoundException
263 {
264 int leftBrackLoc = line.indexOf( "[" );
265
266 if ( leftBrackLoc < 0 )
267 {
268 throw new MalformedEntryDescriptorException( line );
269 }
270
271 int rightBrackLoc = line.indexOf( "]",
272 leftBrackLoc + 1 );
273
274 if ( rightBrackLoc < 0 )
275 {
276 throw new MalformedEntryDescriptorException( line );
277 }
278
279 String loaderName = line.substring( leftBrackLoc + 1,
280 rightBrackLoc );
281
282 String className = line.substring( rightBrackLoc + 1 ).trim();
283
284 this.entryLoader = getClassLoader( loaderName );
285
286 if ( this.entryLoader == null )
287 {
288 throw new NoSuchClassLoaderException( loaderName );
289 }
290
291 this.entryClass = Class.forName( className,
292 true,
293 this.entryLoader );
294
295 }
296
297 /*** Load a glob, file, or URL into the specified classloader.
298 *
299 * @param line The path configuration line.
300 * @param loader The loader to populate
301 *
302 * @throws MalformedURLException If the line does not represent
303 * a valid path element.
304 */
305 protected void load(String line,
306 ForeheadClassLoader loader) throws MalformedURLException
307 {
308 if ( line.indexOf( "*" ) >= 0 )
309 {
310 loadGlob( line,
311 loader );
312 }
313 else
314 {
315 loadFileOrUrl( line,
316 loader );
317 }
318 }
319
320 /*** Load a glob into the specified classloader.
321 *
322 * @param line The path configuration line.
323 * @param loader The loader to populate
324 *
325 * @throws MalformedURLException If the line does not represent
326 * a valid path element.
327 */
328 protected void loadGlob(String line,
329 ForeheadClassLoader loader) throws MalformedURLException
330 {
331 File globFile = new File( line );
332
333 File dir = globFile.getParentFile();
334
335 String localName = globFile.getName();
336
337 int starLoc = localName.indexOf( "*" );
338
339 final String prefix = localName.substring( 0,
340 starLoc );
341
342 final String suffix = localName.substring( starLoc + 1 );
343
344 File[] matches = dir.listFiles(
345 new FilenameFilter() {
346 public boolean accept(File dir, String name)
347 {
348 if ( prefix != null
349 &&
350 !name.startsWith( prefix ) )
351 {
352 return false;
353 }
354
355 if ( suffix != null
356 &&
357 !name.endsWith( suffix ) )
358 {
359 return false;
360 }
361
362 return true;
363 }
364 }
365 );
366
367 for ( int i = 0 ; i < matches.length ; ++i )
368 {
369 loader.addURL( matches[i].toURL() );
370 }
371 }
372
373 /*** Load a file or URL into the specified classloader.
374 *
375 * @param line The path configuration line.
376 * @param loader The loader to populate
377 *
378 * @throws MalformedURLException If the line does not represent
379 * a valid path element.
380 */
381 protected void loadFileOrUrl(String line,
382 ForeheadClassLoader loader) throws MalformedURLException
383 {
384 URL url = null;
385
386 File file = new File( line );
387
388 if ( file.exists() )
389 {
390 url = file.toURL();
391 }
392 else
393 {
394 url = new URL( line );
395 }
396
397 loader.addURL( url );
398 }
399
400 /*** Create a new ClassLoader given a parent and a name.
401 *
402 * @param parent The parent of the ClassLoader to create.
403 * @param name The name of the ClassLoader to create.
404 *
405 * @return A newly configured <code>ClassLoader</code>.
406 */
407 protected ForeheadClassLoader createClassLoader(ForeheadClassLoader parent,
408 String name)
409 {
410 ForeheadClassLoader loader = new ForeheadClassLoader( parent,
411 name );
412
413 this.classLoaders.put( name,
414 loader );
415
416 return loader;
417 }
418
419 /*** Retrieve a ClassLoader by name.
420 *
421 * @param name The name of the ClassLoader to retrieve.
422 *
423 * @return The associated ClassLoader, or <code>null</code>
424 * if none.
425 */
426 public ForeheadClassLoader getClassLoader(String name)
427 {
428 return (ForeheadClassLoader) this.classLoaders.get( name );
429 }
430
431 /*** Resolve imported properties.
432 *
433 * @param input The string input to resolve properties.
434 * @param props Properties to resolve against.
435 *
436 * @return The string with properties resolved.
437 */
438 public String resolveProperties(String input,
439 Properties props)
440 {
441 String output = "";
442
443 int cur = 0;
444
445 int prefixLoc = 0;
446 int suffixLoc = 0;
447
448 while ( cur < input.length() )
449 {
450 prefixLoc = input.indexOf( "${",
451 cur );
452
453 if ( prefixLoc < 0 )
454 {
455 //output = output + input.substring( cur );
456 break;
457 }
458
459 suffixLoc = input.indexOf( "}",
460 prefixLoc );
461
462 String propName = input.substring( prefixLoc + 2,
463 suffixLoc );
464
465 output = output + input.substring( cur, prefixLoc );
466
467 output = output + props.getProperty( propName );
468
469 cur = suffixLoc + 1;
470 }
471
472 output = output + input.substring( cur );
473
474 return output;
475 }
476
477 /*** Launch the wrapped application.
478 *
479 * @param args Command-line args to pass to wrapped application.
480 *
481 * @throws NoSuchEntryMethodException If unable to find the entry method on the class.
482 * @throws IllegalAccessException If an error occurs while attempting to invoke the
483 * entry-point method.
484 * @throws InvocationTargetException If an error occurs while attempting to invoke the
485 * entry-point method.
486 */
487 public void run(String[] args) throws NoSuchEntryMethodException, IllegalAccessException, InvocationTargetException
488 {
489 Method[] methods = this.entryClass.getMethods();
490
491 for ( int i = 0 ; i < methods.length ; ++i )
492 {
493 if ( !"main".equals( methods[i].getName() ) )
494 {
495 continue;
496 }
497
498 int modifiers = methods[i].getModifiers();
499
500 if ( !( Modifier.isStatic( modifiers )
501 &&
502 Modifier.isPublic( modifiers ) ) )
503 {
504 continue;
505 }
506
507 if ( methods[i].getReturnType() != Void.TYPE )
508 {
509 continue;
510 }
511
512 Class[] paramTypes = methods[i].getParameterTypes();
513
514 if ( paramTypes.length != 1 )
515 {
516 continue;
517 }
518
519 if ( paramTypes[0] != String[].class )
520 {
521 continue;
522 }
523
524 this.entryMethod = methods[i];
525 break;
526 }
527
528 if ( this.entryMethod == null )
529 {
530 throw new NoSuchEntryMethodException( this.entryClass,
531 "public static void main(String[] args)" );
532 }
533
534 Thread.currentThread().setContextClassLoader( this.entryLoader );
535
536 this.entryMethod.invoke( this.entryClass,
537 new Object[] { args } );
538 }
539
540 // ------------------------------------------------------------
541 // Class methods
542 // ------------------------------------------------------------
543
544 /*** Main.
545 *
546 * @param args Command-line arguments to pass to the wrapped
547 * application.
548 */
549 public static void main(String[] args)
550 {
551 String confFileName = System.getProperty( "forehead.conf.file" );
552
553 File confFile = new File( confFileName );
554
555 Forehead forehead = new Forehead();
556
557 try
558 {
559 forehead.config( new FileInputStream( confFile ) );
560
561 forehead.run( args );
562 }
563 catch (NoSuchEntryMethodException e)
564 {
565 System.err.println( "No method on class " + e.getEntryClass() + " matching: " + e.getEntryMethodDescriptor() );
566 e.printStackTrace();
567 }
568 catch (ForeheadException e)
569 {
570 System.err.println( "Error during configuration: " + e.getLocalizedMessage() );
571 e.printStackTrace();
572 }
573 catch (MalformedURLException e)
574 {
575 e.printStackTrace();
576 }
577 catch (IOException e)
578 {
579 System.err.println( "Error reading configuration: " + e.getLocalizedMessage() );
580 e.printStackTrace();
581 }
582 catch (ClassNotFoundException e)
583 {
584 System.err.println( e.getLocalizedMessage() );
585 e.printStackTrace();
586 }
587 catch (Exception e)
588 {
589 e.printStackTrace();
590 }
591 }
592 }
This page was automatically generated by Maven