1 package org.codehaus.classworlds;
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 import java.io.BufferedReader;
50 import java.io.File;
51 import java.io.FileNotFoundException;
52 import java.io.FilenameFilter;
53 import java.io.IOException;
54 import java.io.InputStream;
55 import java.io.InputStreamReader;
56 import java.io.FileInputStream;
57 import java.net.MalformedURLException;
58 import java.net.URL;
59 import java.util.ArrayList;
60 import java.util.Collections;
61 import java.util.Comparator;
62 import java.util.HashMap;
63 import java.util.Iterator;
64 import java.util.List;
65 import java.util.Map;
66 import java.util.Properties;
67
68 /***
69 * <code>Launcher</code> configurator.
70 *
71 * @author <a href="mailto:bob@eng.werken.com">bob mcwhirter</a>
72 * @author <a href="mailto:jason@zenplex.com">Jason van Zyl</a>
73 * @version $Id: Configurator.java,v 1.3 2004/09/07 13:17:42 brett Exp $
74 */
75 public class Configurator
76 {
77 public static final String MAIN_PREFIX = "main is";
78
79 public static final String SET_PREFIX = "set";
80
81 public static final String IMPORT_PREFIX = "import";
82
83 public static final String LOAD_PREFIX = "load";
84
85 /*** Optionally spec prefix. */
86 public static final String OPTIONALLY_PREFIX = "optionally";
87
88 /*** The launcher to configure. */
89 private Launcher launcher;
90
91 private ClassWorld world;
92
93 /*** Processed Realms. */
94 private Map configuredRealms;
95
96 /*** Construct.
97 *
98 * @param launcher The launcher to configure.
99 */
100 public Configurator( Launcher launcher )
101 {
102 this.launcher = launcher;
103
104 configuredRealms = new HashMap();
105 }
106
107 /*** Construct.
108 *
109 * @param classWorld The classWorld to configure.
110 */
111 public Configurator( ClassWorld world )
112 {
113 setClassWorld( world );
114 }
115
116 /*** set world.
117 * this setter is provided so you can use the same configurator to configure several "worlds"
118 *
119 * @param classWorld The classWorld to configure.
120 */
121 public void setClassWorld( ClassWorld world )
122 {
123 this.world = world;
124
125 configuredRealms = new HashMap();
126 }
127
128 /***
129 * Configure from a file.
130 *
131 * @param is The config input stream
132 * @throws IOException If an error occurs reading the config file.
133 * @throws MalformedURLException If the config file contains invalid URLs.
134 * @throws ConfigurationException If the config file is corrupt.
135 * @throws DuplicateRealmException If the config file defines two realms with the same id.
136 * @throws NoSuchRealmException If the config file defines a main entry point in
137 * a non-existent realm.
138 */
139 public void configure( InputStream is )
140 throws IOException, MalformedURLException, ConfigurationException, DuplicateRealmException, NoSuchRealmException
141 {
142 BufferedReader reader = new BufferedReader( new InputStreamReader( is ) );
143
144 if ( world == null )
145 {
146 world = new ClassWorld();
147 }
148
149 ClassRealm curRealm = null;
150
151 String line = null;
152
153 int lineNo = 0;
154
155 boolean mainSet = false;
156
157 while ( true )
158 {
159 line = reader.readLine();
160
161 if ( line == null )
162 {
163 break;
164 }
165
166 ++lineNo;
167 line = line.trim();
168
169 if ( canIgnore( line ) )
170 {
171 continue;
172 }
173
174 if ( line.startsWith( MAIN_PREFIX ) )
175 {
176 if ( mainSet )
177 {
178 throw new ConfigurationException( "Duplicate main configuration", lineNo, line );
179 }
180
181 String conf = line.substring( MAIN_PREFIX.length() ).trim();
182
183 int fromLoc = conf.indexOf( "from" );
184
185 if ( fromLoc < 0 )
186 {
187 throw new ConfigurationException( "Missing from clause", lineNo, line );
188 }
189
190 String mainClassName = conf.substring( 0, fromLoc ).trim();
191
192 String mainRealmName = conf.substring( fromLoc + 4 ).trim();
193
194 if ( this.launcher != null )
195 {
196 this.launcher.setAppMain( mainClassName, mainRealmName );
197 }
198
199 mainSet = true;
200 }
201 else if ( line.startsWith( SET_PREFIX ) )
202 {
203 String conf = line.substring( SET_PREFIX.length() ).trim();
204
205 int usingLoc = conf.indexOf( " using" ) + 1;
206
207 String property = null;
208 String propertiesFileName = null;
209 if ( usingLoc > 0 )
210 {
211 property = conf.substring( 0, usingLoc ).trim();
212
213 propertiesFileName = filter( conf.substring( usingLoc + 5 ).trim() );
214
215 conf = propertiesFileName;
216 }
217
218 String defaultValue = null;
219
220 int defaultLoc = conf.indexOf( " default" ) + 1;
221
222 if ( defaultLoc > 0 )
223 {
224 defaultValue = conf.substring( defaultLoc + 7 ).trim();
225
226 if ( property == null )
227 {
228 property = conf.substring( 0, defaultLoc ).trim();
229 }
230 else
231 {
232 propertiesFileName = conf.substring( 0, defaultLoc ).trim();
233 }
234 }
235
236 String value = System.getProperty( property );
237
238 if ( value != null )
239 {
240 continue;
241 }
242
243 if ( propertiesFileName != null )
244 {
245 File propertiesFile = new File( propertiesFileName );
246
247 if ( propertiesFile.exists() )
248 {
249 Properties properties = new Properties();
250
251 try
252 {
253 properties.load( new FileInputStream( propertiesFileName ) );
254
255 value = properties.getProperty( property );
256 }
257 catch ( Exception e )
258 {
259
260 }
261 }
262 }
263
264 if ( value == null && defaultValue != null )
265 {
266 value = defaultValue;
267 }
268
269 if ( value != null )
270 {
271 value = filter( value );
272 System.setProperty( property, value );
273 }
274 }
275 else if ( line.startsWith( "[" ) )
276 {
277 int rbrack = line.indexOf( "]" );
278
279 if ( rbrack < 0 )
280 {
281 throw new ConfigurationException( "Invalid realm specifier", lineNo, line );
282 }
283
284 String realmName = line.substring( 1, rbrack );
285
286 curRealm = world.newRealm( realmName );
287
288
289 configuredRealms.put( realmName, curRealm );
290 }
291 else if ( line.startsWith( IMPORT_PREFIX ) )
292 {
293 if ( curRealm == null )
294 {
295 throw new ConfigurationException( "Unhandled import", lineNo, line );
296 }
297
298 String conf = line.substring( IMPORT_PREFIX.length() ).trim();
299
300 int fromLoc = conf.indexOf( "from" );
301
302 if ( fromLoc < 0 )
303 {
304 throw new ConfigurationException( "Missing from clause", lineNo, line );
305 }
306
307 String importSpec = conf.substring( 0, fromLoc ).trim();
308
309 String relamName = conf.substring( fromLoc + 4 ).trim();
310
311 curRealm.importFrom( relamName, importSpec );
312
313 }
314 else if ( line.startsWith( LOAD_PREFIX ) )
315 {
316 String constituent = line.substring( LOAD_PREFIX.length() ).trim();
317
318 constituent = filter( constituent );
319
320 if ( constituent.indexOf( "*" ) >= 0 )
321 {
322 loadGlob( constituent, curRealm );
323 }
324 else
325 {
326 File file = new File( constituent );
327
328 if ( file.exists() )
329 {
330 curRealm.addConstituent( file.toURL() );
331 }
332 else
333 {
334 try
335 {
336 curRealm.addConstituent( new URL( constituent ) );
337 }
338 catch ( MalformedURLException e )
339 {
340 throw new FileNotFoundException( constituent );
341 }
342 }
343 }
344 }
345 else if ( line.startsWith( OPTIONALLY_PREFIX ) )
346 {
347 String constituent = line.substring( OPTIONALLY_PREFIX.length() ).trim();
348
349 constituent = filter( constituent );
350
351 if ( constituent.indexOf( "*" ) >= 0 )
352 {
353 loadGlob( constituent, curRealm, true );
354 }
355 else
356 {
357 File file = new File( constituent );
358
359 if ( file.exists() )
360 {
361 curRealm.addConstituent( file.toURL() );
362 }
363 else
364 {
365 try
366 {
367 curRealm.addConstituent( new URL( constituent ) );
368 }
369 catch (MalformedURLException e)
370 {
371
372 }
373 }
374 }
375 }
376 else
377 {
378 throw new ConfigurationException( "Unhandled configuration", lineNo, line );
379 }
380 }
381
382
383 associateRealms();
384
385 if ( this.launcher != null ) this.launcher.setWorld( world );
386
387 reader.close();
388 }
389
390 /***
391 * Associate parent realms with their children.
392 */
393 protected void associateRealms()
394 {
395 List sortRealmNames = new ArrayList( configuredRealms.keySet() );
396
397
398 Comparator comparator = new Comparator()
399 {
400 public int compare( Object o1, Object o2 )
401 {
402 String g1 = (String) o1;
403 String g2 = (String) o2;
404 return g1.compareTo( g2 );
405 }
406 };
407
408 Collections.sort( sortRealmNames, comparator );
409
410
411
412
413
414
415
416
417
418
419
420 for ( Iterator i = sortRealmNames.iterator(); i.hasNext(); )
421 {
422 String realmName = (String) i.next();
423
424 int j = realmName.lastIndexOf( '.' );
425
426 if ( j > 0 )
427 {
428 String parentRealmName = realmName.substring( 0, j );
429
430 ClassRealm parentRealm = (ClassRealm) configuredRealms.get( parentRealmName );
431
432 if ( parentRealm != null )
433 {
434 ClassRealm realm = (ClassRealm) configuredRealms.get( realmName );
435
436 realm.setParent( parentRealm );
437 }
438 }
439 }
440 }
441
442 /***
443 * Load a glob into the specified classloader.
444 *
445 * @param line The path configuration line.
446 * @param realm The realm to populate
447 * @throws MalformedURLException If the line does not represent
448 * a valid path element.
449 * @throws FileNotFoundException If the line does not represent
450 * a valid path element in the filesystem.
451 */
452 protected void loadGlob( String line, ClassRealm realm )
453 throws MalformedURLException, FileNotFoundException
454 {
455 loadGlob( line, realm, false );
456 }
457
458 /***
459 * Load a glob into the specified classloader.
460 *
461 * @param line The path configuration line.
462 * @param realm The realm to populate
463 * @param optionally Whether the path is optional or required
464 * @throws MalformedURLException If the line does not represent
465 * a valid path element.
466 * @throws FileNotFoundException If the line does not represent
467 * a valid path element in the filesystem.
468 */
469 protected void loadGlob( String line, ClassRealm realm, boolean optionally )
470 throws MalformedURLException, FileNotFoundException
471 {
472 File globFile = new File( line );
473
474 File dir = globFile.getParentFile();
475 if ( ! dir.exists() )
476 {
477 if ( optionally )
478 {
479 return;
480 }
481 else
482 {
483 throw new FileNotFoundException( dir.toString() );
484 }
485 }
486
487 String localName = globFile.getName();
488
489 int starLoc = localName.indexOf( "*" );
490
491 final String prefix = localName.substring( 0, starLoc );
492
493 final String suffix = localName.substring( starLoc + 1 );
494
495 File[] matches = dir.listFiles( new FilenameFilter()
496 {
497 public boolean accept( File dir, String name )
498 {
499 if ( !name.startsWith( prefix ) )
500 {
501 return false;
502 }
503
504 if ( !name.endsWith( suffix ) )
505 {
506 return false;
507 }
508
509 return true;
510 }
511 } );
512
513 for ( int i = 0; i < matches.length; ++i )
514 {
515 realm.addConstituent( matches[i].toURL() );
516 }
517 }
518
519 /***
520 * Filter a string for system properties.
521 *
522 * @param text The text to filter.
523 * @return The filtered text.
524 * @throws ConfigurationException If the property does not
525 * exist or if there is a syntax error.
526 */
527 protected String filter( String text )
528 throws ConfigurationException
529 {
530 String result = "";
531
532 int cur = 0;
533 int textLen = text.length();
534
535 int propStart = -1;
536 int propStop = -1;
537
538 String propName = null;
539 String propValue = null;
540
541 while ( cur < textLen )
542 {
543 propStart = text.indexOf( "${", cur );
544
545 if ( propStart < 0 )
546 {
547 break;
548 }
549
550 result += text.substring( cur, propStart );
551
552 propStop = text.indexOf( "}", propStart );
553
554 if ( propStop < 0 )
555 {
556 throw new ConfigurationException( "Unterminated property: " + text.substring( propStart ) );
557 }
558
559 propName = text.substring( propStart + 2, propStop );
560
561 propValue = System.getProperty( propName );
562
563 if ( propValue == null )
564 {
565 throw new ConfigurationException( "No such property: " + propName );
566 }
567 result += propValue;
568
569 cur = propStop + 1;
570 }
571
572 result += text.substring( cur );
573
574 return result;
575 }
576
577 /***
578 * Determine if a line can be ignored because it is
579 * a comment or simply blank.
580 *
581 * @param line The line to test.
582 * @return <code>true</code> if the line is ignorable,
583 * otherwise <code>false</code>.
584 */
585 private boolean canIgnore( String line )
586 {
587 return ( line.length() == 0
588 ||
589 line.startsWith( "#" ) );
590 }
591 }
592