KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > avalon > extension > manager > impl > DefaultExtensionManager


1 /*
2  * Copyright 2004 Apache Software Foundation
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12  * implied.
13  *
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.avalon.extension.manager.impl;
19
20 import java.io.File JavaDoc;
21 import java.io.IOException JavaDoc;
22 import java.util.ArrayList JavaDoc;
23 import java.util.Collections JavaDoc;
24 import java.util.HashMap JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.Map JavaDoc;
27 import java.util.StringTokenizer JavaDoc;
28 import java.util.jar.JarFile JavaDoc;
29 import java.util.jar.Manifest JavaDoc;
30
31 import org.apache.avalon.extension.Extension;
32 import org.apache.avalon.extension.manager.ExtensionManager;
33 import org.apache.avalon.extension.manager.OptionalPackage;
34
35 /**
36  * <p>Interface used to contain "Optional Packages" (formerly known as
37  * "Standard Extensions"). It is assumed that each "Optional Package" is
38  * represented by a single file on the file system. This Repository searches
39  * a path to find the Optional Packages.</p>
40  *
41  * @author <a HREF="mailto:dev@avalon.apache.org">Avalon Development Team</a>
42  * @version $Revision: 1.2 $ $Date: 2004/02/24 22:39:31 $
43  * @see OptionalPackage
44  * @see ExtensionManager
45  */

46 public class DefaultExtensionManager
47     implements ExtensionManager
48 {
49     private static final boolean DEBUG = false;
50
51     /**
52      * separator used to separate path elements in a string.
53      */

54     private static final String JavaDoc SEPARATOR = "|";
55
56     /**
57      * Map between files and {@link OptionalPackage} objects.
58      */

59     private final Map JavaDoc m_packages = new HashMap JavaDoc();
60
61     /**
62      * The set of directories in which to look for Optional Packages
63      */

64     private File JavaDoc[] m_path;
65
66     /**
67      * Flag set when it is necessary to scan paths to
68      * build "Optional Package" list
69      */

70     private boolean m_needToScan;
71
72     /**
73      * Construct a package repository with no path specified.
74      */

75     public DefaultExtensionManager()
76     {
77         this( new File JavaDoc[ 0 ] );
78     }
79
80     /**
81      * Construct a package repository with path.
82      *
83      * @param path The set of directories in which to look for Optional Packages
84      */

85     public DefaultExtensionManager( final File JavaDoc[] path )
86     {
87         setPath( path );
88     }
89
90     /**
91      * Return an array of path elements where each
92      * element in array represents a directory
93      * in which the ExtensionManager will look
94      * for Extensions.
95      *
96      * @return the list of paths to search in
97      */

98     public File JavaDoc[] getPaths()
99     {
100         return m_path;
101     }
102
103     /**
104      * Return all the {@link OptionalPackage}s that satisfy specified
105      * {@link Extension}. It is expected that this {@link Extension}
106      * object will be one retrieved via getLocalExtension() method. If the
107      * specified {@link Extension} is not local then <code>null</code>
108      * is returned.
109      *
110      * @param extension the extension to search for
111      * @return an array of optional packages that satisfy the extension
112      * (and the extensions dependencies)
113      */

114     public synchronized OptionalPackage[] getOptionalPackages( final Extension extension )
115     {
116         if( m_needToScan )
117         {
118             scanPath();
119         }
120
121         final ArrayList JavaDoc results = new ArrayList JavaDoc();
122         final ArrayList JavaDoc candidates = (ArrayList JavaDoc)m_packages.get( extension.getExtensionName() );
123         if( null != candidates )
124         {
125             final int size = candidates.size();
126             for( int i = 0; i < size; i++ )
127             {
128                 final OptionalPackage optionalPackage = (OptionalPackage)candidates.get( i );
129                 final Extension[] extensions = optionalPackage.getAvailableExtensions();
130
131                 for( int j = 0; j < extensions.length; j++ )
132                 {
133                     if( extensions[ j ].isCompatibleWith( extension ) )
134                     {
135                         results.add( optionalPackage );
136                     }
137                 }
138             }
139         }
140
141         final OptionalPackageComparator comparator =
142             new OptionalPackageComparator( extension.getExtensionName() );
143         Collections.sort( results, comparator );
144         return (OptionalPackage[])results.toArray( new OptionalPackage[ 0 ] );
145     }
146
147     /**
148      * Return all the OptionalPackages stored in ExtensionManager.
149      *
150      * @return all the OptionalPackages stored in ExtensionManager.
151      */

152     protected synchronized OptionalPackage[] getAllOptionalPackages()
153     {
154         //This is woefully inefficient - should rewrite it somehow
155
final ArrayList JavaDoc packages = new ArrayList JavaDoc();
156         final Iterator JavaDoc iterator = m_packages.values().iterator();
157         while( iterator.hasNext() )
158         {
159             final ArrayList JavaDoc list = (ArrayList JavaDoc)iterator.next();
160             final int size = list.size();
161             for( int i = 0; i < size; i++ )
162             {
163                 final OptionalPackage optionalPackage = (OptionalPackage)list.get( i );
164                 if( !packages.contains( optionalPackage ) )
165                 {
166                     packages.add( optionalPackage );
167                 }
168             }
169         }
170
171         return (OptionalPackage[])packages.toArray( new OptionalPackage[ packages.size() ] );
172     }
173
174     /**
175      * Add path elements to repository search path
176      *
177      * @param pathElements the path elements to add to repository search path
178      */

179     protected void addPathElements( final String JavaDoc[] pathElements )
180     {
181         final File JavaDoc[] path = toFiles( pathElements );
182         addPathElements( path );
183     }
184
185     /**
186      * Add path elements to repository search path
187      *
188      * @param path the path elements to add to repository search path
189      */

190     protected synchronized void addPathElements( final File JavaDoc[] path )
191     {
192         validatePath( path );
193         final File JavaDoc[] files = resolvePath( path );
194         m_path = mergePaths( files );
195         m_needToScan = true;
196     }
197
198     /**
199      * Add path elements to repository search path.
200      * Note that each path element is separated by a '|' character.
201      *
202      * @param pathString the path elements to add to repository search path
203      */

204     protected void addPathElements( final String JavaDoc pathString )
205     {
206         final String JavaDoc[] pathElements = split( pathString, SEPARATOR );
207         addPathElements( pathElements );
208     }
209
210     /**
211      * Set the path for the Repository.
212      * Note thart each path element is separated by a '|' character.
213      *
214      * @param pathString the list of directories in which to search
215      */

216     protected synchronized void setPath( final String JavaDoc pathString )
217     {
218         final String JavaDoc[] pathElements = split( pathString, SEPARATOR );
219         setPath( pathElements );
220     }
221
222     /**
223      * Set the path for the Repository.
224      *
225      * @param pathElements the list of directories in which to search
226      */

227     protected synchronized void setPath( final String JavaDoc[] pathElements )
228     {
229         final File JavaDoc[] path = toFiles( pathElements );
230         setPath( path );
231     }
232
233     /**
234      * Set the path for the Repository.
235      *
236      * @param path the list of directories in which to search
237      */

238     protected synchronized void setPath( final File JavaDoc[] path )
239     {
240         validatePath( path );
241         m_path = resolvePath( path );
242         m_needToScan = true;
243     }
244
245     /**
246      * Scan the path for this repository and reload all
247      * the "Optional Packages" found in the path.
248      * All of the old Extensions/Optional Packages will
249      * be removed.
250      */

251     protected final synchronized void scanPath()
252     {
253         clearCache();
254
255         for( int i = 0; i < m_path.length; i++ )
256         {
257             scanDirectory( m_path[ i ] );
258         }
259     }
260
261     /**
262      * Utility method to scan a directory for
263      * all jar fi;les in directory and add them as
264      * OptionalPackages.
265      *
266      * @param directory the directory to scan
267      */

268     private synchronized void scanDirectory( final File JavaDoc directory )
269     {
270         final File JavaDoc[] files = directory.listFiles();
271         for( int i = 0; i < files.length; i++ )
272         {
273             final File JavaDoc file = files[ i ];
274             final String JavaDoc name = file.getName();
275
276             if( !name.endsWith( ".jar" ) )
277             {
278                 final String JavaDoc message =
279                     "Skipping " + file + " as it does not end with '.jar'";
280                 debug( message );
281                 continue;
282             }
283
284             if( !file.isFile() )
285             {
286                 final String JavaDoc message =
287                     "Skipping " + file + " as it is not a file.";
288                 debug( message );
289                 continue;
290             }
291
292             if( !file.canRead() )
293             {
294                 final String JavaDoc message =
295                     "Skipping " + file + " as it is not readable.";
296                 debug( message );
297                 continue;
298             }
299
300             try
301             {
302                 final OptionalPackage optionalPackage = getOptionalPackage( file );
303                 cacheOptionalPackage( optionalPackage );
304             }
305             catch( final IOException JavaDoc ioe )
306             {
307                 final String JavaDoc message =
308                     "Skipping " + file + " as it could not be loaded " +
309                     "due to " + ioe;
310                 debug( message );
311             }
312         }
313     }
314
315     /**
316      * Clear internal cache of optional packages.
317      */

318     protected final synchronized void clearCache()
319     {
320         m_packages.clear();
321         m_needToScan = true;
322     }
323
324     /**
325      * Add OptionalPackage to internal cache of Optional Packages.
326      * Note that this method is only protected so that unit tests can sub-class
327      * and add entries to PackageRepository by calling this method.
328      *
329      * @param optionalPackage the OptionalPackage to be added to repository
330      */

331     protected final synchronized void cacheOptionalPackage( final OptionalPackage optionalPackage )
332     {
333         m_needToScan = false;
334
335         // added to avoid out of bounds exception
336
if( optionalPackage.getAvailableExtensions().length == 0 )
337         {
338             return;
339         }
340
341         final Extension extension = optionalPackage.getAvailableExtensions()[ 0 ];
342         ArrayList JavaDoc candidates = (ArrayList JavaDoc)m_packages.get( extension.getExtensionName() );
343         if( null == candidates )
344         {
345             candidates = new ArrayList JavaDoc();
346             m_packages.put( extension.getExtensionName(), candidates );
347         }
348
349         candidates.add( optionalPackage );
350     }
351
352     /**
353      * Construct an OptionalPackage out of the specified jar archive.
354      *
355      * @param archive the file object for Jar archive
356      * @return the OptionalPackage constructed
357      * @throws IOException if an error occurs
358      */

359     private OptionalPackage getOptionalPackage( final File JavaDoc archive )
360         throws IOException JavaDoc
361     {
362         final File JavaDoc file = archive.getCanonicalFile();
363         final JarFile JavaDoc jarFile = new JarFile JavaDoc( file );
364         try
365         {
366             final Manifest JavaDoc manifest = jarFile.getManifest();
367             if( null == manifest )
368             {
369                 return null;
370             }
371             final Extension[] available = Extension.getAvailable( manifest );
372             final Extension[] required = Extension.getRequired( manifest );
373
374             return new OptionalPackage( file, available, required );
375         }
376         finally
377         {
378             jarFile.close();
379         }
380     }
381
382     /**
383      * Output a debug message for repository.
384      *
385      * @param message the debug message
386      */

387     protected void debug( final String JavaDoc message )
388     {
389         if( DEBUG )
390         {
391             System.out.println( message );
392         }
393     }
394
395     /**
396      * Get Canonical or failing that the absolute file
397      * for every specified file.
398      *
399      * @param path the files that make up path
400      * @return the resolved path
401      */

402     private File JavaDoc[] resolvePath( final File JavaDoc[] path )
403     {
404         final File JavaDoc[] resultPath = new File JavaDoc[ path.length ];
405         for( int i = 0; i < path.length; i++ )
406         {
407             resultPath[ i ] = resolveFile( path[ i ] );
408         }
409         return resultPath;
410     }
411
412     /**
413      * Get Canonical or failing that the absolute file
414      * for specified file.
415      *
416      * @param file the file
417      * @return the resolved file
418      */

419     private File JavaDoc resolveFile( final File JavaDoc file )
420     {
421         try
422         {
423             return file.getCanonicalFile();
424         }
425         catch( IOException JavaDoc e )
426         {
427             return file.getAbsoluteFile();
428         }
429     }
430
431     /**
432      * Validate each element in path to make sure they are valid.
433      *
434      * @param path the path
435      */

436     private void validatePath( final File JavaDoc[] path )
437     {
438         if( null == path )
439         {
440             throw new NullPointerException JavaDoc( "path" );
441         }
442
443         for( int i = 0; i < path.length; i++ )
444         {
445             validatePathElement( path[ i ] );
446         }
447     }
448
449     /**
450      * Make sure specified path element is valid.
451      * The elements should exist and should be a directory.
452      *
453      * @param file the path element
454      */

455     private void validatePathElement( final File JavaDoc file )
456     {
457         if( !file.exists() || !file.isDirectory() )
458         {
459             final String JavaDoc message = "path element " + file +
460                 " must exist and must be a directory";
461             throw new IllegalArgumentException JavaDoc( message );
462         }
463     }
464
465     /**
466      * Merge the specified file list with existing path.
467      *
468      * @param files the files to merge
469      * @return the merged path
470      */

471     private File JavaDoc[] mergePaths( final File JavaDoc[] files )
472     {
473         final File JavaDoc[] resultPath =
474             new File JavaDoc[ m_path.length + files.length ];
475         System.arraycopy( m_path, 0, resultPath, 0, m_path.length );
476         System.arraycopy( files, m_path.length, resultPath, 0, files.length );
477         return resultPath;
478     }
479
480     /**
481      * Convert set of string elements into file objects
482      *
483      * @param pathElements the string path elements
484      * @return the file array representing each element
485      */

486     private File JavaDoc[] toFiles( final String JavaDoc[] pathElements )
487     {
488         final File JavaDoc[] path = new File JavaDoc[ pathElements.length ];
489         for( int i = 0; i < path.length; i++ )
490         {
491             path[ i ] = new File JavaDoc( pathElements[ i ] );
492         }
493         return path;
494     }
495
496     /**
497      * Splits the string on every token into an array of strings.
498      *
499      * @param string the string
500      * @param onToken the token
501      * @return the resultant array
502      */

503     private static String JavaDoc[] split( final String JavaDoc string, final String JavaDoc onToken )
504     {
505         final StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc( string, onToken );
506         final String JavaDoc[] result = new String JavaDoc[ tokenizer.countTokens() ];
507
508         for( int i = 0; i < result.length; i++ )
509         {
510             result[ i ] = tokenizer.nextToken();
511         }
512
513         return result;
514     }
515 }
516
Popular Tags