KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > loom > components > extensions > pkgmgr > impl > DefaultExtensionManager


1 /* ====================================================================
2  * Loom Software License, version 1.1
3  *
4  * Copyright (c) 2003, Loom Group. All rights reserved.
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in
14  * the documentation and/or other materials provided with the
15  * distribution.
16  *
17  * 3. Neither the name of the Loom Group nor the name "Loom" nor
18  * the names of its contributors may be used to endorse or promote
19  * products derived from this software without specific prior
20  * written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *
35  * ====================================================================
36  *
37  * Loom includes code from the Apache Software Foundation
38  *
39  * ====================================================================
40  * The Apache Software License, Version 1.1
41  *
42  * Copyright (c) 1997-2003 The Apache Software Foundation. All rights
43  * reserved.
44  *
45  * Redistribution and use in source and binary forms, with or without
46  * modification, are permitted provided that the following conditions
47  * are met:
48  *
49  * 1. Redistributions of source code must retain the above copyright
50  * notice, this list of conditions and the following disclaimer.
51  *
52  * 2. Redistributions in binary form must reproduce the above copyright
53  * notice, this list of conditions and the following disclaimer in
54  * the documentation and/or other materials provided with the
55  * distribution.
56  *
57  * 3. The end-user documentation included with the redistribution,
58  * if any, must include the following acknowledgment:
59  * "This product includes software developed by the
60  * Apache Software Foundation (http://www.apache.org/)."
61  * Alternately, this acknowledgment may appear in the software
62  * itself, if and wherever such third-party acknowledgments
63  * normally appear.
64  *
65  * 4. The names "Jakarta", "Avalon", and "Apache Software Foundation"
66  * must not be used to endorse or promote products derived from this
67  * software without prior written permission. For written
68  * permission, please contact apache@apache.org.
69  *
70  * 5. Products derived from this software may not be called "Apache",
71  * nor may "Apache" appear in their name, without prior written
72  * permission of the Apache Software Foundation.
73  *
74  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
75  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
76  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
77  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
78  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
79  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
80  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
81  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
82  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
83  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
84  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
85  * SUCH DAMAGE.
86  */

87 package org.codehaus.loom.components.extensions.pkgmgr.impl;
88
89 import java.io.File JavaDoc;
90 import java.io.IOException JavaDoc;
91 import java.util.ArrayList JavaDoc;
92 import java.util.Collections JavaDoc;
93 import java.util.HashMap JavaDoc;
94 import java.util.Iterator JavaDoc;
95 import java.util.Map JavaDoc;
96 import java.util.StringTokenizer JavaDoc;
97 import java.util.jar.JarFile JavaDoc;
98 import java.util.jar.Manifest JavaDoc;
99
100 import org.codehaus.loom.components.extensions.pkgmgr.ExtensionManager;
101 import org.codehaus.loom.components.extensions.pkgmgr.OptionalPackage;
102 import org.codehaus.loom.extension.Extension;
103
104 /**
105  * <p>Interface used to contain "Optional Packages" (formerly known as "Standard
106  * Extensions"). It is assumed that each "Optional Package" is represented by a
107  * single file on the file system. This Repository searches a path to find the
108  * Optional Packages.</p>
109  *
110  * @author Peter Donald
111  * @version $Revision: 1.3 $ $Date: 2004/08/17 23:14:32 $
112  * @see OptionalPackage
113  * @see ExtensionManager
114  */

115 public class DefaultExtensionManager
116     implements ExtensionManager
117 {
118     private static final boolean DEBUG = false;
119
120     /** separator used to separate path elements in a string. */
121     private static final String JavaDoc SEPARATOR = "|";
122
123     /** Map between files and {@link OptionalPackage} objects. */
124     private final Map JavaDoc m_packages = new HashMap JavaDoc();
125
126     /** The set of directories in which to look for Optional Packages */
127     private File JavaDoc[] m_path;
128
129     /**
130      * Flag set when it is necessary to scan paths to build "Optional Package"
131      * list
132      */

133     private boolean m_needToScan;
134
135     /**
136      * Construct a package repository with no path specified.
137      */

138     public DefaultExtensionManager()
139     {
140         this( new File JavaDoc[ 0 ] );
141     }
142
143     /**
144      * Construct a package repository with path.
145      *
146      * @param path The set of directories in which to look for Optional
147      * Packages
148      */

149     public DefaultExtensionManager( final File JavaDoc[] path )
150     {
151         setPath( path );
152     }
153
154     /**
155      * Return an array of path elements where each element in array represents a
156      * directory in which the ExtensionManager will look for Extensions.
157      *
158      * @return the list of paths to search in
159      */

160     public File JavaDoc[] getPaths()
161     {
162         return m_path;
163     }
164
165     /**
166      * Return all the {@link OptionalPackage}s that satisfy specified {@link
167      * Extension}. It is expected that this {@link Extension} object will be one
168      * retrieved via getLocalExtension() method. If the specified {@link
169      * Extension} is not local then <code>null</code> is returned.
170      *
171      * @param extension the extension to search for
172      * @return an array of optional packages that satisfy the extension (and the
173      * extensions dependencies)
174      */

175     public synchronized OptionalPackage[] getOptionalPackages(
176         final Extension extension )
177     {
178         if( m_needToScan )
179         {
180             scanPath();
181         }
182
183         final ArrayList JavaDoc results = new ArrayList JavaDoc();
184         final ArrayList JavaDoc candidates = (ArrayList JavaDoc)m_packages.get(
185             extension.getExtensionName() );
186         if( null != candidates )
187         {
188             final int size = candidates.size();
189             for( int i = 0; i < size; i++ )
190             {
191                 final OptionalPackage optionalPackage = (OptionalPackage)candidates.get(
192                     i );
193                 final Extension[] extensions = optionalPackage.getAvailableExtensions();
194
195                 for( int j = 0; j < extensions.length; j++ )
196                 {
197                     if( extensions[ j ].isCompatibleWith( extension ) )
198                     {
199                         results.add( optionalPackage );
200                     }
201                 }
202             }
203         }
204
205         final OptionalPackageComparator comparator =
206             new OptionalPackageComparator( extension.getExtensionName() );
207         Collections.sort( results, comparator );
208         return (OptionalPackage[])results.toArray( new OptionalPackage[ 0 ] );
209     }
210
211     /**
212      * Return all the OptionalPackages stored in ExtensionManager.
213      *
214      * @return all the OptionalPackages stored in ExtensionManager.
215      */

216     protected synchronized OptionalPackage[] getAllOptionalPackages()
217     {
218         //This is woefully inefficient - should rewrite it somehow
219
final ArrayList JavaDoc packages = new ArrayList JavaDoc();
220         final Iterator JavaDoc iterator = m_packages.values().iterator();
221         while( iterator.hasNext() )
222         {
223             final ArrayList JavaDoc list = (ArrayList JavaDoc)iterator.next();
224             final int size = list.size();
225             for( int i = 0; i < size; i++ )
226             {
227                 final OptionalPackage optionalPackage = (OptionalPackage)list.get(
228                     i );
229                 if( !packages.contains( optionalPackage ) )
230                 {
231                     packages.add( optionalPackage );
232                 }
233             }
234         }
235
236         return (OptionalPackage[])packages.toArray(
237             new OptionalPackage[ packages.size() ] );
238     }
239
240     /**
241      * Add path elements to repository search path
242      *
243      * @param pathElements the path elements to add to repository search path
244      */

245     protected void addPathElements( final String JavaDoc[] pathElements )
246     {
247         final File JavaDoc[] path = toFiles( pathElements );
248         addPathElements( path );
249     }
250
251     /**
252      * Add path elements to repository search path
253      *
254      * @param path the path elements to add to repository search path
255      */

256     protected synchronized void addPathElements( final File JavaDoc[] path )
257     {
258         validatePath( path );
259         final File JavaDoc[] files = resolvePath( path );
260         m_path = mergePaths( files );
261         m_needToScan = true;
262     }
263
264     /**
265      * Add path elements to repository search path. Note that each path element
266      * is separated by a '|' character.
267      *
268      * @param pathString the path elements to add to repository search path
269      */

270     protected void addPathElements( final String JavaDoc pathString )
271     {
272         final String JavaDoc[] pathElements = split( pathString, SEPARATOR );
273         addPathElements( pathElements );
274     }
275
276     /**
277      * Set the path for the Repository. Note thart each path element is
278      * separated by a '|' character.
279      *
280      * @param pathString the list of directories in which to search
281      */

282     protected synchronized void setPath( final String JavaDoc pathString )
283     {
284         final String JavaDoc[] pathElements = split( pathString, SEPARATOR );
285         setPath( pathElements );
286     }
287
288     /**
289      * Set the path for the Repository.
290      *
291      * @param pathElements the list of directories in which to search
292      */

293     protected synchronized void setPath( final String JavaDoc[] pathElements )
294     {
295         final File JavaDoc[] path = toFiles( pathElements );
296         setPath( path );
297     }
298
299     /**
300      * Set the path for the Repository.
301      *
302      * @param path the list of directories in which to search
303      */

304     protected synchronized void setPath( final File JavaDoc[] path )
305     {
306         validatePath( path );
307         m_path = resolvePath( path );
308         m_needToScan = true;
309     }
310
311     /**
312      * Scan the path for this repository and reload all the "Optional Packages"
313      * found in the path. All of the old Extensions/Optional Packages will be
314      * removed.
315      */

316     protected final synchronized void scanPath()
317     {
318         clearCache();
319
320         for( int i = 0; i < m_path.length; i++ )
321         {
322             scanDirectory( m_path[ i ] );
323         }
324     }
325
326     /**
327      * Utility method to scan a directory for all jar fi;les in directory and
328      * add them as OptionalPackages.
329      *
330      * @param directory the directory to scan
331      */

332     private synchronized void scanDirectory( final File JavaDoc directory )
333     {
334         final File JavaDoc[] files = directory.listFiles();
335         for( int i = 0; i < files.length; i++ )
336         {
337             final File JavaDoc file = files[ i ];
338             final String JavaDoc name = file.getName();
339
340             if( !name.endsWith( ".jar" ) )
341             {
342                 final String JavaDoc message =
343                     "Skipping " + file + " as it does not end with '.jar'";
344                 debug( message );
345                 continue;
346             }
347
348             if( !file.isFile() )
349             {
350                 final String JavaDoc message =
351                     "Skipping " + file + " as it is not a file.";
352                 debug( message );
353                 continue;
354             }
355
356             if( !file.canRead() )
357             {
358                 final String JavaDoc message =
359                     "Skipping " + file + " as it is not readable.";
360                 debug( message );
361                 continue;
362             }
363
364             try
365             {
366                 final OptionalPackage optionalPackage = getOptionalPackage(
367                     file );
368                 cacheOptionalPackage( optionalPackage );
369             }
370             catch( final IOException JavaDoc ioe )
371             {
372                 final String JavaDoc message =
373                     "Skipping " +
374                     file +
375                     " as it could not be loaded " +
376                     "due to " + ioe;
377                 debug( message );
378             }
379         }
380     }
381
382     /**
383      * Clear internal cache of optional packages.
384      */

385     protected final synchronized void clearCache()
386     {
387         m_packages.clear();
388         m_needToScan = true;
389     }
390
391     /**
392      * Add OptionalPackage to internal cache of Optional Packages. Note that
393      * this method is only protected so that unit tests can sub-class and add
394      * entries to PackageRepository by calling this method.
395      *
396      * @param optionalPackage the OptionalPackage to be added to repository
397      */

398     protected final synchronized void cacheOptionalPackage(
399         final OptionalPackage optionalPackage )
400     {
401         m_needToScan = false;
402
403         // added to avoid out of bounds exception
404
if( optionalPackage.getAvailableExtensions().length == 0 )
405         {
406             return;
407         }
408
409         final Extension extension = optionalPackage.getAvailableExtensions()[ 0 ];
410         ArrayList JavaDoc candidates = (ArrayList JavaDoc)m_packages.get(
411             extension.getExtensionName() );
412         if( null == candidates )
413         {
414             candidates = new ArrayList JavaDoc();
415             m_packages.put( extension.getExtensionName(), candidates );
416         }
417
418         candidates.add( optionalPackage );
419     }
420
421     /**
422      * Construct an OptionalPackage out of the specified jar archive.
423      *
424      * @param archive the file object for Jar archive
425      * @return the OptionalPackage constructed
426      * @throws IOException if an error occurs
427      */

428     private OptionalPackage getOptionalPackage( final File JavaDoc archive )
429         throws IOException JavaDoc
430     {
431         final File JavaDoc file = archive.getCanonicalFile();
432         final JarFile JavaDoc jarFile = new JarFile JavaDoc( file );
433         try
434         {
435             final Manifest JavaDoc manifest = jarFile.getManifest();
436             if( null == manifest )
437             {
438                 return null;
439             }
440             final Extension[] available = Extension.getAvailable( manifest );
441             final Extension[] required = Extension.getRequired( manifest );
442
443             return new OptionalPackage( file, available, required );
444         }
445         finally
446         {
447             jarFile.close();
448         }
449     }
450
451     /**
452      * Output a debug message for repository.
453      *
454      * @param message the debug message
455      */

456     protected void debug( final String JavaDoc message )
457     {
458         if( DEBUG )
459         {
460             System.out.println( message );
461         }
462     }
463
464     /**
465      * Get Canonical or failing that the absolute file for every specified
466      * file.
467      *
468      * @param path the files that make up path
469      * @return the resolved path
470      */

471     private File JavaDoc[] resolvePath( final File JavaDoc[] path )
472     {
473         final File JavaDoc[] resultPath = new File JavaDoc[ path.length ];
474         for( int i = 0; i < path.length; i++ )
475         {
476             resultPath[ i ] = resolveFile( path[ i ] );
477         }
478         return resultPath;
479     }
480
481     /**
482      * Get Canonical or failing that the absolute file for specified file.
483      *
484      * @param file the file
485      * @return the resolved file
486      */

487     private File JavaDoc resolveFile( final File JavaDoc file )
488     {
489         try
490         {
491             return file.getCanonicalFile();
492         }
493         catch( IOException JavaDoc e )
494         {
495             return file.getAbsoluteFile();
496         }
497     }
498
499     /**
500      * Validate each element in path to make sure they are valid.
501      *
502      * @param path the path
503      */

504     private void validatePath( final File JavaDoc[] path )
505     {
506         if( null == path )
507         {
508             throw new NullPointerException JavaDoc( "path" );
509         }
510
511         for( int i = 0; i < path.length; i++ )
512         {
513             validatePathElement( path[ i ] );
514         }
515     }
516
517     /**
518      * Make sure specified path element is valid. The elements should exist and
519      * should be a directory.
520      *
521      * @param file the path element
522      */

523     private void validatePathElement( final File JavaDoc file )
524     {
525         if( !file.exists() || !file.isDirectory() )
526         {
527             final String JavaDoc message = "path element " +
528                 file +
529                 " must exist and must be a directory";
530             throw new IllegalArgumentException JavaDoc( message );
531         }
532     }
533
534     /**
535      * Merge the specified file list with existing path.
536      *
537      * @param files the files to merge
538      * @return the merged path
539      */

540     private File JavaDoc[] mergePaths( final File JavaDoc[] files )
541     {
542         final File JavaDoc[] resultPath =
543             new File JavaDoc[ m_path.length + files.length ];
544         System.arraycopy( m_path, 0, resultPath, 0, m_path.length );
545         System.arraycopy( files, m_path.length, resultPath, 0, files.length );
546         return resultPath;
547     }
548
549     /**
550      * Convert set of string elements into file objects
551      *
552      * @param pathElements the string path elements
553      * @return the file array representing each element
554      */

555     private File JavaDoc[] toFiles( final String JavaDoc[] pathElements )
556     {
557         final File JavaDoc[] path = new File JavaDoc[ pathElements.length ];
558         for( int i = 0; i < path.length; i++ )
559         {
560             path[ i ] = new File JavaDoc( pathElements[ i ] );
561         }
562         return path;
563     }
564
565     /**
566      * Splits the string on every token into an array of strings.
567      *
568      * @param string the string
569      * @param onToken the token
570      * @return the resultant array
571      */

572     private static String JavaDoc[] split( final String JavaDoc string, final String JavaDoc onToken )
573     {
574         final StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc( string,
575                                                                onToken );
576         final String JavaDoc[] result = new String JavaDoc[ tokenizer.countTokens() ];
577
578         for( int i = 0; i < result.length; i++ )
579         {
580             result[ i ] = tokenizer.nextToken();
581         }
582
583         return result;
584     }
585 }
586
Popular Tags