[CVS jcontainer] Rework Extension loader so it just complains if there is an malformed manifest rather than ejecting load

Peter Donald <pdonald-yCVjj/[email protected]> Tue, 20 Apr 2004 20:39:59 -0500
Newsgroups gmane.comp.java.jcontainer.cvs
Message-ID <[email protected]>
<html>
<head>
<style><!--
  body {background-color:#ffffff;}
  .file {border:1px solid #eeeeee;margin-top:1em;margin-bottom:1em;}
  .pathname {font-family:monospace; float:right;}
  .fileheader {margin-bottom:.5em;}
  .diff {margin:0;}
  .tasklist {padding:4px;border:1px dashed #000000;margin-top:1em;}
  .tasklist ul {margin-top:0;margin-bottom:0;}
  tr.alt {background-color:#eeeeee}
  #added {background-color:#ddffdd;}
  #addedchars {background-color:#99ff99;font-weight:bolder;}
  tr.alt #added {background-color:#ccf7cc;}
  #removed {background-color:#ffdddd;}
  #removedchars {background-color:#ff9999;font-weight:bolder;}
  tr.alt #removed {background-color:#f7cccc;}
  #info {color:#888888;}
  #context {background-color:#eeeeee;}
  td {padding-left:.3em;padding-right:.3em;}
  tr.head {border-bottom-width:1px;border-bottom-style:solid;}
  tr.head td {padding:0;padding-top:.2em;}
  .task {background-color:#ffff00;}
  .comment {padding:4px;border:1px dashed #000000;background-color:#ffffdd}
  .error {color:red;}
  hr {border-width:0px;height:2px;background:black;}
--></style>
</head>
<body>
<table cellspacing="0" cellpadding="0" border="0" rules="cols">
<tr class="head"><td colspan="4">Commit in <b><tt>jcontainer/loom</tt></b><span id="info"> on MAIN</span></td></tr>
<tr><td><tt><a href="#file1">loom.iml</a></tt></td><td align="right" id="added">+1</td><td></td><td nowrap="nowrap" align="center"><a href="http://xstream.cvs.codehaus.org/jcontainer/loom/loom.iml?rev=1.1&amp;content-type=text/vnd.viewcvs-markup">1.1</a> <a href="http://xstream.cvs.codehaus.org/jcontainer/loom/loom.iml.diff?r1=1.1&amp;r2=1.2">-&gt;</a> <a href="http://xstream.cvs.codehaus.org/jcontainer/loom/loom.iml?rev=1.2&amp;content-type=text/vnd.viewcvs-markup">1.2</a></td></tr>
<tr class="alt"><td><tt>engine/src/java/org/codehaus/loom/components/classloader/<a href="#file2">SarLoaderResolver.java</a></tt></td><td align="right" id="added">+13</td><td align="right" id="removed">-2</td><td nowrap="nowrap" align="center"><a href="http://xstream.cvs.codehaus.org/jcontainer/loom/engine/src/java/org/codehaus/loom/components/classloader/SarLoaderResolver.java?rev=1.1&amp;content-type=text/vnd.viewcvs-markup">1.1</a> <a href="http://xstream.cvs.codehaus.org/jcontainer/loom/engine/src/java/org/codehaus/loom/components/classloader/SarLoaderResolver.java.diff?r1=1.1&amp;r2=1.2">-&gt;</a> <a href="http://xstream.cvs.codehaus.org/jcontainer/loom/engine/src/java/org/codehaus/loom/components/classloader/SarLoaderResolver.java?rev=1.2&amp;content-type=text/vnd.viewcvs-markup">1.2</a></td></tr>
<tr><td><tt>support/classman/src/java/org/codehaus/loom/classman/builder/<a href="#file3">Resources.properties</a></tt></td><td align="right" id="added">+2</td><td align="right" id="removed">-1</td><td nowrap="nowrap" align="center"><a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java/org/codehaus/loom/classman/builder/Resources.properties?rev=1.1&amp;content-type=text/vnd.viewcvs-markup">1.1</a> <a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java/org/codehaus/loom/classman/builder/Resources.properties.diff?r1=1.1&amp;r2=1.2">-&gt;</a> <a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java/org/codehaus/loom/classman/builder/Resources.properties?rev=1.2&amp;content-type=text/vnd.viewcvs-markup">1.2</a></td></tr>
<tr class="alt"><td><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/<a href="#file4">SimpleLoaderResolver.java</a></tt></td><td align="right" id="added">+1</td><td align="right" id="removed">-1</td><td nowrap="nowrap" align="center"><a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java/org/codehaus/loom/classman/builder/SimpleLoaderResolver.java?rev=1.1&amp;content-type=text/vnd.viewcvs-markup">1.1</a> <a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java/org/codehaus/loom/classman/builder/SimpleLoaderResolver.java.diff
 ?r1=1.1&amp;r2=1.2">-&gt;</a> <a href="http://xstream.cvs.codehaus.org/jcontainer/loom/su!
pport/classman/src/java/org/codehaus/loom/classman/builder/SimpleLoaderResolver.java?rev=1.2&amp;content-type=text/vnd.viewcvs-markup">1.2</a></td></tr>
<tr><td></td><td align="right" id="added">+17</td><td align="right" id="removed">-4</td><td></td></tr>
</table>
<small id="info">4 modified files</small><br />
<pre class="comment">
Rework Extension loader so it just complains if there is an malformed manifest rather than ejecting load
</pre>
<hr /><a name="file1" /><div class="file">
<span class="pathname"><a href="http://xstream.cvs.codehaus.org/jcontainer">jcontainer</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom">loom</a><br /></span>
<div class="fileheader"><big><b>loom.iml</b></big> <small id="info"><a href="http://xstream.cvs.codehaus.org/jcontainer/loom/loom.iml?rev=1.1&amp;content-type=text/vnd.viewcvs-markup">1.1</a> <a href="http://xstream.cvs.codehaus.org/jcontainer/loom/loom.iml.diff?r1=1.1&amp;r2=1.2">-&gt;</a> <a href="http://xstream.cvs.codehaus.org/jcontainer/loom/loom.iml?rev=1.2&amp;content-type=text/vnd.viewcvs-markup">1.2</a></small></div>
<pre class="diff"><small id="info">diff -u -r1.1 -r1.2
--- loom.iml	19 Apr 2004 23:32:17 -0000	1.1
+++ loom.iml	21 Apr 2004 01:39:58 -0000	1.2
@@ -43,6 +43,7 @@
</small></pre><pre class="diff" id="context">     &lt;orderEntry type="library" name="Ant" level="application" /&gt;
     &lt;orderEntry type="module" module-name="dna" /&gt;
     &lt;orderEntry type="module" module-name="metaclass" /&gt;
</pre><pre class="diff" id="added">+    &lt;orderEntry type="module" module-name="salt" /&gt;
</pre><pre class="diff" id="context">   &lt;/component&gt;
 &lt;/module&gt;
 
</pre></div>
<hr /><a name="file2" /><div class="file">
<span class="pathname"><a href="http://xstream.cvs.codehaus.org/jcontainer">jcontainer</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom">loom</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/engine">engine</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/engine/src">src</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/engine/src/java">java</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/engine/src/java/org">org</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/engine/src/java/org/codehaus">codehaus</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/engine/src/java/org/codehaus/loom">loom</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/engine/src/java/org/codehaus/loom/components">components</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/engine/src/java/org/codehaus/loom/components
 /classloader">classloader</a><br /></span>
<div class="fileheader"><big><b>SarLoaderResolver.java</b></big> <small id="info"><a href="http://xstream.cvs.codehaus.org/jcontainer/loom/engine/src/java/org/codehaus/loom/components/classloader/SarLoaderResolver.java?rev=1.1&amp;content-type=text/vnd.viewcvs-markup">1.1</a> <a href="http://xstream.cvs.codehaus.org/jcontainer/loom/engine/src/java/org/codehaus/loom/components/classloader/SarLoaderResolver.java.diff?r1=1.1&amp;r2=1.2">-&gt;</a> <a href="http://xstream.cvs.codehaus.org/jcontainer/loom/engine/src/java/org/codehaus/loom/components/classloader/SarLoaderResolver.java?rev=1.2&amp;content-type=text/vnd.viewcvs-markup">1.2</a></small></div>
<pre class="diff"><small id="info">diff -u -r1.1 -r1.2
--- SarLoaderResolver.java	19 Apr 2004 22:22:48 -0000	1.1
+++ SarLoaderResolver.java	21 Apr 2004 01:39:58 -0000	1.2
@@ -107,7 +107,7 @@
</small></pre><pre class="diff" id="context">  * split across multiple directories.
  *
  * @author Peter Donald
</pre><pre class="diff" id="removed">- * @version $Revision: 1.1 $ $Date: 2004/04/19 22:22:48 $
</pre><pre class="diff" id="added">+ * @version $Revision: 1.2 $ $Date: 2004/04/21 01:39:58 $
</pre><pre class="diff" id="context">  */
 class SarLoaderResolver
     extends SimpleLoaderResolver
</pre><pre class="diff"><small id="info">@@ -288,13 +288,24 @@
</small></pre><pre class="diff" id="context">     }
 
     /**
</pre><pre class="diff" id="removed">-     * Route Logging to Avalons Logger.
</pre><pre class="diff" id="added">+     * Route Logging to Logger facade.
</pre><pre class="diff" id="context">      *
      * @param message the warn message
      */
     protected void warn( final String message )
     {
         m_logger.warn( message );
</pre><pre class="diff" id="added">+    }
+
+    /**
+     * Route Logging to Logger facade.
+     *
+     * @param message the warn message
+     */
+    protected void warn( final String message, 
+                         final Throwable t )
+    {
+        m_logger.warn( message, t );
</pre><pre class="diff" id="context">     }
 
     protected void scanDependencies( final Extension[] required,
</pre></div>
<hr /><a name="file3" /><div class="file">
<span class="pathname"><a href="http://xstream.cvs.codehaus.org/jcontainer">jcontainer</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom">loom</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support">support</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman">classman</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src">src</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java">java</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java/org">org</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java/org/codehaus">codehaus</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java/org/codehaus/loom">loom</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/j
 ava/org/codehaus/loom/classman">classman</a>/<a href="http://xstream.cvs.codehaus.org/jco!
ntainer/loom/support/classman/src/java/org/codehaus/loom/classman/builder">builder</a><br /></span>
<div class="fileheader"><big><b>Resources.properties</b></big> <small id="info"><a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java/org/codehaus/loom/classman/builder/Resources.properties?rev=1.1&amp;content-type=text/vnd.viewcvs-markup">1.1</a> <a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java/org/codehaus/loom/classman/builder/Resources.properties.diff?r1=1.1&amp;r2=1.2">-&gt;</a> <a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java/org/codehaus/loom/classman/builder/Resources.properties?rev=1.2&amp;content-type=text/vnd.viewcvs-markup">1.2</a></small></div>
<pre class="diff"><small id="info">diff -u -r1.1 -r1.2
--- Resources.properties	19 Apr 2004 22:19:25 -0000	1.1
+++ Resources.properties	21 Apr 2004 01:39:59 -0000	1.2
@@ -5,4 +5,5 @@
</small></pre><pre class="diff" id="context"> required-extensions=The list of required extensions for application includes; {0}
 optional-packages-added=The list of "Optional Packages" added to the application includes; {0}
 classpath-entries=The list of classpath entries for the application includes; {0}
</pre><pre class="diff" id="removed">-missing-packagemanager=Unable to resolve Extension as no PackageManager has been specified.
</pre><pre class="diff"><small id="info">\ No newline at end of file
</small></pre><pre class="diff" id="added">+missing-packagemanager=Unable to resolve Extension as no PackageManager has been specified.
+malformed.extension=Malformed manifest found: {0}
</pre><pre class="diff"><small id="info">\ No newline at end of file
</small></pre></div>
<hr /><a name="file4" /><div class="file">
<span class="pathname"><a href="http://xstream.cvs.codehaus.org/jcontainer">jcontainer</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom">loom</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support">support</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman">classman</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src">src</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java">java</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java/org">org</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java/org/codehaus">codehaus</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java/org/codehaus/loom">loom</a>/<a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/j
 ava/org/codehaus/loom/classman">classman</a>/<a href="http://xstream.cvs.codehaus.org/jco!
ntainer/loom/support/classman/src/java/org/codehaus/loom/classman/builder">builder</a><br /></span>
<div class="fileheader"><big><b>SimpleLoaderResolver.java</b></big> <small id="info"><a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java/org/codehaus/loom/classman/builder/SimpleLoaderResolver.java?rev=1.1&amp;content-type=text/vnd.viewcvs-markup">1.1</a> <a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java/org/codehaus/loom/classman/builder/SimpleLoaderResolver.java.diff?r1=1.1&amp;r2=1.2">-&gt;</a> <a href="http://xstream.cvs.codehaus.org/jcontainer/loom/support/classman/src/java/org/codehaus/loom/classman/builder/SimpleLoaderResolver.java?rev=1.2&amp;content-type=text/vnd.viewcvs-markup">1.2</a></small></div>
<pre class="diff"><small id="info">diff -u -r1.1 -r1.2
--- SimpleLoaderResolver.java	19 Apr 2004 22:19:25 -0000	1.1
+++ SimpleLoaderResolver.java	21 Apr 2004 01:39:59 -0000	1.2
@@ -1 +1 @@
</small></pre><pre class="diff" id="removed">-/*
 * Copyright (C) The Spice Group. All rights reserved.
 *
 * This software is published under the terms of the Spice
 * Software License version 1.1, a copy of which has been included
 * with this distribution in the LICENSE.txt file.
 */
package org.codehaus.loom.classman.builder;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.jar.Manifest;

import org.codehaus.loom.classman.runtime.JoinClassLoader;
import org.codehaus.loom.extension.Extension;
import org.codehaus.spice.salt.i18n.ResourceManager;
import org.codehaus.spice.salt.i18n.Resources;
import org.codehaus.spice.salt.io.FileUtil;
import org.codehaus.spice.salt.io.PathMatcher;

/**
 * This is a class that performs resolver that; &lt;ul&gt; &lt;li&gt;creates "Join"
 * ClassLoaders using the  {@link JoinClassLoader} class&lt;/li&gt; &lt;li&gt;creates normal
 * ClassLoaders using a {@link URLClassLoader}. It also makes sure that all
 * dependencies of jars (as declared using the JDK1.3 "Optional Pakcages" Spec)
 * are present in classloader.&lt;/li&gt; &lt;li&gt;The locations are resolved to a single
 * base directiory.&lt;/li&gt; &lt;li&gt;The Extensions are not resolved but may be by
 * subclasses.&lt;/li&gt; &lt;li&gt;FileSets are currently unsupported and throw a {@link
 * UnsupportedOperationException} if attempt to be constructed.&lt;/li&gt; &lt;/ul&gt;
 *
 * @author Peter Donald
 * @version $Revision: 1.1 $ $Date: 2004/04/19 22:19:25 $
 */
public class SimpleLoaderResolver
    implements LoaderResolver
{
    /** i18n utils for presenting messages. */
    private static final Resources REZ =
        ResourceManager.getPackageResources( SimpleLoaderResolver.class );

    /** The base directory relative to which to aquire files. */
    private File m_baseDirectory;

    /**
     * Create a resolver that resolves all files according to specied
     * baseDirectory.
     *
     * @param baseDirectory the base directory
     */
    public SimpleLoaderResolver( final File baseDirectory )
    {
        setBaseDirectory( baseDirectory );
    }

    /**
     * Retrieve a URL for specified extension.
     *
     * @param extension the extension
     * @return the URL
     * @throws Exception if unable to locate URL for extension
     */
    public URL resolveExtension( final Extension extension )
        throws Exception
    {
        throw new UnsupportedOperationException();
    }

    /**
     * Resolve a location to a particular URL.
     *
     * @param location the location
     * @return the URL
     * @throws Exception if unable to resolve location
     */
    public URL resolveURL( final String location )
        throws Exception
    {
        final File file = getFileFor( location );
        String url = file.toURL().toString();
        if( file.isDirectory() )
        {
            url += "/";
        }
        return new URL( url );
    }

    /**
     * Resolve a fileset.
     *
     * @param baseDirectory the base directory of fileset
     * @param includes the list of ant-style includes
     * @param excludes the list of ant style excludes
     * @return the URLs contained within fileset
     * @throws Exception if unable to resolve fileset
     */
    public URL[] resolveFileSet( final String baseDirectory,
                                 final String[] includes,
                                 final String[] excludes )
        throws Exception
    {
        final File base = getFileFor( "." );
        return resolveFileSet( base, baseDirectory, includes, excludes );
    }

    /**
     * Create a Join ClassLoader for specified ClassLoaders. Use {@link
     * JoinClassLoader} to implement functionality.
     *
     * @param classLoaders the ClassLoaders to "join"
     * @return the joined ClassLoader
     * @throws Exception if unable to create classloader
     */
    public ClassLoader createJoinClassLoader( final ClassLoader[] classLoaders )
        throws Exception
    {
        return new JoinClassLoader( classLoaders,
                                    ClassLoader.getSystemClassLoader() );
    }

    /**
     * Create a ClassLoader with specified parent and containing specified URLs.
     * This implementation just creates it using the default URLClassLoader.
     *
     * @param parent the parent classloader
     * @param urls the URLs that the ClassLoader should contain
     * @return the newly created ClassLoader
     * @throws Exception if unable to create classloader
     */
    public ClassLoader createClassLoader( final ClassLoader parent,
                                          final URL[] urls )
        throws Exception
    {
        final URL[] classpath = determineCompleteClasspath( urls );
        return new URLClassLoader( classpath, parent );
    }

    /**
     * Retrieve the complete classpath given an input set of URLs. The complete
     * classpath includes all URLs for extensions required by the jars
     * (according to the "Optional Package" Spec).
     *
     * @param urls the urls
     * @return the complete set of URLs for classpath
     * @throws Exception if unable to determine complete classpath set
     */
    protected final URL[] determineCompleteClasspath( final URL[] urls )
        throws Exception
    {
        final ArrayList classpathSet = new ArrayList();

        //Add all supplied URLS to classpath
        for( int i = 0; i &lt; urls.length; i++ )
        {
            final URL url = urls[ i ];
            classpathSet.add( url );
        }

        //Add all the optional packages that are declared as
        // dependencies of class path elements
        final File[] files = getOptionalPackagesFor( urls );
        for( int i = 0; i &lt; files.length; i++ )
        {
            final File file = files[ i ];
            classpathSet.add( file.toURL() );
        }

        //Define final classpath with all dependencies added
        return (URL[])classpathSet.toArray( new URL[ classpathSet.size() ] );
    }

    /**
     * Utility class to retrieve a file object for specified location.
     *
     * @param location which to get file for.
     * @return the file for specified location
     */
    protected File getFileFor( final String location )
        throws IOException
    {
        File base = getBaseDirectory();
        if( null == base )
        {
            base = new File( "." );
        }

        return new File( base, location ).getCanonicalFile();
    }

    /**
     * Return the base directory against which to resolve relative files.
     *
     * @return the base directory against which to resolve relative files.
     */
    protected File getBaseDirectory()
    {
        return m_baseDirectory;
    }

    /**
     * Set the base directory.
     *
     * @param baseDirectory the base directory.
     */
    protected void setBaseDirectory( File baseDirectory )
    {
        m_baseDirectory = baseDirectory;
    }

    /**
     * Retrieve the files for the optional packages required by the jars in
     * ClassPath.
     *
     * @param classPath the Classpath array
     * @return the files that need to be added to ClassLoader
     */
    protected final File[] getOptionalPackagesFor( final URL[] classPath )
        throws Exception
    {
        final Manifest[] manifests = getManifests( classPath );
        final Extension[] available = Extension.getAvailable( manifests );
        final Extension[] required = Extension.getRequired( manifests );

        if( isDebugEnabled() )
        {
            final String message1 =
                REZ.format( "available-extensions",
                            Arrays.asList( available ) );
            debug( message1 );
            final String message2 =
                REZ.format( "required-extensions",
                            Arrays.asList( required ) );
            debug( message2 );
        }

        if( 0 == required.length )
        {
            return new File[ 0 ];
        }

        final Set dependencies = new HashSet();
        final Set unsatisfied = new HashSet();

        scanDependencies( required,
                          available,
                          dependencies,
                          unsatisfied );

        final int size = unsatisfied.size();
        if( 0 != size )
        {
            final Iterator iterator = unsatisfied.iterator();
            while( iterator.hasNext() )
            {
                final Extension extension = (Extension)iterator.next();
                final Object[] params = new Object[]
                {
                    extension.getExtensionName(),
                    extension.getSpecificationVendor(),
                    extension.getSpecificationVersion(),
                    extension.getImplementationVendor(),
                    extension.getImplementationVendorID(),
                    extension.getImplementationVersion(),
                    extension.getImplementationURL()
                };
                final String message = REZ.format( "missing.extension",
                                                   params );
                warn( message );
            }

            final String message =
                REZ.format( "unsatisfied.extensions", new Integer( size ) );
            throw new Exception( message );
        }

        if( isDebugEnabled() )
        {
            final String message =
                REZ.format( "optional-packages-added", dependencies );
            debug( message );
        }

        return (File[])dependencies.toArray( new File[ dependencies.size() ] );
    }

    /**
     * Attempt to locate a set of dependencies that transitively satisfy all
     * required extensions.
     *
     * @param required the required extensions
     * @param available the extensions already available
     * @param dependencies the set of dependencies collected by this method
     * @param unsatisfied this method should place any unsatisfied depenencies
     * into this set
     */
    protected void scanDependencies( final Extension[] required,
                                     final Extension[] available,
                                     final Set dependencies,
                                     final Set unsatisfied )
    {
        throw new UnsupportedOperationException();
    }

    /**
     * write out a warning message. Subclasses may overide this method to
     * redirect logging as appropriate.
     *
     * @param message the warning message
     */
    protected void warn( final String message )
    {
    }

    /**
     * Determine if debug messages are turned on. Subclasses should overide this
     * method.
     *
     * @return true if debugging enabled.
     */
    protected boolean isDebugEnabled()
    {
        return false;
    }

    /**
     * write out a debug message. Subclasses may overide this method to redirect
     * logging as appropriate.
     *
     * @param message the debug message
     */
    protected void debug( final String message )
    {
    }

    /**
     * Retrieve all the Manifests from the specified Classlpath.
     *
     * @param classPath the classpath
     * @return the set of manifests on the classpath
     * @throws Exception if there is an error reading manifests from files on
     * classpath
     */
    private Manifest[] getManifests( final URL[] classPath )
        throws Exception
    {
        final ArrayList manifests = new ArrayList();

        for( int i = 0; i &lt; classPath.length; i++ )
        {
            final URL element = classPath[ i ];
            if( element.getFile().endsWith( ".jar" ) )
            {
                try
                {
                    final URL url = new URL( "jar:" + element + "!/" );
                    final JarURLConnection connection =
                        (JarURLConnection)url.openConnection();
                    final Manifest manifest = connection.getManifest();
                    if( null != manifest )
                    {
                        manifests.add( manifest );
                    }
                }
                catch( final IOException ioe )
                {
                    final String message =
                        REZ.format( "bad-classpath-entry", element );
                    throw new Exception( message );
                }
            }
        }

        return (Manifest[])manifests.toArray( new Manifest[ 0 ] );
    }

    /**
     * Resolve a fileset in a particular hierarchy.
     *
     * @param base the file hierarchy to use
     * @param baseDirectory the base directory (relative to base)
     * @param includes the ant-style include patterns
     * @param excludes the ant-style exclude patterns
     * @return the resolved URLs for fileset
     */
    protected final URL[] resolveFileSet( final File base,
                                          final String baseDirectory,
                                          final String[] includes,
                                          final String[] excludes )
    {
        //woefully inefficient .. but then again - no need
        //for efficency here
        final String newBaseDirectory = FileUtil.normalize( baseDirectory );
        final String[] newIncludes = prefixPatterns( newBaseDirectory,
                                                     includes );
        final String[] newExcludes = prefixPatterns( newBaseDirectory,
                                                     excludes );
        final PathMatcher matcher = new PathMatcher( newIncludes,
                                                     newExcludes );
        final File[] files = FileUtil.resolveFileSet( base, matcher );
        try
        {
            return FileUtil.toURLs( files );
        }
        catch( IOException ioe )
        {
            throw new IllegalArgumentException( ioe.getMessage() );
        }
    }

    /**
     * Return a new array with specified prefix added to start of every element
     * in supplied array.
     *
     * @param prefix the prefix
     * @param patterns the source array
     * @return a new array with all elements having prefix added
     */
    private String[] prefixPatterns( final String prefix,
                                     final String[] patterns )
    {
        if( 0 == prefix.length() || ".".equals( prefix ) )
        {
            return patterns;
        }

        final String[] newPatterns = new String[ patterns.length ];
        for( int i = 0; i &lt; newPatterns.length; i++ )
        {
            newPatterns[ i ] = prefix + "/" + patterns[ i ];
        }
        return newPatterns;
    }
}
</pre><pre class="diff"><small id="info">\ No newline at end of file
</small></pre><pre class="diff" id="added">+/*
 * Copyright (C) The Spice Group. All rights reserved.
 *
 * This software is published under the terms of the Spice
 * Software License version 1.1, a copy of which has been included
 * with this distribution in the LICENSE.txt file.
 */
package org.codehaus.loom.classman.builder;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.jar.Manifest;

import org.codehaus.loom.classman.runtime.JoinClassLoader;
import org.codehaus.loom.extension.Extension;
import org.codehaus.spice.salt.i18n.ResourceManager;
import org.codehaus.spice.salt.i18n.Resources;
import org.codehaus.spice.salt.io.FileUtil;
import org.codehaus.spice.salt.io.PathMatcher;

/**
 * This is a class that performs resolver that; &lt;ul&gt; &lt;li&gt;creates "Join"
 * ClassLoaders using the  {@link JoinClassLoader} class&lt;/li&gt; &lt;li&gt;creates normal
 * ClassLoaders using a {@link URLClassLoader}. It also makes sure that all
 * dependencies of jars (as declared using the JDK1.3 "Optional Pakcages" Spec)
 * are present in classloader.&lt;/li&gt; &lt;li&gt;The locations are resolved to a single
 * base directiory.&lt;/li&gt; &lt;li&gt;The Extensions are not resolved but may be by
 * subclasses.&lt;/li&gt; &lt;li&gt;FileSets are currently unsupported and throw a {@link
 * UnsupportedOperationException} if attempt to be constructed.&lt;/li&gt; &lt;/ul&gt;
 *
 * @author Peter Donald
 * @version $Revision: 1.2 $ $Date: 2004/04/21 01:39:59 $
 */
public class SimpleLoaderResolver
    implements LoaderResolver
{
    /** i18n utils for presenting messages. */
    private static final Resources REZ =
        ResourceManager.getPackageResources( SimpleLoaderResolver.class );

    /** The base directory relative to which to aquire files. */
    private File m_baseDirectory;

    /**
     * Create a resolver that resolves all files according to specied
     * baseDirectory.
     *
     * @param baseDirectory the base directory
     */
    public SimpleLoaderResolver( final File baseDirectory )
    {
        setBaseDirectory( baseDirectory );
    }

    /**
     * Retrieve a URL for specified extension.
     *
     * @param extension the extension
     * @return the URL
     * @throws Exception if unable to locate URL for extension
     */
    public URL resolveExtension( final Extension extension )
        throws Exception
    {
        throw new UnsupportedOperationException();
    }

    /**
     * Resolve a location to a particular URL.
     *
     * @param location the location
     * @return the URL
     * @throws Exception if unable to resolve location
     */
    public URL resolveURL( final String location )
        throws Exception
    {
        final File file = getFileFor( location );
        String url = file.toURL().toString();
        if( file.isDirectory() )
        {
            url += "/";
        }
        return new URL( url );
    }

    /**
     * Resolve a fileset.
     *
     * @param baseDirectory the base directory of fileset
     * @param includes the list of ant-style includes
     * @param excludes the list of ant style excludes
     * @return the URLs contained within fileset
     * @throws Exception if unable to resolve fileset
     */
    public URL[] resolveFileSet( final String baseDirectory,
                                 final String[] includes,
                                 final String[] excludes )
        throws Exception
    {
        final File base = getFileFor( "." );
        return resolveFileSet( base, baseDirectory, includes, excludes );
    }

    /**
     * Create a Join ClassLoader for specified ClassLoaders. Use {@link
     * JoinClassLoader} to implement functionality.
     *
     * @param classLoaders the ClassLoaders to "join"
     * @return the joined ClassLoader
     * @throws Exception if unable to create classloader
     */
    public ClassLoader createJoinClassLoader( final ClassLoader[] classLoaders )
        throws Exception
    {
        return new JoinClassLoader( classLoaders,
                                    ClassLoader.getSystemClassLoader() );
    }

    /**
     * Create a ClassLoader with specified parent and containing specified URLs.
     * This implementation just creates it using the default URLClassLoader.
     *
     * @param parent the parent classloader
     * @param urls the URLs that the ClassLoader should contain
     * @return the newly created ClassLoader
     * @throws Exception if unable to create classloader
     */
    public ClassLoader createClassLoader( final ClassLoader parent,
                                          final URL[] urls )
        throws Exception
    {
        final URL[] classpath = determineCompleteClasspath( urls );
        return new URLClassLoader( classpath, parent );
    }

    /**
     * Retrieve the complete classpath given an input set of URLs. The complete
     * classpath includes all URLs for extensions required by the jars
     * (according to the "Optional Package" Spec).
     *
     * @param urls the urls
     * @return the complete set of URLs for classpath
     * @throws Exception if unable to determine complete classpath set
     */
    protected final URL[] determineCompleteClasspath( final URL[] urls )
        throws Exception
    {
        final ArrayList classpathSet = new ArrayList();

        //Add all supplied URLS to classpath
        for( int i = 0; i &lt; urls.length; i++ )
        {
            final URL url = urls[ i ];
            classpathSet.add( url );
        }

        //Add all the optional packages that are declared as
        // dependencies of class path elements
        final File[] files = getOptionalPackagesFor( urls );
        for( int i = 0; i &lt; files.length; i++ )
        {
            final File file = files[ i ];
            classpathSet.add( file.toURL() );
        }

        //Define final classpath with all dependencies added
        return (URL[])classpathSet.toArray( new URL[ classpathSet.size() ] );
    }

    /**
     * Utility class to retrieve a file object for specified location.
     *
     * @param location which to get file for.
     * @return the file for specified location
     */
    protected File getFileFor( final String location )
        throws IOException
    {
        File base = getBaseDirectory();
        if( null == base )
        {
            base = new File( "." );
        }

        return new File( base, location ).getCanonicalFile();
    }

    /**
     * Return the base directory against which to resolve relative files.
     *
     * @return the base directory against which to resolve relative files.
     */
    protected File getBaseDirectory()
    {
        return m_baseDirectory;
    }

    /**
     * Set the base directory.
     *
     * @param baseDirectory the base directory.
     */
    protected void setBaseDirectory( File baseDirectory )
    {
        m_baseDirectory = baseDirectory;
    }

    /**
     * Retrieve the files for the optional packages required by the jars in
     * ClassPath.
     *
     * @param classPath the Classpath array
     * @return the files that need to be added to ClassLoader
     */
    protected final File[] getOptionalPackagesFor( final URL[] classPath )
        throws Exception
    {
        final Manifest[] manifests = getManifests( classPath );
        final Extension[] available = getAvailable( manifests );
        final Extension[] required = Extension.getRequired( manifests );

        if( isDebugEnabled() )
        {
            final String message1 =
                REZ.format( "available-extensions",
                            Arrays.asList( available ) );
            debug( message1 );
            final String message2 =
                REZ.format( "required-extensions",
                            Arrays.asList( required ) );
            debug( message2 );
        }

        if( 0 == required.length )
        {
            return new File[ 0 ];
        }

        final Set dependencies = new HashSet();
        final Set unsatisfied = new HashSet();

        scanDependencies( required,
                          available,
                          dependencies,
                          unsatisfied );

        final int size = unsatisfied.size();
        if( 0 != size )
        {
            final Iterator iterator = unsatisfied.iterator();
            while( iterator.hasNext() )
            {
                final Extension extension = (Extension)iterator.next();
                final Object[] params = new Object[]
                {
                    extension.getExtensionName(),
                    extension.getSpecificationVendor(),
                    extension.getSpecificationVersion(),
                    extension.getImplementationVendor(),
                    extension.getImplementationVendorID(),
                    extension.getImplementationVersion(),
                    extension.getImplementationURL()
                };
                final String message = REZ.format( "missing.extension",
                                                   params );
                warn( message );
            }

            final String message =
                REZ.format( "unsatisfied.extensions", new Integer( size ) );
            throw new Exception( message );
        }

        if( isDebugEnabled() )
        {
            final String message =
                REZ.format( "optional-packages-added", dependencies );
            debug( message );
        }

        return (File[])dependencies.toArray( new File[ dependencies.size() ] );
    }


    /**
     * Retrieve the set of &lt;code&gt;Extension&lt;/code&gt; objects that are available by
     * the specified Manifest objects. If there are no such optional packages, a
     * zero-length list is returned.
     *
     * @param manifests the manifests to scan
     * @return the extensions
     */
    private Extension[] getAvailable( final Manifest[] manifests )
    {
        final ArrayList set = new ArrayList();
        for( int i = 0; i &lt; manifests.length; i++ )
        {
            try
            {
                final Extension[] extensions = Extension.getAvailable( manifests[ i ] );
                for( int j = 0; j &lt; extensions.length; j++ )
                {
                    set.add( extensions[ j ] );
                }
            }
            catch( final Exception e )
            {
                final String message =
                    REZ.format( "malformed.extension", manifests[ i ] );
                warn( message, e );
            }
        }
        return (Extension[])set.toArray( new Extension[ set.size() ] );
    }

    /**
     * Attempt to locate a set of dependencies that transitively satisfy all
     * required extensions.
     *
     * @param required the required extensions
     * @param available the extensions already available
     * @param dependencies the set of dependencies collected by this method
     * @param unsatisfied this method should place any unsatisfied depenencies
     * into this set
     */
    protected void scanDependencies( final Extension[] required,
                                     final Extension[] available,
                                     final Set dependencies,
                                     final Set unsatisfied )
    {
        throw new UnsupportedOperationException();
    }

    /**
     * write out a warning message. Subclasses may overide this method to
     * redirect logging as appropriate.
     *
     * @param message the warning message
     */
    protected void warn( final String message )
    {
    }

    /**
     * write out a warning message. Subclasses may overide this method to
     * redirect logging as appropriate.
     *
     * @param message the warning message
     * @param t the throwable
     */
    protected void warn( final String message, final Throwable t )
    {
    }

    /**
     * Determine if debug messages are turned on. Subclasses should overide this
     * method.
     *
     * @return true if debugging enabled.
     */
    protected boolean isDebugEnabled()
    {
        return false;
    }

    /**
     * write out a debug message. Subclasses may overide this method to redirect
     * logging as appropriate.
     *
     * @param message the debug message
     */
    protected void debug( final String message )
    {
    }

    /**
     * Retrieve all the Manifests from the specified Classlpath.
     *
     * @param classPath the classpath
     * @return the set of manifests on the classpath
     * @throws Exception if there is an error reading manifests from files on
     * classpath
     */
    private Manifest[] getManifests( final URL[] classPath )
        throws Exception
    {
        final ArrayList manifests = new ArrayList();

        for( int i = 0; i &lt; classPath.length; i++ )
        {
            final URL element = classPath[ i ];
            if( element.getFile().endsWith( ".jar" ) )
            {
                try
                {
                    final URL url = new URL( "jar:" + element + "!/" );
                    final JarURLConnection connection =
                        (JarURLConnection)url.openConnection();
                    final Manifest manifest = connection.getManifest();
                    if( null != manifest )
                    {
                        manifests.add( manifest );
                    }
                }
                catch( final IOException ioe )
                {
                    final String message =
                        REZ.format( "bad-classpath-entry", element );
                    throw new Exception( message );
                }
            }
        }

        return (Manifest[])manifests.toArray( new Manifest[ 0 ] );
    }

    /**
     * Resolve a fileset in a particular hierarchy.
     *
     * @param base the file hierarchy to use
     * @param baseDirectory the base directory (relative to base)
     * @param includes the ant-style include patterns
     * @param excludes the ant-style exclude patterns
     * @return the resolved URLs for fileset
     */
    protected final URL[] resolveFileSet( final File base,
                                          final String baseDirectory,
                                          final String[] includes,
                                          final String[] excludes )
    {
        //woefully inefficient .. but then again - no need
        //for efficency here
        final String newBaseDirectory = FileUtil.normalize( baseDirectory );
        final String[] newIncludes = prefixPatterns( newBaseDirectory,
                                                     includes );
        final String[] newExcludes = prefixPatterns( newBaseDirectory,
                                                     excludes );
        final PathMatcher matcher = new PathMatcher( newIncludes,
                                                     newExcludes );
        final File[] files = FileUtil.resolveFileSet( base, matcher );
        try
        {
            return FileUtil.toURLs( files );
        }
        catch( IOException ioe )
        {
            throw new IllegalArgumentException( ioe.getMessage() );
        }
    }

    /**
     * Return a new array with specified prefix added to start of every element
     * in supplied array.
     *
     * @param prefix the prefix
     * @param patterns the source array
     * @return a new array with all elements having prefix added
     */
    private String[] prefixPatterns( final String prefix,
                                     final String[] patterns )
    {
        if( 0 == prefix.length() || ".".equals( prefix ) )
        {
            return patterns;
        }

        final String[] newPatterns = new String[ patterns.length ];
        for( int i = 0; i &lt; newPatterns.length; i++ )
        {
            newPatterns[ i ] = prefix + "/" + patterns[ i ];
        }
        return newPatterns;
    }
}
</pre><pre class="diff"><small id="info">\ No newline at end of file
</small></pre></div>
<center><small><a href="http://www.badgers-in-foil.co.uk/projects/cvsspam/" title="commit -&gt; email">CVSspam</a> 0.2.8</small></center>
</body></html>