KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > loom > classman > builder > SimpleLoaderResolver


1 /*
2  * Copyright (C) The Spice Group. All rights reserved.
3  *
4  * This software is published under the terms of the Spice
5  * Software License version 1.1, a copy of which has been included
6  * with this distribution in the LICENSE.txt file.
7  */

8 package org.codehaus.loom.classman.builder;
9
10 import java.io.File JavaDoc;
11 import java.io.IOException JavaDoc;
12 import java.net.JarURLConnection JavaDoc;
13 import java.net.URL JavaDoc;
14 import java.net.URLClassLoader JavaDoc;
15 import java.util.ArrayList JavaDoc;
16 import java.util.Arrays JavaDoc;
17 import java.util.HashSet JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.Set JavaDoc;
20 import java.util.jar.Manifest JavaDoc;
21
22 import org.codehaus.loom.classman.runtime.JoinClassLoader;
23 import org.codehaus.loom.extension.Extension;
24 import org.codehaus.spice.salt.i18n.ResourceManager;
25 import org.codehaus.spice.salt.i18n.Resources;
26 import org.codehaus.spice.salt.io.FileUtil;
27 import org.codehaus.spice.salt.io.PathMatcher;
28
29 /**
30  * This is a class that performs resolver that; <ul> <li>creates "Join"
31  * ClassLoaders using the {@link JoinClassLoader} class</li> <li>creates normal
32  * ClassLoaders using a {@link URLClassLoader}. It also makes sure that all
33  * dependencies of jars (as declared using the JDK1.3 "Optional Pakcages" Spec)
34  * are present in classloader.</li> <li>The locations are resolved to a single
35  * base directiory.</li> <li>The Extensions are not resolved but may be by
36  * subclasses.</li> <li>FileSets are currently unsupported and throw a {@link
37  * UnsupportedOperationException} if attempt to be constructed.</li> </ul>
38  *
39  * @author Peter Donald
40  * @version $Revision: 1.2 $ $Date: 2004/04/21 01:39:59 $
41  */

42 public class SimpleLoaderResolver
43     implements LoaderResolver
44 {
45     /** i18n utils for presenting messages. */
46     private static final Resources REZ =
47         ResourceManager.getPackageResources( SimpleLoaderResolver.class );
48
49     /** The base directory relative to which to aquire files. */
50     private File JavaDoc m_baseDirectory;
51
52     /**
53      * Create a resolver that resolves all files according to specied
54      * baseDirectory.
55      *
56      * @param baseDirectory the base directory
57      */

58     public SimpleLoaderResolver( final File JavaDoc baseDirectory )
59     {
60         setBaseDirectory( baseDirectory );
61     }
62
63     /**
64      * Retrieve a URL for specified extension.
65      *
66      * @param extension the extension
67      * @return the URL
68      * @throws Exception if unable to locate URL for extension
69      */

70     public URL JavaDoc resolveExtension( final Extension extension )
71         throws Exception JavaDoc
72     {
73         throw new UnsupportedOperationException JavaDoc();
74     }
75
76     /**
77      * Resolve a location to a particular URL.
78      *
79      * @param location the location
80      * @return the URL
81      * @throws Exception if unable to resolve location
82      */

83     public URL JavaDoc resolveURL( final String JavaDoc location )
84         throws Exception JavaDoc
85     {
86         final File JavaDoc file = getFileFor( location );
87         String JavaDoc url = file.toURL().toString();
88         if( file.isDirectory() )
89         {
90             url += "/";
91         }
92         return new URL JavaDoc( url );
93     }
94
95     /**
96      * Resolve a fileset.
97      *
98      * @param baseDirectory the base directory of fileset
99      * @param includes the list of ant-style includes
100      * @param excludes the list of ant style excludes
101      * @return the URLs contained within fileset
102      * @throws Exception if unable to resolve fileset
103      */

104     public URL JavaDoc[] resolveFileSet( final String JavaDoc baseDirectory,
105                                  final String JavaDoc[] includes,
106                                  final String JavaDoc[] excludes )
107         throws Exception JavaDoc
108     {
109         final File JavaDoc base = getFileFor( "." );
110         return resolveFileSet( base, baseDirectory, includes, excludes );
111     }
112
113     /**
114      * Create a Join ClassLoader for specified ClassLoaders. Use {@link
115      * JoinClassLoader} to implement functionality.
116      *
117      * @param classLoaders the ClassLoaders to "join"
118      * @return the joined ClassLoader
119      * @throws Exception if unable to create classloader
120      */

121     public ClassLoader JavaDoc createJoinClassLoader( final ClassLoader JavaDoc[] classLoaders )
122         throws Exception JavaDoc
123     {
124         return new JoinClassLoader( classLoaders,
125                                     ClassLoader.getSystemClassLoader() );
126     }
127
128     /**
129      * Create a ClassLoader with specified parent and containing specified URLs.
130      * This implementation just creates it using the default URLClassLoader.
131      *
132      * @param parent the parent classloader
133      * @param urls the URLs that the ClassLoader should contain
134      * @return the newly created ClassLoader
135      * @throws Exception if unable to create classloader
136      */

137     public ClassLoader JavaDoc createClassLoader( final ClassLoader JavaDoc parent,
138                                           final URL JavaDoc[] urls )
139         throws Exception JavaDoc
140     {
141         final URL JavaDoc[] classpath = determineCompleteClasspath( urls );
142         return new URLClassLoader JavaDoc( classpath, parent );
143     }
144
145     /**
146      * Retrieve the complete classpath given an input set of URLs. The complete
147      * classpath includes all URLs for extensions required by the jars
148      * (according to the "Optional Package" Spec).
149      *
150      * @param urls the urls
151      * @return the complete set of URLs for classpath
152      * @throws Exception if unable to determine complete classpath set
153      */

154     protected final URL JavaDoc[] determineCompleteClasspath( final URL JavaDoc[] urls )
155         throws Exception JavaDoc
156     {
157         final ArrayList JavaDoc classpathSet = new ArrayList JavaDoc();
158
159         //Add all supplied URLS to classpath
160
for( int i = 0; i < urls.length; i++ )
161         {
162             final URL JavaDoc url = urls[ i ];
163             classpathSet.add( url );
164         }
165
166         //Add all the optional packages that are declared as
167
// dependencies of class path elements
168
final File JavaDoc[] files = getOptionalPackagesFor( urls );
169         for( int i = 0; i < files.length; i++ )
170         {
171             final File JavaDoc file = files[ i ];
172             classpathSet.add( file.toURL() );
173         }
174
175         //Define final classpath with all dependencies added
176
return (URL JavaDoc[])classpathSet.toArray( new URL JavaDoc[ classpathSet.size() ] );
177     }
178
179     /**
180      * Utility class to retrieve a file object for specified location.
181      *
182      * @param location which to get file for.
183      * @return the file for specified location
184      */

185     protected File JavaDoc getFileFor( final String JavaDoc location )
186         throws IOException JavaDoc
187     {
188         File JavaDoc base = getBaseDirectory();
189         if( null == base )
190         {
191             base = new File JavaDoc( "." );
192         }
193
194         return new File JavaDoc( base, location ).getCanonicalFile();
195     }
196
197     /**
198      * Return the base directory against which to resolve relative files.
199      *
200      * @return the base directory against which to resolve relative files.
201      */

202     protected File JavaDoc getBaseDirectory()
203     {
204         return m_baseDirectory;
205     }
206
207     /**
208      * Set the base directory.
209      *
210      * @param baseDirectory the base directory.
211      */

212     protected void setBaseDirectory( File JavaDoc baseDirectory )
213     {
214         m_baseDirectory = baseDirectory;
215     }
216
217     /**
218      * Retrieve the files for the optional packages required by the jars in
219      * ClassPath.
220      *
221      * @param classPath the Classpath array
222      * @return the files that need to be added to ClassLoader
223      */

224     protected final File JavaDoc[] getOptionalPackagesFor( final URL JavaDoc[] classPath )
225         throws Exception JavaDoc
226     {
227         final Manifest JavaDoc[] manifests = getManifests( classPath );
228         final Extension[] available = getAvailable( manifests );
229         final Extension[] required = Extension.getRequired( manifests );
230
231         if( isDebugEnabled() )
232         {
233             final String JavaDoc message1 =
234                 REZ.format( "available-extensions",
235                             Arrays.asList( available ) );
236             debug( message1 );
237             final String JavaDoc message2 =
238                 REZ.format( "required-extensions",
239                             Arrays.asList( required ) );
240             debug( message2 );
241         }
242
243         if( 0 == required.length )
244         {
245             return new File JavaDoc[ 0 ];
246         }
247
248         final Set JavaDoc dependencies = new HashSet JavaDoc();
249         final Set JavaDoc unsatisfied = new HashSet JavaDoc();
250
251         scanDependencies( required,
252                           available,
253                           dependencies,
254                           unsatisfied );
255
256         final int size = unsatisfied.size();
257         if( 0 != size )
258         {
259             final Iterator JavaDoc iterator = unsatisfied.iterator();
260             while( iterator.hasNext() )
261             {
262                 final Extension extension = (Extension)iterator.next();
263                 final Object JavaDoc[] params = new Object JavaDoc[]
264                 {
265                     extension.getExtensionName(),
266                     extension.getSpecificationVendor(),
267                     extension.getSpecificationVersion(),
268                     extension.getImplementationVendor(),
269                     extension.getImplementationVendorID(),
270                     extension.getImplementationVersion(),
271                     extension.getImplementationURL()
272                 };
273                 final String JavaDoc message = REZ.format( "missing.extension",
274                                                    params );
275                 warn( message );
276             }
277
278             final String JavaDoc message =
279                 REZ.format( "unsatisfied.extensions", new Integer JavaDoc( size ) );
280             throw new Exception JavaDoc( message );
281         }
282
283         if( isDebugEnabled() )
284         {
285             final String JavaDoc message =
286                 REZ.format( "optional-packages-added", dependencies );
287             debug( message );
288         }
289
290         return (File JavaDoc[])dependencies.toArray( new File JavaDoc[ dependencies.size() ] );
291     }
292
293
294     /**
295      * Retrieve the set of <code>Extension</code> objects that are available by
296      * the specified Manifest objects. If there are no such optional packages, a
297      * zero-length list is returned.
298      *
299      * @param manifests the manifests to scan
300      * @return the extensions
301      */

302     private Extension[] getAvailable( final Manifest JavaDoc[] manifests )
303     {
304         final ArrayList JavaDoc set = new ArrayList JavaDoc();
305         for( int i = 0; i < manifests.length; i++ )
306         {
307             try
308             {
309                 final Extension[] extensions = Extension.getAvailable( manifests[ i ] );
310                 for( int j = 0; j < extensions.length; j++ )
311                 {
312                     set.add( extensions[ j ] );
313                 }
314             }
315             catch( final Exception JavaDoc e )
316             {
317                 final String JavaDoc message =
318                     REZ.format( "malformed.extension", manifests[ i ] );
319                 warn( message, e );
320             }
321         }
322         return (Extension[])set.toArray( new Extension[ set.size() ] );
323     }
324
325     /**
326      * Attempt to locate a set of dependencies that transitively satisfy all
327      * required extensions.
328      *
329      * @param required the required extensions
330      * @param available the extensions already available
331      * @param dependencies the set of dependencies collected by this method
332      * @param unsatisfied this method should place any unsatisfied depenencies
333      * into this set
334      */

335     protected void scanDependencies( final Extension[] required,
336                                      final Extension[] available,
337                                      final Set JavaDoc dependencies,
338                                      final Set JavaDoc unsatisfied )
339     {
340         throw new UnsupportedOperationException JavaDoc();
341     }
342
343     /**
344      * write out a warning message. Subclasses may overide this method to
345      * redirect logging as appropriate.
346      *
347      * @param message the warning message
348      */

349     protected void warn( final String JavaDoc message )
350     {
351     }
352
353     /**
354      * write out a warning message. Subclasses may overide this method to
355      * redirect logging as appropriate.
356      *
357      * @param message the warning message
358      * @param t the throwable
359      */

360     protected void warn( final String JavaDoc message, final Throwable JavaDoc t )
361     {
362     }
363
364     /**
365      * Determine if debug messages are turned on. Subclasses should overide this
366      * method.
367      *
368      * @return true if debugging enabled.
369      */

370     protected boolean isDebugEnabled()
371     {
372         return false;
373     }
374
375     /**
376      * write out a debug message. Subclasses may overide this method to redirect
377      * logging as appropriate.
378      *
379      * @param message the debug message
380      */

381     protected void debug( final String JavaDoc message )
382     {
383     }
384
385     /**
386      * Retrieve all the Manifests from the specified Classlpath.
387      *
388      * @param classPath the classpath
389      * @return the set of manifests on the classpath
390      * @throws Exception if there is an error reading manifests from files on
391      * classpath
392      */

393     private Manifest JavaDoc[] getManifests( final URL JavaDoc[] classPath )
394         throws Exception JavaDoc
395     {
396         final ArrayList JavaDoc manifests = new ArrayList JavaDoc();
397
398         for( int i = 0; i < classPath.length; i++ )
399         {
400             final URL JavaDoc element = classPath[ i ];
401             if( element.getFile().endsWith( ".jar" ) )
402             {
403                 try
404                 {
405                     final URL JavaDoc url = new URL JavaDoc( "jar:" + element + "!/" );
406                     final JarURLConnection JavaDoc connection =
407                         (JarURLConnection JavaDoc)url.openConnection();
408                     final Manifest JavaDoc manifest = connection.getManifest();
409                     if( null != manifest )
410                     {
411                         manifests.add( manifest );
412                     }
413                 }
414                 catch( final IOException JavaDoc ioe )
415                 {
416                     final String JavaDoc message =
417                         REZ.format( "bad-classpath-entry", element );
418                     throw new Exception JavaDoc( message );
419                 }
420             }
421         }
422
423         return (Manifest JavaDoc[])manifests.toArray( new Manifest JavaDoc[ 0 ] );
424     }
425
426     /**
427      * Resolve a fileset in a particular hierarchy.
428      *
429      * @param base the file hierarchy to use
430      * @param baseDirectory the base directory (relative to base)
431      * @param includes the ant-style include patterns
432      * @param excludes the ant-style exclude patterns
433      * @return the resolved URLs for fileset
434      */

435     protected final URL JavaDoc[] resolveFileSet( final File JavaDoc base,
436                                           final String JavaDoc baseDirectory,
437                                           final String JavaDoc[] includes,
438                                           final String JavaDoc[] excludes )
439     {
440         //woefully inefficient .. but then again - no need
441
//for efficency here
442
final String JavaDoc newBaseDirectory = FileUtil.normalize( baseDirectory );
443         final String JavaDoc[] newIncludes = prefixPatterns( newBaseDirectory,
444                                                      includes );
445         final String JavaDoc[] newExcludes = prefixPatterns( newBaseDirectory,
446                                                      excludes );
447         final PathMatcher matcher = new PathMatcher( newIncludes,
448                                                      newExcludes );
449         final File JavaDoc[] files = FileUtil.resolveFileSet( base, matcher );
450         try
451         {
452             return FileUtil.toURLs( files );
453         }
454         catch( IOException JavaDoc ioe )
455         {
456             throw new IllegalArgumentException JavaDoc( ioe.getMessage() );
457         }
458     }
459
460     /**
461      * Return a new array with specified prefix added to start of every element
462      * in supplied array.
463      *
464      * @param prefix the prefix
465      * @param patterns the source array
466      * @return a new array with all elements having prefix added
467      */

468     private String JavaDoc[] prefixPatterns( final String JavaDoc prefix,
469                                      final String JavaDoc[] patterns )
470     {
471         if( 0 == prefix.length() || ".".equals( prefix ) )
472         {
473             return patterns;
474         }
475
476         final String JavaDoc[] newPatterns = new String JavaDoc[ patterns.length ];
477         for( int i = 0; i < newPatterns.length; i++ )
478         {
479             newPatterns[ i ] = prefix + "/" + patterns[ i ];
480         }
481         return newPatterns;
482     }
483 }
Popular Tags