* 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. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@272180 13f79535-47bb-0310-9956-ffa450edef68master
@@ -67,6 +67,7 @@ Legal: | |||
<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 @@ Legal: | |||
<!-- 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 @@ Legal: | |||
<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 @@ Legal: | |||
<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 @@ Legal: | |||
<!-- 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"/> | |||
@@ -837,23 +837,12 @@ public class MyrmidonSecurityManager | |||
<code><socket></code> | |||
conditions to an antlib. Need to resolve how these will be passed a logger. | |||
</li> | |||
<li>Make the | |||
<code><uptodate></code> task a condition, and move to | |||
an antlib. | |||
</li> | |||
<li>Split up | |||
<code><is-set></code> condition into is-set and is-true conditions. | |||
</li> | |||
<li>Allow the | |||
<code><if></code> task to take any condition implementation. | |||
</li> | |||
<li>Add an else block to the | |||
<code><if></code> task. | |||
</li> | |||
<li>Split the | |||
<code><available></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 @@ public class MyrmidonSecurityManager | |||
<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><condition></code> should set the property | |||
value to <code>false</code> when the condition is false.</li> | |||
<li>Split the <code><uptodate></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> | |||
@@ -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.logger.AbstractLogEnabled; | |||
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 @@ import org.apache.tools.todo.types.PathUtil; | |||
* A default implementation of a ClassLoader manager. | |||
* | |||
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||
* @version $Revision$ $Date$ | |||
*/ | |||
public class DefaultClassLoaderManager | |||
extends AbstractLogEnabled | |||
@@ -47,13 +46,22 @@ public class DefaultClassLoaderManager | |||
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 class DefaultClassLoaderManager | |||
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 @@ public class DefaultClassLoaderManager | |||
/** | |||
* 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 @@ public class DefaultClassLoaderManager | |||
{ | |||
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 ArrayList files ) | |||
private ClassLoader buildClassLoader( final OptionalPackage pkg, | |||
final Set pending ) | |||
throws Exception | |||
{ | |||
final ArrayList allFiles = new ArrayList( files ); | |||
final int count = files.size(); | |||
for( int i = 0; i < count; i++ ) | |||
final File jarFile = pkg.getFile(); | |||
// Check for cached classloader | |||
ClassLoader classLoader = (ClassLoader)m_classLoaders.get( jarFile ); | |||
if( classLoader != null ) | |||
{ | |||
final File file = (File)files.get(i ); | |||
checkFile( file ); | |||
getOptionalPackagesFor( file, allFiles ); | |||
return classLoader; | |||
} | |||
final URL[] urls = buildClasspath( allFiles ); | |||
return new URLClassLoader( urls, m_commonClassLoader ); | |||
// 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[] buildParentClassLoaders( final OptionalPackage[] packages, | |||
final Set pending ) | |||
throws Exception | |||
{ | |||
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 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 ); | |||
} | |||
} | |||
} | |||
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() ] ); | |||
} | |||
/** | |||
@@ -0,0 +1,141 @@ | |||
/* | |||
* 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$ $Date$ | |||
*/ | |||
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,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. |
@@ -108,7 +108,7 @@ public class DefaultDeployer | |||
{ | |||
try | |||
{ | |||
final ClassLoader classLoader = m_classLoaderManager.createClassLoader( file ); | |||
final ClassLoader classLoader = m_classLoaderManager.getClassLoader( file ); | |||
return createDeployment( classLoader, file.toURL() ); | |||
} | |||
catch( final Exception e ) | |||
@@ -71,10 +71,7 @@ public class DefaultEmbeddor | |||
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 class DefaultEmbeddor | |||
public void initialize() | |||
throws Exception | |||
{ | |||
// setup default properties | |||
m_defaults = createDefaultParameters(); | |||
// setup the root components | |||
setupComponents(); | |||
@@ -183,9 +177,6 @@ public class DefaultEmbeddor | |||
m_workspaceServiceManager = new MultiSourceServiceManager(); | |||
m_workspaceServiceManager.add( projServiceManager ); | |||
m_workspaceServiceManager.add( m_serviceManager ); | |||
// setup | |||
setupFiles(); | |||
} | |||
public void start() | |||
@@ -198,7 +189,8 @@ public class DefaultEmbeddor | |||
// 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 @@ public class DefaultEmbeddor | |||
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; | |||
} | |||
/** | |||
@@ -296,80 +268,6 @@ public class DefaultEmbeddor | |||
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 ); | |||
} | |||
} | |||
/** | |||
* Create a component that implements an interface. | |||
* | |||
@@ -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}. |
@@ -50,7 +50,6 @@ public class DefaultExtensionManager | |||
File.separator + "lib" + File.separator + "tools.jar"; | |||
private Logger m_logger; | |||
private String m_path; | |||
public DefaultExtensionManager() | |||
@@ -58,6 +57,11 @@ public class DefaultExtensionManager | |||
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 class DefaultExtensionManager | |||
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 @@ public class DefaultExtensionManager | |||
scanPath(); | |||
// Add the JVM's tools.jar as an extension | |||
final Extension extension = createToolsExtension(); | |||
final File jar = getToolsJar(); | |||
final Extension[] available = new Extension[]{extension}; | |||
@@ -99,6 +101,23 @@ public class DefaultExtensionManager | |||
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 ) | |||
{ | |||
m_logger.debug( message ); | |||
@@ -277,10 +277,10 @@ public class CLIMain | |||
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 @@ public class CLIMain | |||
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 ) ) | |||
@@ -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; | |||
@@ -47,12 +49,25 @@ public class EmbeddedAnt | |||
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 @@ public class EmbeddedAnt | |||
} | |||
/** | |||
* 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 class EmbeddedAnt | |||
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 class EmbeddedAnt | |||
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 @@ public class EmbeddedAnt | |||
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 @@ public class EmbeddedAnt | |||
{ | |||
m_embeddor = null; | |||
m_project = null; | |||
m_listener = null; | |||
m_listeners.clear(); | |||
} | |||
} | |||
@@ -213,19 +226,26 @@ public class EmbeddedAnt | |||
*/ | |||
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 @@ public class EmbeddedAnt | |||
/** | |||
* 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 ) | |||
{ | |||
final ProjectListener listener = embeddor.createListener( m_listenerName ); | |||
workspace.addProjectListener( listener ); | |||
} | |||
final int count = m_listeners.size(); | |||
for( int i = 0; i < count; i++ ) | |||
{ | |||
m_listener = embeddor.createListener( m_listenerName ); | |||
final ProjectListener listener = (ProjectListener)m_listeners.get(i ); | |||
workspace.addProjectListener( listener ); | |||
} | |||
return m_listener; | |||
} | |||
/** | |||
@@ -310,4 +337,36 @@ public class EmbeddedAnt | |||
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 ); | |||
} | |||
} | |||
} |
@@ -16,7 +16,8 @@ define.opt=Define a property (ie -Dfoo=var). | |||
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 @@ repeat.notice=Continue ? (Enter no to stop) | |||
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 |
@@ -13,6 +13,7 @@ import java.io.File; | |||
* Manages a classloader hierarchy. | |||
* | |||
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||
* @version $Revision$ $Date$ | |||
*/ | |||
public interface ClassLoaderManager | |||
{ | |||
@@ -20,15 +21,22 @@ public interface ClassLoaderManager | |||
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 @@ public interface ClassLoaderManager | |||
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(); | |||
} |
@@ -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$ $Date$ | |||
*/ | |||
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 ); | |||
} |
@@ -29,28 +29,21 @@ public abstract class AbstractMyrmidonTest | |||
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 @@ public abstract class AbstractMyrmidonTest | |||
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( "." ) ); | |||
} | |||
} | |||
/** | |||
@@ -8,12 +8,7 @@ | |||
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; | |||
/** | |||
@@ -25,50 +20,11 @@ import org.apache.myrmidon.listeners.ProjectListener; | |||
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 @@ public class AbstractProjectTest | |||
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(); | |||
@@ -10,12 +10,16 @@ package org.apache.myrmidon.components; | |||
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 @@ public abstract class AbstractComponentTest | |||
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 @@ public abstract class AbstractComponentTest | |||
} | |||
} | |||
// 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 @@ public abstract class AbstractComponentTest | |||
} | |||
/** | |||
* 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(); | |||
} | |||
@@ -0,0 +1,364 @@ | |||
/* | |||
* 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$ $Date$ | |||
*/ | |||
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 | |||
*/ | |||
} |
@@ -0,0 +1,10 @@ | |||
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 |
@@ -0,0 +1,10 @@ | |||
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 |
@@ -0,0 +1,21 @@ | |||
/* | |||
* 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$ $Date$ | |||
*/ | |||
public class ExtnClass | |||
{ | |||
public SharedClass m_test = new SharedClass(); | |||
} |
@@ -0,0 +1 @@ | |||
A test resource loaded from an extension. |
@@ -0,0 +1,3 @@ | |||
Extension-List: extension1 | |||
extension1-Extension-Name: test.simple | |||
extension1-Specification-Version: 1.0 |
@@ -0,0 +1,18 @@ | |||
/* | |||
* 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$ $Date$ | |||
*/ | |||
public class SharedClass | |||
{ | |||
} |
@@ -0,0 +1 @@ | |||
A shared resource. |
@@ -0,0 +1,7 @@ | |||
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 |
@@ -0,0 +1,21 @@ | |||
/* | |||
* 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$ $Date$ | |||
*/ | |||
public class UnsharedClass | |||
{ | |||
public SharedClass m_test = new SharedClass(); | |||
} |
@@ -0,0 +1 @@ | |||
An unshared resource. |
@@ -9,8 +9,10 @@ package org.apache.myrmidon.components.embeddor.test; | |||
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; | |||
@@ -26,11 +28,50 @@ import org.apache.myrmidon.listeners.ProjectListener; | |||
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. | |||
*/ | |||
@@ -63,6 +104,7 @@ public class DefaultEmbeddorTest | |||
public void testCreateListener() throws Exception | |||
{ | |||
final ProjectListener listener = getEmbeddor().createListener( "default" ); | |||
assertNotNull( listener ); | |||
} | |||
/** | |||
@@ -524,23 +524,12 @@ public class MyrmidonSecurityManager | |||
<code><socket></code> | |||
conditions to an antlib. Need to resolve how these will be passed a logger. | |||
</li> | |||
<li>Make the | |||
<code><uptodate></code> task a condition, and move to | |||
an antlib. | |||
</li> | |||
<li>Split up | |||
<code><is-set></code> condition into is-set and is-true conditions. | |||
</li> | |||
<li>Allow the | |||
<code><if></code> task to take any condition implementation. | |||
</li> | |||
<li>Add an else block to the | |||
<code><if></code> task. | |||
</li> | |||
<li>Split the | |||
<code><available></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 @@ public class MyrmidonSecurityManager | |||
<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><condition></code> should set the property | |||
value to <code>false</code> when the condition is false.</li> | |||
<li>Split the <code><uptodate></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> | |||