You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by ad...@apache.org on 2002/04/03 12:58:20 UTC

cvs commit: jakarta-ant/proposal/myrmidon/src/xdocs todo.xml

adammurdoch    02/04/03 02:58:20

  Modified:    proposal/myrmidon build.xml
               proposal/myrmidon/docs todo.html
               proposal/myrmidon/src/java/org/apache/myrmidon/components/classloader
                        DefaultClassLoaderManager.java Resources.properties
               proposal/myrmidon/src/java/org/apache/myrmidon/components/deployer
                        DefaultDeployer.java
               proposal/myrmidon/src/java/org/apache/myrmidon/components/embeddor
                        DefaultEmbeddor.java Resources.properties
               proposal/myrmidon/src/java/org/apache/myrmidon/components/extensions
                        DefaultExtensionManager.java
               proposal/myrmidon/src/java/org/apache/myrmidon/frontends
                        CLIMain.java EmbeddedAnt.java Resources.properties
               proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/classloader
                        ClassLoaderManager.java
               proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/extensions
                        ExtensionManager.java
               proposal/myrmidon/src/test/org/apache/myrmidon
                        AbstractMyrmidonTest.java AbstractProjectTest.java
               proposal/myrmidon/src/test/org/apache/myrmidon/components
                        AbstractComponentTest.java
               proposal/myrmidon/src/test/org/apache/myrmidon/components/embeddor/test
                        DefaultEmbeddorTest.java
               proposal/myrmidon/src/xdocs todo.xml
  Added:       proposal/myrmidon/src/java/org/apache/myrmidon/components/classloader
                        MultiParentURLClassLoader.java
               proposal/myrmidon/src/test/org/apache/myrmidon/components/classloader/test
                        DefaultClassLoaderManagerTestCase.java
               proposal/myrmidon/src/test/org/apache/myrmidon/components/classloader/test/libs
                        cycle-extension-1.mf cycle-extension-2.mf
                        one-dependency.mf simple-extension.mf
               proposal/myrmidon/src/test/org/apache/myrmidon/components/classloader/test/libs/extn
                        ExtnClass.java extn.txt
               proposal/myrmidon/src/test/org/apache/myrmidon/components/classloader/test/libs/shared
                        SharedClass.java shared.txt
               proposal/myrmidon/src/test/org/apache/myrmidon/components/classloader/test/libs/unshared
                        UnsharedClass.java unshared.txt
  Log:
  ClassLoader hierarchy changes:
  
  * Use multi-parent ClassLoaders for antlibs and extensions, so that each
    extension jar is loaded by a single ClassLoader in the hierarchy.  Allows
    classes from extensions to be shared across dependent antlibs and extensions.
  
  * Changed contract of ClassLoaderManager.createClassLoader( File[] ), so that
    it creates a new ClassLoader each time it is called.
  
  * Changed ExtensionManager, so that it no longer extends PackageRepository.
  
  * Added a few test cases for DefaultClassLoaderManager.
  
  * Moved responsibility for checking myrmidon.home and building the various paths,
    from DefaultEmbeddor and DefaultExtensionManager to EmbeddedAnt.  Use the
    platform path separator for the paths, rather than the | char.
  
  * Use EmbeddedAnt in AbstractProjectTest, rather than using an Embeddor directly.
  
  * AbstractComponentTest was not parameterising or initialising the test
    components.
  
  Revision  Changes    Path
  1.102     +56 -1     jakarta-ant/proposal/myrmidon/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/build.xml,v
  retrieving revision 1.101
  retrieving revision 1.102
  diff -u -r1.101 -r1.102
  --- build.xml	2 Apr 2002 12:06:37 -0000	1.101
  +++ build.xml	3 Apr 2002 10:58:18 -0000	1.102
  @@ -67,6 +67,7 @@
       <property name="test.dir" value="${build.dir}/test"/>
       <property name="test.working.dir" value="${test.dir}/test"/>
       <property name="test.classes" value="${test.dir}/classes"/>
  +    <property name="test.fork" value="true"/>
   
       <property name="constants.file" value="org/apache/myrmidon/Constants.java"/>
   
  @@ -544,6 +545,9 @@
   
       <!-- Compiles and runs the unit tests -->
       <target name="run-tests" depends="dist-lite" if="junit.present">
  +
  +        <property name="test.classloader.pkg" value="org/apache/myrmidon/components/classloader/test/libs"/>
  +
           <!-- Compile the unit tests -->
           <mkdir dir="${test.classes}"/>
           <javac srcdir="src/test"
  @@ -558,6 +562,7 @@
   
               <exclude name="**/SmbFileSystemTestCase.java" unless="jcifs.present"/>
               <exclude name="**/FtpFileSystemTestCase.java" unless="netcomp.present"/>
  +            <exclude name="${test.classloader.pkg}/**"/>
           </javac>
   
           <!-- Prepare test files -->
  @@ -602,6 +607,56 @@
               <fileset dir="${test.classes}" includes="org/apache/myrmidon/interfaces/type/test/MyType1.class"/>
           </jar>
   
  +        <!-- Prepare the class loader manager tests -->
  +        <property name="test.classloader.dir" value="${test.working.dir}/${test.classloader.pkg}/.."/>
  +        <property name="test.classloader.classes" value="${test.dir}/classloader"/>
  +        <mkdir dir="${test.classloader.dir}"/>
  +        <mkdir dir="${test.classloader.dir}/ext"/>
  +        <mkdir dir="${test.classloader.classes}"/>
  +        <javac srcdir="src/test"
  +            destdir="${test.classloader.classes}"
  +            debug="${debug}"
  +            deprecation="${deprecation}">
  +            <include name="${test.classloader.pkg}/**"/>
  +        </javac>
  +        <copy todir="${test.classloader.classes}">
  +            <fileset dir="src/test">
  +                <include name="${test.classloader.pkg}/**"/>
  +                <exclude name="**/*.java"/>
  +            </fileset>
  +        </copy>
  +        <jar jarfile="${test.classloader.dir}/common.jar">
  +            <fileset dir="${test.classloader.classes}">
  +                <include name="**/shared/**"/>
  +            </fileset>
  +        </jar>
  +        <jar jarfile="${test.classloader.dir}/no-dependencies.jar">
  +            <fileset dir="${test.classloader.classes}">
  +                <include name="**/shared/**"/>
  +                <include name="**/unshared/**"/>
  +            </fileset>
  +        </jar>
  +        <jar jarfile="${test.classloader.dir}/one-dependency.jar"
  +             manifest="src/test/${test.classloader.pkg}/one-dependency.mf">
  +            <fileset dir="${test.classloader.classes}">
  +                <include name="**/shared/**"/>
  +                <include name="**/unshared/**"/>
  +            </fileset>
  +        </jar>
  +        <copy file="${test.classloader.dir}/one-dependency.jar"
  +              tofile="${test.classloader.dir}/one-dependency-2.jar"/>
  +        <jar jarfile="${test.classloader.dir}/ext/simple-extension.jar"
  +             manifest="src/test/${test.classloader.pkg}/simple-extension.mf" >
  +            <fileset dir="${test.classloader.classes}">
  +                <include name="**/shared/**"/>
  +                <include name="**/extn/**"/>
  +            </fileset>
  +        </jar>
  +        <jar jarfile="${test.classloader.dir}/ext/cycle-extension-1.jar"
  +             manifest="src/test/${test.classloader.pkg}/cycle-extension-1.mf" />
  +        <jar jarfile="${test.classloader.dir}/ext/cycle-extension-2.jar"
  +             manifest="src/test/${test.classloader.pkg}/cycle-extension-2.mf" />
  +
           <!-- Prepare the project tests -->
           <antlib-descriptor libName="unittests"
               destdir="${gen.dir}"
  @@ -616,7 +671,7 @@
   
           <!-- Run all the tests -->
           <junit printsummary="on"
  -               fork="true" failureProperty="test.failed">
  +               fork="${test.fork}" failureProperty="test.failed">
               <formatter type="brief" usefile="false"/>
               <classpath>
                   <fileset dir="${test.working.dir}/dist/bin/lib" includes="**/*.jar"/>
  
  
  
  1.23      +13 -11    jakarta-ant/proposal/myrmidon/docs/todo.html
  
  Index: todo.html
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/docs/todo.html,v
  retrieving revision 1.22
  retrieving revision 1.23
  diff -u -r1.22 -r1.23
  --- todo.html	1 Apr 2002 00:46:26 -0000	1.22
  +++ todo.html	3 Apr 2002 10:58:18 -0000	1.23
  @@ -837,23 +837,12 @@
                           <code>&lt;socket&gt;</code>
                       conditions to an antlib.  Need to resolve how these will be passed a logger.
                       </li>
  -                    <li>Make the
  -                        <code>&lt;uptodate&gt;</code> task a condition, and move to
  -                    an antlib.
  -                    </li>
  -                    <li>Split up
  -                        <code>&lt;is-set&gt;</code> condition into is-set and is-true conditions.
  -                    </li>
                       <li>Allow the
                           <code>&lt;if&gt;</code> task to take any condition implementation.
                       </li>
                       <li>Add an else block to the
                           <code>&lt;if&gt;</code> task.
                       </li>
  -                    <li>Split the
  -                        <code>&lt;available&gt;</code> condition into separate conditions
  -                    that test for the availability of a class, or a resource.
  -                    </li>
                       <li>Move
                           <code>crimson.jar</code> to
                           <code>bin/lib</code> in the distribution,
  @@ -863,6 +852,19 @@
                       <li>Add a <code>--type</code> command-line option, to allow
                           the project builder to be manually selected.
                       </li>
  +                    <li>Change <code>ProjectBuilder</code>
  +                        and <code>Embeddor</code> to throw something more
  +                        specialised than Exception.
  +                    </li>
  +                    <li>Change <code>DefaultClassLoaderManager</code> to handle
  +                        directories as part of a library classpath.
  +                    </li>
  +                    <li><code>&lt;condition&gt;</code> should set the property
  +                    value to <code>false</code> when the condition is false.</li>
  +                    <li>Split the <code>&lt;uptodate&gt;</code> condition into
  +                    a condition that checks against a single target file,
  +                    and one which checks using a destdir/mapper.</li>
  +                    <li>Add a task to unset a property.</li>
                       <li>Unit tests.</li>
                   </ul>
                       </blockquote>
  
  
  
  1.5       +154 -87   jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/classloader/DefaultClassLoaderManager.java
  
  Index: DefaultClassLoaderManager.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/classloader/DefaultClassLoaderManager.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- DefaultClassLoaderManager.java	29 Mar 2002 12:56:03 -0000	1.4
  +++ DefaultClassLoaderManager.java	3 Apr 2002 10:58:19 -0000	1.5
  @@ -8,19 +8,17 @@
   package org.apache.myrmidon.components.classloader;
   
   import java.io.File;
  -import java.net.JarURLConnection;
   import java.net.MalformedURLException;
   import java.net.URL;
  -import java.net.URLClassLoader;
   import java.util.ArrayList;
  -import java.util.Arrays;
   import java.util.HashMap;
   import java.util.Map;
  -import java.util.List;
  +import java.util.Set;
  +import java.util.HashSet;
   import java.util.jar.Manifest;
  +import java.util.jar.JarFile;
   import org.apache.avalon.excalibur.extension.Extension;
   import org.apache.avalon.excalibur.extension.OptionalPackage;
  -import org.apache.avalon.excalibur.extension.PackageManager;
   import org.apache.avalon.excalibur.i18n.ResourceManager;
   import org.apache.avalon.excalibur.i18n.Resources;
   import org.apache.avalon.framework.activity.Initializable;
  @@ -28,8 +26,8 @@
   import org.apache.avalon.framework.service.ServiceException;
   import org.apache.avalon.framework.service.ServiceManager;
   import org.apache.avalon.framework.service.Serviceable;
  -import org.apache.myrmidon.interfaces.classloader.ClassLoaderManager;
   import org.apache.myrmidon.interfaces.classloader.ClassLoaderException;
  +import org.apache.myrmidon.interfaces.classloader.ClassLoaderManager;
   import org.apache.myrmidon.interfaces.deployer.DeploymentException;
   import org.apache.myrmidon.interfaces.extensions.ExtensionManager;
   import org.apache.tools.todo.types.PathUtil;
  @@ -38,6 +36,7 @@
    * A default implementation of a ClassLoader manager.
    *
    * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
  + * @version $Revision: 1.5 $ $Date: 2002/04/03 10:58:19 $
    */
   public class DefaultClassLoaderManager
       extends AbstractLogEnabled
  @@ -47,13 +46,22 @@
           ResourceManager.getPackageResources( DefaultClassLoaderManager.class );
   
       /**
  -     * Map from File to the ClassLoader for that file.
  +     * Map from File/ArrayList to the ClassLoader for that file/files.
        */
  -    private final Map m_fileDeployers = new HashMap();
  +    private final Map m_classLoaders = new HashMap();
   
  -    private PackageManager m_packageManager;
  +    private ExtensionManager m_extensionManager;
       private ClassLoader m_commonClassLoader;
   
  +    public DefaultClassLoaderManager()
  +    {
  +    }
  +
  +    public DefaultClassLoaderManager( final ClassLoader commonClassLoader )
  +    {
  +        m_commonClassLoader = commonClassLoader;
  +    }
  +
       public void initialize() throws Exception
       {
           if( null == m_commonClassLoader )
  @@ -68,18 +76,7 @@
       public void service( final ServiceManager serviceManager )
           throws ServiceException
       {
  -        final ExtensionManager extensionManager =
  -            (ExtensionManager)serviceManager.lookup( ExtensionManager.ROLE );
  -        m_packageManager = new PackageManager( extensionManager );
  -    }
  -
  -    /**
  -     * Sets the ClassLoader to use as the parent for all classloaders
  -     * created by this ClassLoader manager.
  -     */
  -    public void setCommonClassLoader( final ClassLoader classLoader )
  -    {
  -        m_commonClassLoader = classLoader;
  +        m_extensionManager = (ExtensionManager)serviceManager.lookup( ExtensionManager.ROLE );
       }
   
       /**
  @@ -94,9 +91,27 @@
       /**
        * Creates a class loader for a Jar file.
        */
  -    public ClassLoader createClassLoader( final File file ) throws ClassLoaderException
  +    public ClassLoader getClassLoader( final File file ) throws ClassLoaderException
       {
  -        return createClassLoader( new File[] { file } );
  +        try
  +        {
  +            final File canonFile = file.getCanonicalFile();
  +
  +            // Check for cached classloader, creating it if required
  +            ClassLoader loader = (ClassLoader)m_classLoaders.get( canonFile );
  +            if( loader == null )
  +            {
  +                checkFile( canonFile );
  +                final OptionalPackage optionalPackage = toOptionalPackage( canonFile );
  +                loader = buildClassLoader( optionalPackage, new HashSet() );
  +            }
  +            return loader;
  +        }
  +        catch( final Exception e )
  +        {
  +            final String message = REZ.getString( "create-classloader-for-file.error", file );
  +            throw new ClassLoaderException( message, e );
  +        }
       }
   
       /**
  @@ -106,113 +121,165 @@
       {
           try
           {
  -            // Build a list of canonical file names
  -            final ArrayList canonFiles = new ArrayList( files.length );
  -            for( int i = 0; i < files.length; i++ )
  +            if( files == null || files.length == 0 )
               {
  -                canonFiles.add( files[ i ].getCanonicalFile() );
  +                return m_commonClassLoader;
               }
   
  -            // Locate cached classloader, creating it if necessary
  -            ClassLoader classLoader = (ClassLoader)m_fileDeployers.get( canonFiles );
  -            if( classLoader == null )
  +            // Build a list of optional packages for the files
  +            final OptionalPackage[] packages = new OptionalPackage[ files.length ];
  +            for( int i = 0; i < files.length; i++ )
               {
  -                classLoader = buildClassLoader( canonFiles );
  -                m_fileDeployers.put( canonFiles, classLoader );
  +                final File canonFile = files[ i ].getCanonicalFile();
  +                checkFile( canonFile );
  +                packages[ i ] = toOptionalPackage( canonFile );
               }
  -            return classLoader;
  +
  +            // Build the classloaders for the required extensions
  +            final ClassLoader[] parentClassLoaders = buildParentClassLoaders( packages, new HashSet() );
  +
  +            // Build the classloader
  +            final URL[] urls = buildClasspath( files );
  +            return new MultiParentURLClassLoader( urls, parentClassLoaders );
           }
           catch( final Exception e )
           {
               final String fileNames = PathUtil.formatPath( files );
  -            final String message = REZ.getString( "create-classloader-for-file.error", fileNames );
  +            final String message = REZ.getString( "create-classloader-for-files.error", fileNames );
               throw new ClassLoaderException( message, e );
           }
       }
   
       /**
  -     * Builds the classloader for a set of files.
  +     * Builds the classloader for an optional package.
  +     */
  +    private ClassLoader buildClassLoader( final OptionalPackage pkg,
  +                                          final Set pending )
  +        throws Exception
  +    {
  +        final File jarFile = pkg.getFile();
  +
  +        // Check for cached classloader
  +        ClassLoader classLoader = (ClassLoader)m_classLoaders.get( jarFile );
  +        if( classLoader != null )
  +        {
  +            return classLoader;
  +        }
  +
  +        // Check for cyclic dependency
  +        if( pending.contains( jarFile ) )
  +        {
  +            final String message = REZ.getString( "dependency-cycle.error", jarFile );
  +            throw new Exception( message );
  +        }
  +        pending.add( jarFile );
  +
  +        // Build the classloaders for the extensions required by this optional
  +        // package
  +        final ClassLoader[] parentClassLoaders =
  +            buildParentClassLoaders( new OptionalPackage[] { pkg }, pending );
  +
  +        // Create and cache the classloader
  +        final URL[] urls = { jarFile.toURL() };
  +        classLoader = new MultiParentURLClassLoader( urls, parentClassLoaders );
  +        m_classLoaders.put( jarFile, classLoader );
  +        pending.remove( jarFile );
  +        return classLoader;
  +    }
  +
  +    /**
  +     * Builds the parent classloaders for a set of optional packages.  That is,
  +     * the classloaders for all of the extensions required by the given set
  +     * of optional packages.
        */
  -    private ClassLoader buildClassLoader( final ArrayList files )
  +    private ClassLoader[] buildParentClassLoaders( final OptionalPackage[] packages,
  +                                                   final Set pending )
           throws Exception
       {
  -        final ArrayList allFiles = new ArrayList( files );
  -        final int count = files.size();
  -        for( int i = 0; i < count; i++ )
  +        final ArrayList classLoaders = new ArrayList();
  +
  +        // Include the common class loader
  +        classLoaders.add( m_commonClassLoader );
  +
  +        // Build the classloader for each optional package, filtering out duplicates
  +        for( int i = 0; i < packages.length; i++ )
           {
  -            final File file = (File)files.get(i );
  -            checkFile( file );
  -            getOptionalPackagesFor( file, allFiles );
  +            final OptionalPackage optionalPackage = packages[ i ];
  +
  +            // Locate the dependencies for this jar file
  +            final OptionalPackage[] requiredPackages = getOptionalPackagesFor( optionalPackage );
  +
  +            // Build the classloader for the package
  +            for( int j = 0; j < requiredPackages.length; j++ )
  +            {
  +                final OptionalPackage requiredPackage = requiredPackages[j ];
  +                final ClassLoader classLoader = buildClassLoader( requiredPackage, pending );
  +                if( ! classLoaders.contains( classLoader ) )
  +                {
  +                    classLoaders.add( classLoader );
  +                }
  +            }
           }
   
  -        final URL[] urls = buildClasspath( allFiles );
  -        return new URLClassLoader( urls, m_commonClassLoader );
  +        return (ClassLoader[])classLoaders.toArray( new ClassLoader[classLoaders.size() ] );
       }
   
       /**
        * Assembles a set of files into a URL classpath.
        */
  -    private URL[] buildClasspath( final ArrayList files )
  +    private URL[] buildClasspath( final File[] files )
           throws MalformedURLException
       {
  -        final URL[] urls = new URL[ files.size() ];
  -        final int count = files.size();
  -        for( int i = 0; i < count; i++ )
  +        final URL[] urls = new URL[ files.length ];
  +        for( int i = 0; i < files.length; i++ )
           {
  -            final File file = (File)files.get( i );
  -            urls[ i ] = file.toURL();
  +            urls[ i ]  = files[i ].toURL();
           }
   
           return urls;
       }
   
       /**
  -     * Retrieve the files for the optional packages required by
  -     * the specified typeLibrary jar.
  +     * Builds an OptionalPackage for a Jar file.
        *
  -     * @param jarFile the typeLibrary
  -     * @param packages used to return the files that need to be added to ClassLoader.
  +     * @param file the jar.
        */
  -    private void getOptionalPackagesFor( final File jarFile, final List packages )
  +    private OptionalPackage toOptionalPackage( final File file )
           throws Exception
       {
  -        final URL url = new URL( "jar:" + jarFile.getCanonicalFile().toURL() + "!/" );
  -        final JarURLConnection connection = (JarURLConnection)url.openConnection();
  -        final Manifest manifest = connection.getManifest();
  -        final Extension[] available = Extension.getAvailable( manifest );
  +        // Determine the extensions required by this file
  +        final JarFile jarFile = new JarFile( file );
  +        final Manifest manifest = jarFile.getManifest();
           final Extension[] required = Extension.getRequired( manifest );
  +        return new OptionalPackage( file, new Extension[0], required );
  +    }
   
  -        if( getLogger().isDebugEnabled() )
  -        {
  -            final String message1 =
  -                REZ.getString( "available-extensions.notice", Arrays.asList( available ) );
  -            getLogger().debug( message1 );
  -            final String message2 =
  -                REZ.getString( "required-extensions.notice", Arrays.asList( required ) );
  -            getLogger().debug( message2 );
  -        }
  -
  -        final ArrayList dependencies = new ArrayList();
  -        final ArrayList unsatisfied = new ArrayList();
  -
  -        m_packageManager.scanDependencies( required,
  -                                           available,
  -                                           dependencies,
  -                                           unsatisfied );
  -
  -        if( 0 != unsatisfied.size() )
  +    /**
  +     * Locates the optional packages required by an optional package.
  +     */
  +    private OptionalPackage[] getOptionalPackagesFor( final OptionalPackage pkg )
  +        throws Exception
  +    {
  +        // Locate the optional packages that provide the required extesions
  +        final Extension[] required = pkg.getRequiredExtensions();
  +        final ArrayList packages = new ArrayList();
  +        for( int i = 0; i < required.length; i++ )
           {
  -            final String message =
  -                REZ.getString( "unsatisfied.extensions.error", new Integer( unsatisfied.size() ) );
  -            throw new Exception( message );
  +            final Extension extension = required[i ];
  +            final OptionalPackage optionalPackage = m_extensionManager.getOptionalPackage( extension );
  +            if( optionalPackage == null )
  +            {
  +                final String message =
  +                    REZ.getString( "unsatisfied.extension.error",
  +                                   pkg.getFile(),
  +                                   extension.getExtensionName(),
  +                                   extension.getSpecificationVersion() );
  +                throw new Exception( message );
  +            }
  +            packages.add( optionalPackage );
           }
   
  -        final int count = dependencies.size();
  -        for( int i = 0; i < count; i++ )
  -        {
  -            final OptionalPackage optionalPackage = (OptionalPackage)dependencies.get(i );
  -            packages.add( optionalPackage.getFile() );
  -        }
  +        return (OptionalPackage[])packages.toArray( new OptionalPackage[packages.size() ] );
       }
   
       /**
  
  
  
  1.3       +5 -6      jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/classloader/Resources.properties
  
  Index: Resources.properties
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/classloader/Resources.properties,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- Resources.properties	27 Mar 2002 07:04:16 -0000	1.2
  +++ Resources.properties	3 Apr 2002 10:58:19 -0000	1.3
  @@ -1,6 +1,5 @@
  -create-classloader-for-file.error=Could not create ClassLoader for files: {0}.
  -available-extensions.notice=The list of available extensions for type library includes; {0}
  -required-extensions.notice=The list of required extensions for type library includes; {0}
  -unsatisfied.extensions.error=Missing {0} extensions for type library.
  -no-file.error=Could not find type library "{0}".
  -file-is-dir.error=Type library "{0}" is a directory.
  +create-classloader-for-file.error=Could not create a ClassLoader for "{0}".
  +create-classloader-for-files.error=Could not create a ClassLoader for {0}.
  +unsatisfied.extension.error=Library "{0}" requires unknown extension "{1}" ( version {2}).
  +no-file.error=Could not find library "{0}".
  +file-is-dir.error=Library "{0}" is a directory.
  
  
  
  1.1                  jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/classloader/MultiParentURLClassLoader.java
  
  Index: MultiParentURLClassLoader.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included  with this distribution in
   * the LICENSE.txt file.
   */
  package org.apache.myrmidon.components.classloader;
  
  import java.io.IOException;
  import java.net.URL;
  import java.net.URLClassLoader;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Enumeration;
  import java.util.List;
  import java.util.Set;
  import java.util.HashSet;
  
  /**
   * A URLClassLoader with more than one parent.
   *
   * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
   * @version $Revision: 1.1 $ $Date: 2002/04/03 10:58:19 $
   */
  public class MultiParentURLClassLoader
      extends URLClassLoader
  {
      private final ClassLoader[] m_parents;
  
      /**
       * Constructs a new URLClassLoader for the given URLs.
       *
       * @param urls the URLs from which to load classes and resources
       * @param parents the parent class loaderer for delegation
       */
      public MultiParentURLClassLoader( final URL[] urls, final ClassLoader[] parents )
      {
          super( urls );
          m_parents = parents;
      }
  
      /**
       * Finds a class.
       *
       * @param name the name of the class
       * @return the resulting class
       * @exception ClassNotFoundException if the class could not be found
       */
      protected Class findClass( final String name )
          throws ClassNotFoundException
      {
          // Try the parent classloaders first
          for( int i = 0; i < m_parents.length; i++ )
          {
              try
              {
                  final ClassLoader parent = m_parents[ i ];
                  return parent.loadClass( name );
              }
              catch( ClassNotFoundException e )
              {
                  // Ignore - continue to the next ClassLoader
              }
          }
  
          // Now this classloader
          return super.findClass( name );
      }
  
      /**
       * Finds a resource.
       *
       * @param name the name of the resource
       * @return a <code>URL</code> for the resource, or <code>null</code>
       * if the resource could not be found.
       */
      public URL findResource( final String name )
      {
          // Try the parent classloaders first
          for( int i = 0; i < m_parents.length; i++ )
          {
              final ClassLoader parent = m_parents[ i ];
              final URL resource = parent.getResource( name );
              if( resource != null )
              {
                  return resource;
              }
          }
  
          // Now this classloader
          return super.findResource( name );
      }
  
      /**
       * Returns an Enumeration of URLs representing all of the resources
       * having the specified name.
       *
       * @param name the resource name
       * @throws IOException if an I/O exception occurs
       * @return an <code>Enumeration</code> of <code>URL</code>s
       */
      public Enumeration findResources( final String name ) throws IOException
      {
          // Need to filter out duplicate resources
          final ArrayList urls = new ArrayList();
          final Set urlSet = new HashSet();
  
          // Gather the resources from the parent classloaders
          for( int i = 0; i < m_parents.length; i++ )
          {
              final ClassLoader parent = m_parents[ i ];
              final Enumeration enum = parent.getResources( name );
              addUrls( enum, urls, urlSet );
          }
  
          // Gather the resources from this classloader
          addUrls( super.findResources( name ), urls, urlSet );
  
          return Collections.enumeration( urls );
      }
  
      /**
       * Adds those URLs not already present.
       */
      private void addUrls( final Enumeration enum,
                            final List urls,
                            final Set urlSet )
      {
          while( enum.hasMoreElements() )
          {
              final URL url = (URL)enum.nextElement();
              final String urlStr = url.toExternalForm();
              if( !urlSet.contains( urlStr ) )
              {
                  urls.add( url );
                  urlSet.add( urlStr );
              }
          }
      }
  }
  
  
  
  1.36      +2 -2      jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/deployer/DefaultDeployer.java
  
  Index: DefaultDeployer.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/deployer/DefaultDeployer.java,v
  retrieving revision 1.35
  retrieving revision 1.36
  diff -u -r1.35 -r1.36
  --- DefaultDeployer.java	29 Mar 2002 12:56:03 -0000	1.35
  +++ DefaultDeployer.java	3 Apr 2002 10:58:19 -0000	1.36
  @@ -36,7 +36,7 @@
    *
    * @author <a href="mailto:peter@apache.org">Peter Donald</a>
    * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
  - * @version $Revision: 1.35 $ $Date: 2002/03/29 12:56:03 $
  + * @version $Revision: 1.36 $ $Date: 2002/04/03 10:58:19 $
    */
   public class DefaultDeployer
       extends AbstractLogEnabled
  @@ -108,7 +108,7 @@
       {
           try
           {
  -            final ClassLoader classLoader = m_classLoaderManager.createClassLoader( file );
  +            final ClassLoader classLoader = m_classLoaderManager.getClassLoader( file );
               return createDeployment( classLoader, file.toURL() );
           }
           catch( final Exception e )
  
  
  
  1.41      +3 -105    jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/embeddor/DefaultEmbeddor.java
  
  Index: DefaultEmbeddor.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/embeddor/DefaultEmbeddor.java,v
  retrieving revision 1.40
  retrieving revision 1.41
  diff -u -r1.40 -r1.41
  --- DefaultEmbeddor.java	1 Apr 2002 09:56:26 -0000	1.40
  +++ DefaultEmbeddor.java	3 Apr 2002 10:58:19 -0000	1.41
  @@ -52,7 +52,7 @@
    * Instantiate this to embed inside other applications.
    *
    * @author <a href="mailto:peter@apache.org">Peter Donald</a>
  - * @version $Revision: 1.40 $ $Date: 2002/04/01 09:56:26 $
  + * @version $Revision: 1.41 $ $Date: 2002/04/03 10:58:19 $
    */
   public class DefaultEmbeddor
       extends AbstractLogEnabled
  @@ -71,10 +71,7 @@
       private List m_components = new ArrayList();
       private DefaultServiceManager m_serviceManager = new DefaultServiceManager();
       private Parameters m_parameters;
  -    private Parameters m_defaults;
   
  -    private File m_homeDir;
  -    private File m_taskLibDir;
       private static final String MYRMIDON_HOME = "myrmidon.home";
   
       /**
  @@ -163,9 +160,6 @@
       public void initialize()
           throws Exception
       {
  -        // setup default properties
  -        m_defaults = createDefaultParameters();
  -
           // setup the root components
           setupComponents();
   
  @@ -183,9 +177,6 @@
           m_workspaceServiceManager = new MultiSourceServiceManager();
           m_workspaceServiceManager.add( projServiceManager );
           m_workspaceServiceManager.add( m_serviceManager );
  -
  -        // setup
  -        setupFiles();
       }
   
       public void start()
  @@ -198,7 +189,8 @@
   
           // Deploy all type libraries in the lib directory
           final ExtensionFileFilter filter = new ExtensionFileFilter( ".atl" );
  -        deployFromDirectory( m_deployer, m_taskLibDir, filter );
  +        final File taskLibDir = new File( m_parameters.getParameter( "myrmidon.lib.path" ) );
  +        deployFromDirectory( m_deployer, taskLibDir, filter );
       }
   
       /**
  @@ -230,26 +222,6 @@
           m_deployer = null;
           m_serviceManager = null;
           m_parameters = null;
  -        m_defaults = null;
  -        m_homeDir = null;
  -        m_taskLibDir = null;
  -    }
  -
  -    /**
  -     * Create default properties which includes default names of all components.
  -     * Overide this in sub-classes to change values.
  -     *
  -     * @return the Parameters
  -     */
  -    private Parameters createDefaultParameters()
  -    {
  -        final Parameters defaults = new Parameters();
  -
  -        //create all the default properties for files/directories
  -        defaults.setParameter( "myrmidon.bin.path", "bin" );
  -        defaults.setParameter( "myrmidon.lib.path", "lib" );
  -
  -        return defaults;
       }
   
       /**
  @@ -294,80 +266,6 @@
           m_serviceManager.put( roleType.getName(), component );
           m_components.add( component );
           return component;
  -    }
  -
  -    /**
  -     * Setup all the files attributes.
  -     */
  -    private void setupFiles()
  -        throws Exception
  -    {
  -        String filepath = null;
  -
  -        filepath = getParameter( MYRMIDON_HOME );
  -        m_homeDir = ( new File( filepath ) ).getAbsoluteFile();
  -        checkDirectory( m_homeDir, "home-dir.name" );
  -
  -        filepath = getParameter( "myrmidon.lib.path" );
  -        m_taskLibDir = resolveDirectory( filepath, "task-lib-dir.name" );
  -    }
  -
  -    /**
  -     * Retrieve value of named property.
  -     * First access passed in properties and then the default properties.
  -     *
  -     * @param name the name of property
  -     * @return the value of property or null
  -     */
  -    private String getParameter( final String name )
  -    {
  -        String value = m_parameters.getParameter( name, null );
  -
  -        if( null == value )
  -        {
  -            value = m_defaults.getParameter( name, null );
  -        }
  -
  -        return value;
  -    }
  -
  -    /**
  -     * Resolve a directory relative to another base directory.
  -     *
  -     * @param dir the base directory
  -     * @param name the relative directory
  -     * @return the created File
  -     * @exception Exception if an error occurs
  -     */
  -    private File resolveDirectory( final String dir, final String name )
  -        throws Exception
  -    {
  -        final File file = FileUtil.resolveFile( m_homeDir, dir );
  -        checkDirectory( file, name );
  -        return file;
  -    }
  -
  -    /**
  -     * Verify file is a directory else throw an exception.
  -     *
  -     * @param file the File
  -     * @param name the name of file type (used in error messages)
  -     */
  -    private void checkDirectory( final File file, final String name )
  -        throws Exception
  -    {
  -        if( !file.exists() )
  -        {
  -            final String nameStr = REZ.getString( name );
  -            final String message = REZ.getString( "file-no-exist.error", nameStr, file );
  -            throw new Exception( message );
  -        }
  -        else if( !file.isDirectory() )
  -        {
  -            final String nameStr = REZ.getString( name );
  -            final String message = REZ.getString( "file-not-dir.error", nameStr, file );
  -            throw new Exception( message );
  -        }
       }
   
       /**
  
  
  
  1.5       +0 -4      jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/embeddor/Resources.properties
  
  Index: Resources.properties
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/embeddor/Resources.properties,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- Resources.properties	11 Mar 2002 06:02:22 -0000	1.4
  +++ Resources.properties	3 Apr 2002 10:58:19 -0000	1.5
  @@ -1,10 +1,6 @@
  -file-no-exist.error={0} ({1}) does not exist.
  -file-not-dir.error={0} ({1}) is not a directory.
   bad-type.error=Object {0} is not an instance of {1}.
   bad-ctor.error=Non-public constructor for {0} {1}.
   no-instantiate.error=Error instantiating class for {0} {1}.
   no-class.error=Could not find the class for {0} ({1}).
   bad-filename.error=Unable to retrieve filename for file {0}.
  -home-dir.name=Myrmidon home directory
  -task-lib-dir.name=Task library directory
   create-project.error=Could not load the project definition from {0}.
  
  
  
  1.8       +26 -7     jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/extensions/DefaultExtensionManager.java
  
  Index: DefaultExtensionManager.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/extensions/DefaultExtensionManager.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- DefaultExtensionManager.java	29 Mar 2002 12:55:04 -0000	1.7
  +++ DefaultExtensionManager.java	3 Apr 2002 10:58:19 -0000	1.8
  @@ -27,7 +27,7 @@
    * PhoenixPackageRepository
    *
    * @author <a href="mailto:peter@apache.org">Peter Donald</a>
  - * @version $Revision: 1.7 $ $Date: 2002/03/29 12:55:04 $
  + * @version $Revision: 1.8 $ $Date: 2002/04/03 10:58:19 $
    */
   public class DefaultExtensionManager
       extends DefaultPackageRepository
  @@ -50,7 +50,6 @@
           File.separator + "lib" + File.separator + "tools.jar";
   
       private Logger m_logger;
  -
       private String m_path;
   
       public DefaultExtensionManager()
  @@ -58,6 +57,11 @@
           super( new File[ 0 ] );
       }
   
  +    public DefaultExtensionManager( final File[] path )
  +    {
  +        super( path );
  +    }
  +
       public void enableLogging( final Logger logger )
       {
           m_logger = logger;
  @@ -66,16 +70,13 @@
       public void parameterize( final Parameters parameters )
           throws ParameterException
       {
  -        final String phoenixHome = parameters.getParameter( "myrmidon.home" );
  -        final String defaultExtPath = phoenixHome + File.separator + "ext";
  -        m_path = parameters.getParameter( "myrmidon.ext.path", defaultExtPath );
  +        m_path = parameters.getParameter( "myrmidon.ext.path" );
       }
   
       public void initialize()
           throws Exception
       {
  -        final String[] pathElements = StringUtil.split( m_path, "|" );
  -
  +        final String[] pathElements = StringUtil.split( m_path, File.pathSeparator );
           final File[] dirs = new File[ pathElements.length ];
           for( int i = 0; i < dirs.length; i++ )
           {
  @@ -86,6 +87,7 @@
   
           scanPath();
   
  +        // Add the JVM's tools.jar as an extension
           final Extension extension = createToolsExtension();
           final File jar = getToolsJar();
           final Extension[] available = new Extension[]{extension};
  @@ -97,6 +99,23 @@
       public void dispose()
       {
           clearCache();
  +    }
  +
  +    /**
  +     * Locates the optional package which best matches a required extension.
  +     *
  +     * @param extension the extension to locate an optional package
  +     * @return the optional package, or null if not found.
  +     */
  +    public OptionalPackage getOptionalPackage( final Extension extension )
  +    {
  +        final OptionalPackage[] packages = getOptionalPackages( extension );
  +
  +        if( null == packages || 0 == packages.length ) return null;
  +
  +        //TODO: Use heurisitic to find which is best package
  +
  +        return packages[ 0 ];
       }
   
       protected void debug( final String message )
  
  
  
  1.37      +4 -4      jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/frontends/CLIMain.java
  
  Index: CLIMain.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/frontends/CLIMain.java,v
  retrieving revision 1.36
  retrieving revision 1.37
  diff -u -r1.36 -r1.37
  --- CLIMain.java	29 Mar 2002 12:56:03 -0000	1.36
  +++ CLIMain.java	3 Apr 2002 10:58:19 -0000	1.37
  @@ -32,7 +32,7 @@
    * to run project.
    *
    * @author <a href="mailto:peter@apache.org">Peter Donald</a>
  - * @version $Revision: 1.36 $ $Date: 2002/03/29 12:56:03 $
  + * @version $Revision: 1.37 $ $Date: 2002/04/03 10:58:19 $
    */
   public class CLIMain
   {
  @@ -277,10 +277,10 @@
                       break;
   
                   case LISTENER_OPT:
  -                    m_embedded.setListener( option.getArgument() );
  +                    m_embedded.setProjectListener( option.getArgument() );
                       break;
                   case NO_PREFIX_OPT:
  -                    m_embedded.setListener( "noprefix" );
  +                    m_embedded.setProjectListener( "noprefix" );
                       break;
   
                   case DEFINE_OPT:
  @@ -310,7 +310,7 @@
           try
           {
               // Set system properties set up by launcher
  -            m_embedded.setEmbeddorProperty( "myrmidon.home", properties.get( "myrmidon.home" ) );
  +            m_embedded.setHomeDirectory( (File)properties.get( "myrmidon.home" ) );
   
               // Command line
               if( !parseCommandLineOptions( args ) )
  
  
  
  1.6       +84 -25    jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/frontends/EmbeddedAnt.java
  
  Index: EmbeddedAnt.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/frontends/EmbeddedAnt.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- EmbeddedAnt.java	29 Mar 2002 12:56:03 -0000	1.5
  +++ EmbeddedAnt.java	3 Apr 2002 10:58:19 -0000	1.6
  @@ -8,8 +8,10 @@
   package org.apache.myrmidon.frontends;
   
   import java.io.File;
  +import java.util.ArrayList;
   import org.apache.avalon.excalibur.i18n.ResourceManager;
   import org.apache.avalon.excalibur.i18n.Resources;
  +import org.apache.avalon.excalibur.io.FileUtil;
   import org.apache.avalon.framework.activity.Disposable;
   import org.apache.avalon.framework.activity.Initializable;
   import org.apache.avalon.framework.activity.Startable;
  @@ -33,7 +35,7 @@
    *
    * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
    * @author <a href="mailto:peter@apache.org">Peter Donald</a>
  - * @version $Revision: 1.5 $ $Date: 2002/03/29 12:56:03 $
  + * @version $Revision: 1.6 $ $Date: 2002/04/03 10:58:19 $
    */
   public class EmbeddedAnt
       extends AbstractLogEnabled
  @@ -47,12 +49,25 @@
       private String m_projectFile = "build.ant";
       private Project m_project;
       private String m_listenerName = "default";
  -    private ProjectListener m_listener;
  +    private ArrayList m_listeners = new ArrayList();
       private Parameters m_workspaceProps = new Parameters();
       private Parameters m_builderProps = new Parameters();
       private Parameters m_embeddorParameters = new Parameters();
       private ClassLoader m_sharedClassLoader;
       private Embeddor m_embeddor;
  +    private File m_homeDir;
  +
  +    /**
  +     * Sets the Myrmidon home directory.  Default is to use the current
  +     * directory.
  +     * 
  +     * @todo Autodetect myrmidon home, rather than using current directory 
  +     *       as the default (which is a dud default).
  +     */
  +    public void setHomeDirectory( final File homeDir )
  +    {
  +        m_homeDir = homeDir.getAbsoluteFile();
  +    }
   
       /**
        * Sets the project file to execute.  Default is 'build.ant'.
  @@ -75,21 +90,20 @@
       }
   
       /**
  -     * Sets the name of the project listener to use.
  +     * Sets the name of the project listener to use.  Set to null to disable
  +     * the project listener.
        */
  -    public void setListener( final String listener )
  +    public void setProjectListener( final String listener )
       {
           m_listenerName = listener;
  -        m_listener = null;
       }
   
       /**
  -     * Sets the project listener to use.
  +     * Adds a project listener.
        */
  -    public void setListener( final ProjectListener listener )
  +    public void addProjectListener( final ProjectListener listener )
       {
  -        m_listenerName = null;
  -        m_listener = listener;
  +        m_listeners.add( listener );
       }
   
       /**
  @@ -99,7 +113,7 @@
       public void setWorkspaceProperty( final String name, final Object value )
       {
           // TODO - Make properties Objects, not Strings
  -        m_workspaceProps.setParameter( name, (String)value );
  +        m_workspaceProps.setParameter( name, value.toString() );
       }
   
       /**
  @@ -109,7 +123,7 @@
       public void setBuilderProperty( final String name, final Object value )
       {
           // TODO - Make properties Objects, not Strings
  -        m_builderProps.setParameter( name, (String)value );
  +        m_builderProps.setParameter( name, value.toString() );
       }
   
       /**
  @@ -145,14 +159,13 @@
   
           checkHomeDir();
   
  -        // Prepare the embeddor, project listener and project model
  +        // Prepare the embeddor, and project model
           final Embeddor embeddor = prepareEmbeddor();
  -        final ProjectListener listener = prepareListener( embeddor );
           final Project project = prepareProjectModel( embeddor );
   
           // Create a new workspace
           final Workspace workspace = embeddor.createWorkspace( m_workspaceProps );
  -        workspace.addProjectListener( listener );
  +        prepareListeners( embeddor, workspace );
   
           //execute the project
           executeTargets( workspace, project, targets );
  @@ -181,7 +194,7 @@
           {
               m_embeddor = null;
               m_project = null;
  -            m_listener = null;
  +            m_listeners.clear();
           }
       }
   
  @@ -213,19 +226,26 @@
        */
       private void checkHomeDir() throws Exception
       {
  -        final String home = m_embeddorParameters.getParameter( "myrmidon.home" );
  -        final File homeDir = ( new File( home ) ).getAbsoluteFile();
  -        if( !homeDir.isDirectory() )
  +        if( m_homeDir == null )
           {
  -            final String message = REZ.getString( "home-not-dir.error", homeDir );
  -            throw new Exception( message );
  +            m_homeDir = new File( "." ).getAbsoluteFile();
           }
  +        checkDirectory( m_homeDir, "home-dir.name" );
  +        m_embeddorParameters.setParameter( "myrmidon.home", m_homeDir.getAbsolutePath() );
   
           if( getLogger().isInfoEnabled() )
           {
  -            final String message = REZ.getString( "homedir.notice", homeDir );
  +            final String message = REZ.getString( "homedir.notice", m_homeDir );
               getLogger().info( message );
           }
  +
  +        String path = m_embeddorParameters.getParameter( "myrmidon.lib.path", "lib" );
  +        File dir = resolveDirectory( m_homeDir, path, "task-lib-dir.name" );
  +        m_embeddorParameters.setParameter( "myrmidon.lib.path", dir.getAbsolutePath() );
  +
  +        path = m_embeddorParameters.getParameter( "myrmidon.ext.path", "ext" );
  +        dir = resolveDirectory( m_homeDir, path, "ext-dir.name" );
  +        m_embeddorParameters.setParameter( "myrmidon.ext.path", dir.getAbsolutePath() );
       }
   
       /**
  @@ -267,14 +287,21 @@
       /**
        * Prepares and returns the project listener to use.
        */
  -    private ProjectListener prepareListener( final Embeddor embeddor )
  +    private void prepareListeners( final Embeddor embeddor,
  +                                   final Workspace workspace )
           throws Exception
       {
  -        if( m_listener == null )
  +        if( m_listenerName != null )
           {
  -            m_listener = embeddor.createListener( m_listenerName );
  +            final ProjectListener listener = embeddor.createListener( m_listenerName );
  +            workspace.addProjectListener( listener );
  +        }
  +        final int count = m_listeners.size();
  +        for( int i = 0; i < count; i++ )
  +        {
  +            final ProjectListener listener = (ProjectListener)m_listeners.get(i );
  +            workspace.addProjectListener( listener );
           }
  -        return m_listener;
       }
   
       /**
  @@ -310,4 +337,36 @@
   
           return projectFile;
       }
  +
  +    /**
  +     * Resolve a directory relative to another base directory.
  +     */
  +    private File resolveDirectory( final File baseDir, final String dir, final String name )
  +        throws Exception
  +    {
  +        final File file = FileUtil.resolveFile( baseDir, dir );
  +        checkDirectory( file, name );
  +        return file;
  +    }
  +
  +    /**
  +     * Verify file is a directory else throw an exception.
  +     */
  +    private void checkDirectory( final File file, final String name )
  +        throws Exception
  +    {
  +        if( !file.exists() )
  +        {
  +            final String nameStr = REZ.getString( name );
  +            final String message = REZ.getString( "file-no-exist.error", nameStr, file );
  +            throw new Exception( message );
  +        }
  +        else if( !file.isDirectory() )
  +        {
  +            final String nameStr = REZ.getString( name );
  +            final String message = REZ.getString( "file-not-dir.error", nameStr, file );
  +            throw new Exception( message );
  +        }
  +    }
  +
   }
  
  
  
  1.8       +5 -1      jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/frontends/Resources.properties
  
  Index: Resources.properties
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/frontends/Resources.properties,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- Resources.properties	26 Mar 2002 02:14:02 -0000	1.7
  +++ Resources.properties	3 Apr 2002 10:58:19 -0000	1.8
  @@ -16,7 +16,8 @@
   build.opt=Define a builder parameter (ie -Bfoo=var).
   dry-run.opt=Do not execute tasks - just print them out.
   
  -home-not-dir.error=myrmidon-home ({0}) is not a directory.
  +file-no-exist.error={0} {1} does not exist.
  +file-not-dir.error={0} {1} is not a directory.
   bad-file.error=File {0} is not a file or doesn't exist.
   bad-loglevel.error=Unknown log level - {0}.
   build-failed.error=BUILD FAILED.
  @@ -26,3 +27,6 @@
   
   homedir.notice=Ant Home Directory: {0}
   buildfile.notice=Ant Build File: {0}
  +home-dir.name=Ant home directory
  +task-lib-dir.name=Task library directory
  +ext-dir.name=Extension library directory
  
  
  
  1.4       +15 -6     jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/classloader/ClassLoaderManager.java
  
  Index: ClassLoaderManager.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/classloader/ClassLoaderManager.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ClassLoaderManager.java	1 Apr 2002 09:56:26 -0000	1.3
  +++ ClassLoaderManager.java	3 Apr 2002 10:58:19 -0000	1.4
  @@ -13,6 +13,7 @@
    * Manages a classloader hierarchy.
    *
    * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
  + * @version $Revision: 1.4 $ $Date: 2002/04/03 10:58:19 $
    */
   public interface ClassLoaderManager
   {
  @@ -20,15 +21,22 @@
       String ROLE = ClassLoaderManager.class.getName();
   
       /**
  -     * Builds the ClassLoader for a Jar file, resolving dependencies.
  +     * Returns the ClassLoader for a Jar file.  The ClassLoader is created,
  +     * if necessary.  The ClassLoader's parent will include the common
  +     * ClassLoader, along with any extensions required by the Jar file.
  +     * It is guaranteed that each extension will appear at most once in the
  +     * ClassLoader hierarchy, so that classes from the extension can be
  +     * shared across the ClassLoaders returned by this method.
  +     *
        * @param jar the jar file containing the classes to load
  -     * @return the created classloader
  +     * @return the classloader
        * @throws ClassLoaderException on error
        */
  -    ClassLoader createClassLoader( File jar ) throws ClassLoaderException;
  +    ClassLoader getClassLoader( File jar ) throws ClassLoaderException;
   
       /**
  -     * Builds the ClassLoader for a set of files, resolving dependencies.
  +     * Creates a ClassLoader for a set of files.  See {@link #getClassLoader}
  +     * for details.
        *
        * @param jars The Jar/zip files to create the classloader for.  Use null
        *             or an empty array to use the common classloader.
  @@ -38,9 +46,10 @@
       ClassLoader createClassLoader( File[] jars ) throws ClassLoaderException;
   
       /**
  -     * Provides the common ClassLoader, which is the parent of all classloaders
  +     * Returns the common ClassLoader, which is the parent of all classloaders
        * built by this ClassLoaderManager.
  -     * @return the common ClassLoader
  +     *
  +     * @return the common ClassLoader.
        */
       ClassLoader getCommonClassLoader();
   }
  
  
  
  1.7       +12 -4     jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/extensions/ExtensionManager.java
  
  Index: ExtensionManager.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/extensions/ExtensionManager.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- ExtensionManager.java	1 Apr 2002 09:56:27 -0000	1.6
  +++ ExtensionManager.java	3 Apr 2002 10:58:19 -0000	1.7
  @@ -7,17 +7,25 @@
    */
   package org.apache.myrmidon.interfaces.extensions;
   
  -import org.apache.avalon.excalibur.extension.PackageRepository;
  +import org.apache.avalon.excalibur.extension.Extension;
  +import org.apache.avalon.excalibur.extension.OptionalPackage;
   
   /**
  - * PackageRepository
  + * Maintains a set of optional packages.
    *
    * @author <a href="mailto:peter@apache.org">Peter Donald</a>
  - * @version $Revision: 1.6 $ $Date: 2002/04/01 09:56:27 $
  + * @version $Revision: 1.7 $ $Date: 2002/04/03 10:58:19 $
    */
   public interface ExtensionManager
  -    extends PackageRepository
   {
       /** Role name for this interface. */
       String ROLE = ExtensionManager.class.getName();
  +
  +    /**
  +     * Locates the optional package which best matches a required extension.
  +     *
  +     * @param extension the extension to locate an optional package
  +     * @return the optional package, or null if not found.
  +     */
  +    public OptionalPackage getOptionalPackage( Extension extension );
   }
  
  
  
  1.12      +35 -29    jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/AbstractMyrmidonTest.java
  
  Index: AbstractMyrmidonTest.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/AbstractMyrmidonTest.java,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- AbstractMyrmidonTest.java	28 Mar 2002 06:35:21 -0000	1.11
  +++ AbstractMyrmidonTest.java	3 Apr 2002 10:58:20 -0000	1.12
  @@ -29,28 +29,21 @@
       private final File m_baseDir;
       private Logger m_logger;
   
  -    protected static final Resources getResourcesForTested( final Class clazz )
  +    public AbstractMyrmidonTest( final String name )
       {
  -        final Package pkg = clazz.getPackage();
  -
  -        String baseName;
  -        if( null == pkg )
  -        {
  -            final String name = clazz.getName();
  -            if( -1 == name.lastIndexOf( "." ) )
  -            {
  -                baseName = "";
  -            }
  -            else
  -            {
  -                baseName = name.substring( 0, name.lastIndexOf( "." ) );
  -            }
  -        }
  -        else
  -        {
  -            baseName = pkg.getName();
  -        }
  +        super( name );
  +        final String baseDirProp = System.getProperty( "test.basedir" );
  +        m_baseDir = getCanonicalFile( new File( baseDirProp ) );
  +        final String packagePath = getPackageName( getClass() ).replace( '.', File.separatorChar );
  +        m_testBaseDir = getCanonicalFile( new File( m_baseDir, packagePath ) );
  +    }
   
  +    /**
  +     * Locates the error message resources for a class.
  +     */
  +    protected static final Resources getResourcesForTested( final Class clazz )
  +    {
  +        String baseName = getPackageName( clazz );
           if( baseName.endsWith( ".test" ) )
           {
               baseName = baseName.substring( 0, baseName.length() - 5 );
  @@ -59,16 +52,29 @@
           return ResourceManager.getBaseResources( baseName + ".Resources", AbstractMyrmidonTest.class.getClassLoader() );
       }
   
  -    public AbstractMyrmidonTest( String name )
  +    /**
  +     * Returns the name of the package containing a class.
  +     *
  +     * @return The . delimited package name, or an empty string if the class
  +     *         is in the default package.
  +     */
  +    protected static String getPackageName( final Class clazz )
       {
  -        super( name );
  -        final String baseDirProp = System.getProperty( "test.basedir" );
  -        m_baseDir = getCanonicalFile( new File( baseDirProp ) );
  -        String packagePath = getClass().getName();
  -        int idx = packagePath.lastIndexOf( '.' );
  -        packagePath = packagePath.substring( 0, idx );
  -        packagePath = packagePath.replace( '.', File.separatorChar );
  -        m_testBaseDir = getCanonicalFile( new File( m_baseDir, packagePath ) );
  +        final Package pkg = clazz.getPackage();
  +        if( null != pkg )
  +        {
  +            return pkg.getName();
  +        }
  +
  +        final String name = clazz.getName();
  +        if( -1 == name.lastIndexOf( "." ) )
  +        {
  +            return "";
  +        }
  +        else
  +        {
  +            return name.substring( 0, name.lastIndexOf( "." ) );
  +        }
       }
   
       /**
  
  
  
  1.7       +13 -52    jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/AbstractProjectTest.java
  
  Index: AbstractProjectTest.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/AbstractProjectTest.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- AbstractProjectTest.java	28 Mar 2002 06:35:21 -0000	1.6
  +++ AbstractProjectTest.java	3 Apr 2002 10:58:20 -0000	1.7
  @@ -8,68 +8,24 @@
   package org.apache.myrmidon;
   
   import java.io.File;
  -import org.apache.avalon.framework.logger.Logger;
  -import org.apache.avalon.framework.parameters.Parameters;
  -import org.apache.myrmidon.components.embeddor.DefaultEmbeddor;
  -import org.apache.myrmidon.interfaces.embeddor.Embeddor;
  -import org.apache.myrmidon.interfaces.model.Project;
  -import org.apache.myrmidon.interfaces.workspace.Workspace;
  +import org.apache.myrmidon.frontends.EmbeddedAnt;
   import org.apache.myrmidon.listeners.ProjectListener;
   
   /**
    * A base class for test cases which need to execute projects.
    *
    * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
  - * @version $Revision: 1.6 $ $Date: 2002/03/28 06:35:21 $
  + * @version $Revision: 1.7 $ $Date: 2002/04/03 10:58:20 $
    */
   public class AbstractProjectTest
       extends AbstractMyrmidonTest
   {
  -    private DefaultEmbeddor m_embeddor;
  -
       public AbstractProjectTest( final String name )
       {
           super( name );
       }
   
       /**
  -     * Tear-down the test.
  -     */
  -    protected void tearDown() throws Exception
  -    {
  -        if( m_embeddor != null )
  -        {
  -            m_embeddor.dispose();
  -            m_embeddor = null;
  -        }
  -    }
  -
  -    /**
  -     * Returns an embeddor which can be used to build and execute projects.
  -     */
  -    protected Embeddor getEmbeddor() throws Exception
  -    {
  -        if( m_embeddor == null )
  -        {
  -            // Need to set the context classloader - The default embeddor uses it
  -            Thread.currentThread().setContextClassLoader( getClass().getClassLoader() );
  -
  -            final Logger logger = getLogger();
  -            m_embeddor = new DefaultEmbeddor();
  -            m_embeddor.enableLogging( logger );
  -
  -            final Parameters params = new Parameters();
  -            final File instDir = getInstallDirectory();
  -            params.setParameter( "myrmidon.home", instDir.getAbsolutePath() );
  -            m_embeddor.parameterize( params );
  -            m_embeddor.initialize();
  -            m_embeddor.start();
  -        }
  -
  -        return m_embeddor;
  -    }
  -
  -    /**
        * Executes a target in a project, and asserts that it fails with the
        * given error message.
        */
  @@ -117,22 +73,27 @@
           throws Exception
       {
           // Create the project and workspace
  -        final Embeddor embeddor = getEmbeddor();
  -        final Project project = embeddor.createProject( projectFile.getAbsolutePath(), null, null );
  -        final Workspace workspace = embeddor.createWorkspace( new Parameters() );
  +        final EmbeddedAnt embeddor = new EmbeddedAnt();
  +        embeddor.setHomeDirectory( getInstallDirectory() );
  +        embeddor.enableLogging( getLogger() );
  +        embeddor.setSharedClassLoader( getClass().getClassLoader() );
  +        embeddor.setProjectFile( projectFile.getAbsolutePath() );
  +        embeddor.setProjectListener( null );
   
           // Add a listener to make sure all is good
           final TrackingProjectListener tracker = new TrackingProjectListener();
  -        workspace.addProjectListener( tracker );
  +        embeddor.addProjectListener( tracker );
   
           // Add supplied listener
           if( listener != null )
           {
  -            workspace.addProjectListener( listener );
  +            embeddor.addProjectListener( listener );
           }
   
           // Now execute the target
  -        workspace.executeProject( project, targetName );
  +        embeddor.executeTargets( new String[] { targetName } );
  +
  +        embeddor.stop();
   
           // Make sure all expected events were delivered
           tracker.assertComplete();
  
  
  
  1.21      +48 -5     jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/components/AbstractComponentTest.java
  
  Index: AbstractComponentTest.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/components/AbstractComponentTest.java,v
  retrieving revision 1.20
  retrieving revision 1.21
  diff -u -r1.20 -r1.21
  --- AbstractComponentTest.java	27 Mar 2002 07:04:17 -0000	1.20
  +++ AbstractComponentTest.java	3 Apr 2002 10:58:20 -0000	1.21
  @@ -10,12 +10,16 @@
   import java.util.ArrayList;
   import java.util.Iterator;
   import java.util.List;
  +import java.io.File;
   import org.apache.aut.converter.Converter;
   import org.apache.avalon.framework.logger.LogEnabled;
   import org.apache.avalon.framework.logger.Logger;
   import org.apache.avalon.framework.service.DefaultServiceManager;
   import org.apache.avalon.framework.service.ServiceManager;
   import org.apache.avalon.framework.service.Serviceable;
  +import org.apache.avalon.framework.parameters.Parameters;
  +import org.apache.avalon.framework.parameters.Parameterizable;
  +import org.apache.avalon.framework.activity.Initializable;
   import org.apache.myrmidon.AbstractMyrmidonTest;
   import org.apache.myrmidon.components.classloader.DefaultClassLoaderManager;
   import org.apache.myrmidon.components.configurer.DefaultConfigurer;
  @@ -93,10 +97,9 @@
               m_serviceManager.put( Executor.ROLE, component );
               components.add( component );
   
  -            final DefaultClassLoaderManager classLoaderMgr = new DefaultClassLoaderManager();
  -            classLoaderMgr.setCommonClassLoader( getClass().getClassLoader() );
  -            m_serviceManager.put( ClassLoaderManager.ROLE, classLoaderMgr );
  -            components.add( classLoaderMgr );
  +            component = createComponent( ClassLoaderManager.ROLE, DefaultClassLoaderManager.class );
  +            m_serviceManager.put( ClassLoaderManager.ROLE, component );
  +            components.add( component );
   
               component = createComponent( ExtensionManager.ROLE, DefaultExtensionManager.class );
               m_serviceManager.put( ExtensionManager.ROLE, component );
  @@ -132,6 +135,29 @@
                   }
               }
   
  +            // Parameterise the components
  +            final Parameters parameters = getParameters();
  +            for( Iterator iterator = components.iterator(); iterator.hasNext(); )
  +            {
  +                Object obj = iterator.next();
  +                if( obj instanceof Parameterizable )
  +                {
  +                    final Parameterizable parameterizable = (Parameterizable)obj;
  +                    parameterizable.parameterize( parameters );
  +                }
  +            }
  +
  +            // Initialise the components
  +            for( Iterator iterator = components.iterator(); iterator.hasNext(); )
  +            {
  +                Object obj = iterator.next();
  +                if( obj instanceof Initializable )
  +                {
  +                    final Initializable initializable = (Initializable)obj;
  +                    initializable.initialize();
  +                }
  +            }
  +
               // Register some standard roles
               // Add some core roles
               final RoleManager roleManager = (RoleManager)getServiceManager().lookup( RoleManager.ROLE );
  @@ -144,12 +170,29 @@
       }
   
       /**
  -     * Creates an instance of a component.  Sub-classes can override this
  +     * Creates the parameters for the test.  Sub-classes can override this
  +     * method to set-up the parameters.
  +     */
  +    protected Parameters getParameters()
  +    {
  +        final Parameters parameters = new Parameters();
  +        final String homeDir = getInstallDirectory().getAbsolutePath();
  +        parameters.setParameter( "myrmidon.home", homeDir );
  +        parameters.setParameter( "myrmidon.ext.path", homeDir + File.separatorChar + "ext" );
  +        return parameters;
  +    }
  +
  +    /**
  +     * Creates an instance of a test component.  Sub-classes can override this
        * method to add a particular implementation to the set of test components.
        */
       protected Object createComponent( final String role, final Class defaultImpl )
           throws Exception
       {
  +        if( role.equals( ClassLoaderManager.ROLE ) )
  +        {
  +            return new DefaultClassLoaderManager( getClass().getClassLoader() );
  +        }
           return defaultImpl.newInstance();
       }
   
  
  
  
  1.1                  jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/components/classloader/test/DefaultClassLoaderManagerTestCase.java
  
  Index: DefaultClassLoaderManagerTestCase.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included  with this distribution in
   * the LICENSE.txt file.
   */
  package org.apache.myrmidon.components.classloader.test;
  
  import java.io.File;
  import java.net.URL;
  import java.net.URLClassLoader;
  import java.util.Enumeration;
  import org.apache.myrmidon.components.AbstractComponentTest;
  import org.apache.myrmidon.components.classloader.DefaultClassLoaderManager;
  import org.apache.myrmidon.interfaces.classloader.ClassLoaderManager;
  import org.apache.myrmidon.interfaces.classloader.ClassLoaderException;
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.avalon.excalibur.i18n.Resources;
  
  /**
   * Test cases for the DefaultClassLoaderManager.
   *
   * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
   * @version $Revision: 1.1 $ $Date: 2002/04/03 10:58:20 $
   */
  public class DefaultClassLoaderManagerTestCase
      extends AbstractComponentTest
  {
      private static final String UNSHARED_PKG_NAME =
          getPackageName( DefaultClassLoaderManagerTestCase.class ) + ".libs.unshared";
      private static final String UNSHARED_RES_NAME = getResourceName( UNSHARED_PKG_NAME, "unshared.txt" );
      private static final String UNSHARED_CLASS_NAME = UNSHARED_PKG_NAME + ".UnsharedClass";
  
      private static final String SHARED_PKG_NAME =
          getPackageName( DefaultClassLoaderManagerTestCase.class ) + ".libs.shared";
      private static final String SHARED_RES_NAME = getResourceName( SHARED_PKG_NAME, "shared.txt" );
      private static final String SHARED_CLASS_NAME = SHARED_PKG_NAME + ".SharedClass";
  
      private static final String EXTN_PKG_NAME =
          getPackageName( DefaultClassLoaderManagerTestCase.class ) + ".libs.extn";
      private static final String EXTN_RES_NAME = getResourceName( EXTN_PKG_NAME, "extn.txt" );
      private static final String EXTN_CLASS_NAME = EXTN_PKG_NAME + ".ExtnClass";
  
      private File m_commonJar;
      private ClassLoader m_commonClassLoader;
      private ClassLoaderManager m_loaderManager;
  
      public DefaultClassLoaderManagerTestCase( final String name )
      {
          super( name );
      }
  
      /**
       * Sets up the test.
       */
      protected void setUp() throws Exception
      {
          m_commonJar = getTestResource( "common.jar" );
          final URL commonJarUrl = m_commonJar.toURL();
          m_commonClassLoader = new URLClassLoader( new URL[]{commonJarUrl} );
  
          assertClassFound( m_commonClassLoader, SHARED_CLASS_NAME );
          assertResourcesFound( m_commonClassLoader, SHARED_RES_NAME, m_commonJar );
  
          // Create the classloader mgr
          m_loaderManager = (ClassLoaderManager)getServiceManager().lookup( ClassLoaderManager.ROLE );
      }
  
      /**
       * Creates an instance of a test component.
       */
      protected Object createComponent( final String role, final Class defaultImpl )
          throws Exception
      {
          if( role.equals( ClassLoaderManager.ROLE ) )
          {
              return new DefaultClassLoaderManager( m_commonClassLoader );
          }
          else
          {
              return super.createComponent( role, defaultImpl );
          }
      }
  
      /**
       * Creates the parameters for the test.  Sub-classes can override this
       * method to set-up the parameters.
       */
      protected Parameters getParameters()
      {
          final Parameters parameters = super.getParameters();
          parameters.setParameter( "myrmidon.ext.path", getTestDirectory( "ext" ).getAbsolutePath() );
          return parameters;
      }
  
      /**
       * Returns the name of a resource in a package.
       */
      private static String getResourceName( final String pkgName,
                                             final String resname )
      {
          return pkgName.replace( '.', '/' ) + '/' + resname;
      }
  
      /**
       * Asserts that a class is not available in a classloader.
       */
      private void assertClassNotFound( final ClassLoader classLoader,
                                        final String className )
      {
          try
          {
              classLoader.loadClass( className );
              fail( "Class " + className + " should not be available." );
          }
          catch( ClassNotFoundException e )
          {
          }
      }
  
      /**
       * Asserts that a class is available in a classloader.
       */
      private void assertClassFound( final ClassLoader classLoader,
                                     final String className )
          throws Exception
      {
          assertClassFound( classLoader, className, classLoader );
      }
  
      /**
       * Asserts that a class is available in a classloader.
       */
      private void assertClassFound( final ClassLoader classLoader,
                                     final String className,
                                     final ClassLoader expectedClassLoader )
          throws Exception
      {
          try
          {
              final Class cls = classLoader.loadClass( className );
              assertSame( expectedClassLoader, cls.getClassLoader() );
              if( classLoader != expectedClassLoader )
              {
                  final Class expectedCls = expectedClassLoader.loadClass( className );
                  assertSame( expectedCls, cls );
              }
          }
          catch( ClassNotFoundException e )
          {
              fail( "Class " + className + " not found." );
          }
  
      }
  
      /**
       * Asserts that a resouce is not available in a classloader.
       */
      private void assertResourceNotFound( final ClassLoader classLoader,
                                           final String resName )
          throws Exception
      {
          assertNull( classLoader.getResource( resName ) );
          assertNull( classLoader.getResourceAsStream( resName ) );
          final Enumeration enum = classLoader.getResources( resName );
          assertTrue( !enum.hasMoreElements() );
      }
  
      /**
       * Asserts that a resource is available in a classloader.
       */
      private void assertResourcesFound( final ClassLoader classLoader,
                                         final String resName,
                                         final File expectedJar )
          throws Exception
      {
          assertResourcesFound( classLoader, resName, new File[]{expectedJar} );
      }
  
      /**
       * Asserts that a resource is available in a classloader.
       */
      private void assertResourcesFound( final ClassLoader classLoader,
                                         final String resName,
                                         final File[] expectedJars )
          throws Exception
      {
          final String[] expectedLocations = new String[ expectedJars.length ];
          for( int i = 0; i < expectedJars.length; i++ )
          {
              final File jar = expectedJars[ i ];
              expectedLocations[ i ] = "jar:" + jar.toURL() + "!/" + resName;
          }
  
          assertResourcesFound( classLoader, resName, expectedLocations );
      }
  
      /**
       * Asserts that a resource is available in a classloader.
       */
      private void assertResourcesFound( final ClassLoader classLoader,
                                         final String resName,
                                         final String[] expectedLocations )
          throws Exception
      {
          // Use the first in the list of expected locations as the location
          // of the resource returned by getResource()
          final URL resUrl = classLoader.getResource( resName );
          assertNotNull( resUrl );
          assertEquals( expectedLocations[ 0 ], resUrl.toString() );
  
          // Now check all of the resources returned by getResources()
          final Enumeration resources = classLoader.getResources( resName );
          for( int i = 0; i < expectedLocations.length; i++ )
          {
              final String expectedLocation = expectedLocations[ i ];
              assertTrue( resources.hasMoreElements() );
              final URL location = (URL)resources.nextElement();
              assertEquals( expectedLocation, location.toString() );
          }
          assertTrue( !resources.hasMoreElements() );
      }
  
      /**
       * Tests for a Jar with no required extensions.
       */
      public void testNoDependencies() throws Exception
      {
          // Make some assumptions about the common classloader
          assertClassNotFound( m_commonClassLoader, UNSHARED_CLASS_NAME );
          assertResourceNotFound( m_commonClassLoader, UNSHARED_RES_NAME );
  
          // Build the classloader
          final File jarFile = getTestResource( "no-dependencies.jar" );
          final ClassLoader classLoader = m_loaderManager.getClassLoader( jarFile );
  
          // Check shared classes/resources
          assertClassFound( classLoader, SHARED_CLASS_NAME, m_commonClassLoader );
          assertResourcesFound( classLoader, SHARED_RES_NAME, new File[]{m_commonJar, jarFile} );
  
          // Check unshared classes/resources
          assertClassFound( classLoader, UNSHARED_CLASS_NAME );
          assertResourcesFound( classLoader, UNSHARED_RES_NAME, jarFile );
      }
  
      /**
       * Tests ClassLoader caching.
       */
      public void testClassLoaderReuse() throws Exception
      {
          final File jarFile = getTestResource( "no-dependencies.jar" );
          final ClassLoader classLoader1 = m_loaderManager.getClassLoader( jarFile );
          final ClassLoader classLoader2 = m_loaderManager.getClassLoader( jarFile );
          assertSame( classLoader1, classLoader2 );
      }
  
      /**
       * Tests for a Jar with a single required extension.
       */
      public void testOneDependency() throws Exception
      {
          // Make some assumptions about the common classloader
          assertClassNotFound( m_commonClassLoader, UNSHARED_CLASS_NAME );
          assertResourceNotFound( m_commonClassLoader, UNSHARED_RES_NAME );
          assertClassNotFound( m_commonClassLoader, EXTN_CLASS_NAME );
          assertResourceNotFound( m_commonClassLoader, EXTN_RES_NAME );
  
          // Build the extension classloader
          final File extnJarFile = getTestResource( "ext/simple-extension.jar" );
          final ClassLoader extnClassLoader = m_loaderManager.getClassLoader( extnJarFile );
  
          // Build the Jar classloader
          final File jarFile = getTestResource( "one-dependency.jar" );
          final ClassLoader classLoader = m_loaderManager.getClassLoader( jarFile );
  
          // Check shared classes/resources
          assertClassFound( classLoader, SHARED_CLASS_NAME, m_commonClassLoader );
          assertResourcesFound( classLoader, SHARED_RES_NAME, new File[]{m_commonJar, extnJarFile, jarFile} );
  
          // Check extension classes/resources
          assertClassFound( classLoader, EXTN_CLASS_NAME, extnClassLoader );
          assertResourcesFound( classLoader, EXTN_RES_NAME, extnJarFile );
  
          // Check unshared classes/resources
          assertClassFound( classLoader, UNSHARED_CLASS_NAME );
          assertResourcesFound( classLoader, UNSHARED_RES_NAME, jarFile );
      }
  
      /**
       * Tests that classes from extensions can be shared across classloaders.
       */
      public void testShareClasses() throws Exception
      {
          // Build the extension classloader
          final File extnJarFile = getTestResource( "ext/simple-extension.jar" );
          final ClassLoader extnClassLoader = m_loaderManager.getClassLoader( extnJarFile );
  
          // Build the Jar classloaders
          final File jarFile1 = getTestResource( "one-dependency.jar" );
          final ClassLoader classLoader1 = m_loaderManager.getClassLoader( jarFile1 );
          final File jarFile2 = getTestResource( "one-dependency-2.jar" );
          final ClassLoader classLoader2 = m_loaderManager.getClassLoader( jarFile2 );
  
          // Check extension classes/resources
          assertClassFound( classLoader1, EXTN_CLASS_NAME, extnClassLoader );
          assertResourcesFound( classLoader1, EXTN_RES_NAME, extnJarFile );
          assertClassFound( classLoader2, EXTN_CLASS_NAME, extnClassLoader );
          assertResourcesFound( classLoader2, EXTN_RES_NAME, extnJarFile );
      }
  
      /**
       * Tests detection of dependency cycles in extensions.
       */
      public void testCycle() throws Exception
      {
          final File jarFile = getTestResource( "ext/cycle-extension-1.jar" );
          try
          {
              m_loaderManager.getClassLoader( jarFile );
              fail();
          }
          catch( final ClassLoaderException e )
          {
              final Resources rez = getResourcesForTested( DefaultClassLoaderManager.class );
              final String[] messages = {
                  rez.getString( "create-classloader-for-file.error", jarFile ),
                  rez.getString( "dependency-cycle.error", jarFile )
              };
              assertSameMessage( messages, e );
          }
      }
  
      /**
       * add some classes to common loader only.
       *
       * unknown extension
       * multiple versions of extensions
       * extn with requirement on itself
       *
       * jar with 1 and 2 extns:
       *   class/resources in parent
       *   class/resources in jar
       *   class/resources in extn
       *   class/resources in all
       *
       * jar with transitive extn
       *   class/resources in 2nd extn
       *
       * jar with transitive extn + explicit extn on same jar
       *   class/resources in 2nd extn
       *
       * Same classes:
       *     get extn explicitly and implicitly, and check classes are the same
       *     extn shared by 2 jars, using same extn and different extns
       *     classes in common classloader, shared by 2 jars
       *
       * multiple files:
       *     fetch classloader twice
       *     different path ordering
       *
       * tools.jar
       */
  }
  
  
  
  1.1                  jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/components/classloader/test/libs/cycle-extension-1.mf
  
  Index: cycle-extension-1.mf
  ===================================================================
  Extension-Name: test.cycle1
  Specification-Title: Test Extension
  Specification-Version: 1.0.0
  Specification-Vendor: Jakarta Apache
  Implementation-Vendor-Id: org.apache.myrmidon
  Implementation-Vendor: Apache Myrmidon Project
  Implementation-Version: 3.0
  Extension-List: cycle2
  cycle2-Extension-Name: test.cycle2
  cycle2-Specification-Version: 1.0
  
  
  
  1.1                  jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/components/classloader/test/libs/cycle-extension-2.mf
  
  Index: cycle-extension-2.mf
  ===================================================================
  Extension-Name: test.cycle2
  Specification-Title: Test Extension
  Specification-Version: 1.0.0
  Specification-Vendor: Jakarta Apache
  Implementation-Vendor-Id: org.apache.myrmidon
  Implementation-Vendor: Apache Myrmidon Project
  Implementation-Version: 1.709.2
  Extension-List: cycle1
  cycle1-Extension-Name: test.cycle1
  cycle1-Specification-Version: 1.0
  
  
  
  1.1                  jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/components/classloader/test/libs/one-dependency.mf
  
  Index: one-dependency.mf
  ===================================================================
  Extension-List: extension1
  extension1-Extension-Name: test.simple
  extension1-Specification-Version: 1.0
  
  
  
  1.1                  jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/components/classloader/test/libs/simple-extension.mf
  
  Index: simple-extension.mf
  ===================================================================
  Extension-Name: test.simple
  Specification-Title: Test Simple Extension
  Specification-Version: 1.0.0
  Specification-Vendor: Jakarta Apache
  Implementation-Vendor-Id: org.apache.myrmidon
  Implementation-Vendor: Apache Myrmidon Project
  Implementation-Version: 1.0.2
  
  
  
  1.1                  jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/components/classloader/test/libs/extn/ExtnClass.java
  
  Index: ExtnClass.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included  with this distribution in
   * the LICENSE.txt file.
   */
  package org.apache.myrmidon.components.classloader.test.libs.extn;
  
  import org.apache.myrmidon.components.classloader.test.libs.shared.SharedClass;
  
  /**
   * A test class loaded from an extension.
   *
   * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
   * @version $Revision: 1.1 $ $Date: 2002/04/03 10:58:20 $
   */
  public class ExtnClass
  {
      public SharedClass m_test = new SharedClass();
  }
  
  
  
  1.1                  jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/components/classloader/test/libs/extn/extn.txt
  
  Index: extn.txt
  ===================================================================
  A test resource loaded from an extension.
  
  
  1.1                  jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/components/classloader/test/libs/shared/SharedClass.java
  
  Index: SharedClass.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included  with this distribution in
   * the LICENSE.txt file.
   */
  package org.apache.myrmidon.components.classloader.test.libs.shared;
  
  /**
   * A test class.
   *
   * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
   * @version $Revision: 1.1 $ $Date: 2002/04/03 10:58:20 $
   */
  public class SharedClass
  {
  }
  
  
  
  1.1                  jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/components/classloader/test/libs/shared/shared.txt
  
  Index: shared.txt
  ===================================================================
  A shared resource.
  
  
  1.1                  jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/components/classloader/test/libs/unshared/UnsharedClass.java
  
  Index: UnsharedClass.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included  with this distribution in
   * the LICENSE.txt file.
   */
  package org.apache.myrmidon.components.classloader.test.libs.unshared;
  
  import org.apache.myrmidon.components.classloader.test.libs.shared.SharedClass;
  
  /**
   * A test class.
   *
   * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
   * @version $Revision: 1.1 $ $Date: 2002/04/03 10:58:20 $
   */
  public class UnsharedClass
  {
      public SharedClass m_test = new SharedClass();
  }
  
  
  
  1.1                  jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/components/classloader/test/libs/unshared/unshared.txt
  
  Index: unshared.txt
  ===================================================================
  An unshared resource.
  
  
  1.2       +43 -1     jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/components/embeddor/test/DefaultEmbeddorTest.java
  
  Index: DefaultEmbeddorTest.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/src/test/org/apache/myrmidon/components/embeddor/test/DefaultEmbeddorTest.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- DefaultEmbeddorTest.java	17 Mar 2002 08:07:09 -0000	1.1
  +++ DefaultEmbeddorTest.java	3 Apr 2002 10:58:20 -0000	1.2
  @@ -9,8 +9,10 @@
   
   import java.io.File;
   import org.apache.avalon.framework.parameters.Parameters;
  +import org.apache.avalon.framework.logger.Logger;
   import org.apache.myrmidon.AbstractProjectTest;
   import org.apache.myrmidon.LogMessageTracker;
  +import org.apache.myrmidon.components.embeddor.DefaultEmbeddor;
   import org.apache.myrmidon.interfaces.embeddor.Embeddor;
   import org.apache.myrmidon.interfaces.model.Project;
   import org.apache.myrmidon.interfaces.model.Target;
  @@ -21,17 +23,56 @@
    * Test cases for the default embeddor.
    *
    * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
  - * @version $Revision: 1.1 $ $Date: 2002/03/17 08:07:09 $
  + * @version $Revision: 1.2 $ $Date: 2002/04/03 10:58:20 $
    */
   public class DefaultEmbeddorTest
       extends AbstractProjectTest
   {
  +    private DefaultEmbeddor m_embeddor;
  +
       public DefaultEmbeddorTest( String name )
       {
           super( name );
       }
   
       /**
  +     * Tear-down the test.
  +     */
  +    protected void tearDown() throws Exception
  +    {
  +        if( m_embeddor != null )
  +        {
  +            m_embeddor.dispose();
  +            m_embeddor = null;
  +        }
  +    }
  +
  +    /**
  +     * Returns an embeddor which can be used to build and execute projects.
  +     */
  +    protected Embeddor getEmbeddor() throws Exception
  +    {
  +        if( m_embeddor == null )
  +        {
  +            // Need to set the context classloader - The default embeddor uses it
  +            Thread.currentThread().setContextClassLoader( getClass().getClassLoader() );
  +
  +            final Logger logger = getLogger();
  +            m_embeddor = new DefaultEmbeddor();
  +            m_embeddor.enableLogging( logger );
  +
  +            final Parameters params = new Parameters();
  +            final File instDir = getInstallDirectory();
  +            params.setParameter( "myrmidon.home", instDir.getAbsolutePath() );
  +            m_embeddor.parameterize( params );
  +            m_embeddor.initialize();
  +            m_embeddor.start();
  +        }
  +
  +        return m_embeddor;
  +    }
  +
  +    /**
        * Tests that a project is successfully built from a file.
        */
       public void testProjectBuilder() throws Exception
  @@ -63,6 +104,7 @@
       public void testCreateListener() throws Exception
       {
           final ProjectListener listener = getEmbeddor().createListener( "default" );
  +        assertNotNull( listener );
       }
   
       /**
  
  
  
  1.26      +15 -11    jakarta-ant/proposal/myrmidon/src/xdocs/todo.xml
  
  Index: todo.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/proposal/myrmidon/src/xdocs/todo.xml,v
  retrieving revision 1.25
  retrieving revision 1.26
  diff -u -r1.25 -r1.26
  --- todo.xml	1 Apr 2002 00:46:26 -0000	1.25
  +++ todo.xml	3 Apr 2002 10:58:20 -0000	1.26
  @@ -524,23 +524,12 @@
                           <code>&lt;socket&gt;</code>
                       conditions to an antlib.  Need to resolve how these will be passed a logger.
                       </li>
  -                    <li>Make the
  -                        <code>&lt;uptodate&gt;</code> task a condition, and move to
  -                    an antlib.
  -                    </li>
  -                    <li>Split up
  -                        <code>&lt;is-set&gt;</code> condition into is-set and is-true conditions.
  -                    </li>
                       <li>Allow the
                           <code>&lt;if&gt;</code> task to take any condition implementation.
                       </li>
                       <li>Add an else block to the
                           <code>&lt;if&gt;</code> task.
                       </li>
  -                    <li>Split the
  -                        <code>&lt;available&gt;</code> condition into separate conditions
  -                    that test for the availability of a class, or a resource.
  -                    </li>
                       <li>Move
                           <code>crimson.jar</code> to
                           <code>bin/lib</code> in the distribution,
  @@ -550,6 +539,21 @@
                       <li>Add a <code>--type</code> command-line option, to allow
                           the project builder to be manually selected.
                       </li>
  +                    <li>Change <code>ProjectBuilder</code>
  +                        and <code>Embeddor</code> to throw something more
  +                        specialised than Exception.
  +                    </li>
  +                    <li>Change <code>DefaultClassLoaderManager</code> to handle
  +                        directories as part of a library classpath.
  +                    </li>
  +                    <li><code>&lt;condition&gt;</code> should set the property
  +                        value to <code>false</code> when the condition is false.</li>
  +                    <li>Split the <code>&lt;uptodate&gt;</code> condition into
  +                        a condition that checks against a single target file,
  +                        and one which checks using a destdir/mapper.</li>
  +                    <li>Add a task to unset a property.</li>
  +                    <li>Change the various def and import task to allow a classpath
  +                        to be provided.</li>
                       <li>Unit tests.</li>
                   </ul>
   
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: cvs commit: jakarta-ant/proposal/myrmidon/src/xdocs todo.xml

Posted by Peter Donald <pe...@apache.org>.
On Wed, 3 Apr 2002 20:58, adammurdoch@apache.org wrote:
>   * Use multi-parent ClassLoaders for antlibs and extensions, so that each
>     extension jar is loaded by a single ClassLoader in the hierarchy. 
> Allows classes from extensions to be shared across dependent antlibs and
> extensions.

ooer!

>   * Changed contract of ClassLoaderManager.createClassLoader( File[] ), so
> that it creates a new ClassLoader each time it is called.

yay!

>   * Added a few test cases for DefaultClassLoaderManager.

BTW I noticed that you create testdata every time through in a few cases. Is 
there anyreason why you don't check the binary test data into the repository? 
This would improve our build times and it also gets a much more reliable test 
set. Because we no longer have to worry about ourselves buggering up one of 
the ant tasks or something and this task (which is subsequently used to build 
test data) would then cause other uni tests to fail. If we use a known 
quantity then it is much easier to reliably do unit testing? Thoughts?

-- 
Cheers,

Pete

*------------------------------------------------------*
| "Computers are useless. They can only give you       |
|            answers." - Pablo Picasso                 |
*------------------------------------------------------*

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>