diff --git a/proposal/myrmidon/build.xml b/proposal/myrmidon/build.xml index c2d72d4d9..aa278c107 100644 --- a/proposal/myrmidon/build.xml +++ b/proposal/myrmidon/build.xml @@ -164,13 +164,22 @@ Legal: - + + + + + + @@ -209,6 +218,7 @@ Legal: + diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/deployer/DefaultDeployer.java b/proposal/myrmidon/src/java/org/apache/myrmidon/components/deployer/DefaultDeployer.java index dfb5b2ec2..735c0aa6e 100644 --- a/proposal/myrmidon/src/java/org/apache/myrmidon/components/deployer/DefaultDeployer.java +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/deployer/DefaultDeployer.java @@ -9,28 +9,37 @@ package org.apache.myrmidon.components.deployer; import java.io.File; import java.net.URL; +import java.net.MalformedURLException; import java.net.URLClassLoader; +import java.net.JarURLConnection; +import java.util.Arrays; +import java.util.ArrayList; import java.util.Enumeration; +import java.util.jar.Manifest; import java.util.HashMap; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import org.apache.avalon.excalibur.extension.PackageManager; +import org.apache.avalon.excalibur.extension.OptionalPackage; +import org.apache.avalon.excalibur.extension.Extension; import org.apache.avalon.excalibur.i18n.ResourceManager; import org.apache.avalon.excalibur.i18n.Resources; import org.apache.avalon.framework.activity.Initializable; import org.apache.avalon.framework.component.ComponentException; import org.apache.avalon.framework.component.ComponentManager; import org.apache.avalon.framework.component.Composable; +import org.apache.avalon.framework.configuration.ClassicSAXConfigurationHandler; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; -import org.apache.avalon.framework.configuration.SAXConfigurationHandler; import org.apache.avalon.framework.logger.AbstractLoggable; import org.apache.myrmidon.api.Task; -import org.apache.myrmidon.interfaces.type.DefaultTypeFactory; import org.apache.myrmidon.converter.Converter; import org.apache.myrmidon.interfaces.converter.ConverterRegistry; import org.apache.myrmidon.interfaces.deployer.Deployer; import org.apache.myrmidon.interfaces.deployer.DeploymentException; +import org.apache.myrmidon.interfaces.extensions.ExtensionManager; import org.apache.myrmidon.interfaces.role.RoleManager; +import org.apache.myrmidon.interfaces.type.DefaultTypeFactory; import org.apache.myrmidon.interfaces.type.TypeManager; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; @@ -49,9 +58,10 @@ public class DefaultDeployer private final static String TYPE_DESCRIPTOR = "META-INF/ant-types.xml"; - private ConverterRegistry m_converterRegistry; - private TypeManager m_typeManager; - private RoleManager m_roleManager; + private ConverterRegistry m_converterRegistry; + private TypeManager m_typeManager; + private RoleManager m_roleManager; + private PackageManager m_packageManager; /** * Retrieve relevent services needed to deploy. @@ -65,6 +75,10 @@ public class DefaultDeployer m_converterRegistry = (ConverterRegistry)componentManager.lookup( ConverterRegistry.ROLE ); m_typeManager = (TypeManager)componentManager.lookup( TypeManager.ROLE ); m_roleManager = (RoleManager)componentManager.lookup( RoleManager.ROLE ); + + final ExtensionManager extensionManager = + (ExtensionManager)componentManager.lookup( ExtensionManager.ROLE ); + m_packageManager = new PackageManager( extensionManager ); } public void initialize() @@ -75,7 +89,7 @@ public class DefaultDeployer final XMLReader parser = saxParser.getXMLReader(); //parser.setFeature( "http://xml.org/sax/features/namespace-prefixes", false ); - final SAXConfigurationHandler handler = new SAXConfigurationHandler(); + final ClassicSAXConfigurationHandler handler = new ClassicSAXConfigurationHandler(); parser.setContentHandler( handler ); parser.setErrorHandler( handler ); @@ -106,14 +120,16 @@ public class DefaultDeployer checkFile( file ); - final Deployment deployment = new Deployment( file ); - final Configuration descriptor = deployment.getDescriptor(); - final URL[] urls = new URL[] { deployment.getURL() }; - final URLClassLoader classLoader = - new URLClassLoader( urls, Thread.currentThread().getContextClassLoader() ); - try { + final File[] extensions = getOptionalPackagesFor( file ); + final URL[] urls = buildClasspath( file, extensions ); + final Deployment deployment = new Deployment( file ); + final Configuration descriptor = deployment.getDescriptor(); + + final URLClassLoader classLoader = + new URLClassLoader( urls, Thread.currentThread().getContextClassLoader() ); + deployFromDescriptor( descriptor, classLoader, deployment.getURL() ); } catch( final DeploymentException de ) @@ -198,6 +214,85 @@ public class DefaultDeployer } } + private URL[] buildClasspath( final File file, final File[] dependencies ) + throws MalformedURLException + { + final URL[] urls = new URL[ dependencies.length + 1 ]; + + for( int i = 0; i < dependencies.length; i++ ) + { + urls[ i ] = dependencies[ i ].toURL(); + } + + urls[ dependencies.length ] = file.toURL(); + + return urls; + } + + /** + * Retrieve the files for the optional packages required by + * the specified typeLibrary jar. + * + * @param typeLibrary the typeLibrary + * @return the files that need to be added to ClassLoader + */ + private File[] getOptionalPackagesFor( final File typeLibrary ) + throws Exception + { + final URL url = new URL( "jar:" + typeLibrary.getCanonicalFile().toURL() + "!/" ); + final JarURLConnection connection = (JarURLConnection)url.openConnection(); + final Manifest manifest = connection.getManifest(); + final Extension[] available = Extension.getAvailable( manifest ); + final Extension[] required = Extension.getRequired( manifest ); + + if( getLogger().isDebugEnabled() ) + { + final String message1 = + REZ.getString( "available-extensions", Arrays.asList( available ) ); + getLogger().debug( message1 ); + final String message2 = + REZ.getString( "required-extensions", 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() ) + { + final int size = unsatisfied.size(); + for( int i = 0; i < size; i++ ) + { + final Extension extension = (Extension)unsatisfied.get( i ); + final Object[] params = new Object[] + { + extension.getExtensionName(), + extension.getSpecificationVendor(), + extension.getSpecificationVersion(), + extension.getImplementationVendor(), + extension.getImplementationVendorId(), + extension.getImplementationVersion(), + extension.getImplementationURL() + }; + final String message = REZ.format( "missing.extension", params ); + getLogger().warn( message ); + } + + final String message = + REZ.getString( "unsatisfied.extensions", new Integer( size ) ); + throw new Exception( message ); + } + + final OptionalPackage[] packages = + (OptionalPackage[])dependencies.toArray( new OptionalPackage[ 0 ] ); + return OptionalPackage.toFiles( packages ); + } + private void deployFromDescriptor( final Configuration descriptor, final ClassLoader classLoader, final URL url ) diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/deployer/Resources.properties b/proposal/myrmidon/src/java/org/apache/myrmidon/components/deployer/Resources.properties index 986034c78..3138fa3bc 100644 --- a/proposal/myrmidon/src/java/org/apache/myrmidon/components/deployer/Resources.properties +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/deployer/Resources.properties @@ -15,3 +15,10 @@ file-is-dir.error=Could not find application archive at {0} as it is a directory bad-url.error=Unable to form url from file {0}. bad-parser.error=Error configuring parser. bad-read.error=Error reading configuration. + +available-extensions=The list of available extensions for Type Library includes; {0} +required-extensions=The list of required extensions for Type Library includes; {0} +optional-packages-added=The list of "Optional Packages" added to the Type Library includes; {0} +classpath-entries=The list of classpath entrys for the Type Library includes; {0} +missing.extension=Unable to locate an extension that is required by Type Library.\nExtension Name: {0}\nSpecification Vendor: {1}\nSpecification Version: {2}\nImplementation Vendor: {3}\nImplementation Vendor-Id: {4}\nImplementation Version: {5}\nImplementation URL: {6} +unsatisfied.extensions=Missing {0} extensions and thus can not build ClassLoader for Type Library. \ No newline at end of file diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/libs/selftest/ExtensionsTest.java b/proposal/myrmidon/src/java/org/apache/myrmidon/libs/selftest/ExtensionsTest.java new file mode 100644 index 000000000..2d320e67a --- /dev/null +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/libs/selftest/ExtensionsTest.java @@ -0,0 +1,27 @@ +/* + * 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 file. + */ +package org.apache.myrmidon.libs.selftest; + +import org.apache.myrmidon.api.AbstractTask; +import org.apache.myrmidon.api.TaskException; +import org.apache.myrmidon.libs.selftest.extension1.ExtensionsLoadedClass; + +/** + * This is to test whether extension is loaded. + * + * @author Peter Donald + */ +public class ExtensionsTest + extends AbstractTask +{ + public void execute() + throws TaskException + { + ExtensionsLoadedClass.doSomething(); + } +} diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/libs/selftest/extension1/ExtensionsLoadedClass.java b/proposal/myrmidon/src/java/org/apache/myrmidon/libs/selftest/extension1/ExtensionsLoadedClass.java new file mode 100644 index 000000000..7da9e8536 --- /dev/null +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/libs/selftest/extension1/ExtensionsLoadedClass.java @@ -0,0 +1,24 @@ +/* + * 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 file. + */ +package org.apache.myrmidon.libs.selftest.extension1; + +import org.apache.myrmidon.api.AbstractTask; +import org.apache.myrmidon.api.TaskException; + +/** + * This is to test whether extension is loaded. + * + * @author Peter Donald + */ +public class ExtensionsLoadedClass +{ + public static void doSomething() + { + System.out.println( "This was loaded via an extension - yea!" ); + } +} diff --git a/proposal/myrmidon/src/make/primitive-tests.ant b/proposal/myrmidon/src/make/primitive-tests.ant index fe030c785..356efa619 100644 --- a/proposal/myrmidon/src/make/primitive-tests.ant +++ b/proposal/myrmidon/src/make/primitive-tests.ant @@ -41,7 +41,7 @@ Legal: - + @@ -72,4 +72,8 @@ Legal: + + + + \ No newline at end of file diff --git a/proposal/myrmidon/src/make/sample.ant b/proposal/myrmidon/src/make/sample.ant index 691f62ecd..7e9e785d0 100644 --- a/proposal/myrmidon/src/make/sample.ant +++ b/proposal/myrmidon/src/make/sample.ant @@ -6,7 +6,7 @@ Sample build file Authors: - Peter Donald + Peter Donald Legal: Copyright (c) 2000 The Apache Software Foundation. All Rights Reserved. @@ -20,7 +20,7 @@ Legal: - + diff --git a/proposal/myrmidon/src/manifest/selftest-ant-descriptor.xml b/proposal/myrmidon/src/manifest/selftest-ant-descriptor.xml index c6ce854c2..4232cd70e 100644 --- a/proposal/myrmidon/src/manifest/selftest-ant-descriptor.xml +++ b/proposal/myrmidon/src/manifest/selftest-ant-descriptor.xml @@ -6,5 +6,6 @@ + \ No newline at end of file diff --git a/proposal/myrmidon/src/manifest/selftest-extension1.mf b/proposal/myrmidon/src/manifest/selftest-extension1.mf new file mode 100644 index 000000000..c58bdb884 --- /dev/null +++ b/proposal/myrmidon/src/manifest/selftest-extension1.mf @@ -0,0 +1,9 @@ +Manifest-Version: 1.0 +Extension-Name: cornerstone.test.extension +Specification-Title: Avalon Cornerstone Test Extension +Specification-Version: 1.1 +Specification-Vendor: Jakarta Apache +Implementation-Vendor-Id: org.apache.avalon +Implementation-Vendor: Apache Avalon Project +Implementation-Version: 1.0.2 + diff --git a/proposal/myrmidon/src/manifest/selftest.mf b/proposal/myrmidon/src/manifest/selftest.mf new file mode 100644 index 000000000..a1998654a --- /dev/null +++ b/proposal/myrmidon/src/manifest/selftest.mf @@ -0,0 +1,11 @@ +Manifest-Version: 1.0 +Created-By: Apache Avalon Project +Extension-Name: cornerstone.demo.simple +Specification-Title: Avalon Cornerstone SimpleServer Demo Extension +Implementation-Vendor-Id: org.apache.avalon +Implementation-Vendor: Apache Avalon Project +Extension-List: required1 +required1-Extension-Name: cornerstone.test.extension +required1-Specification-Version: 1.0 +required1-Implementation-Version: 1.0.2 +required1-Implementation-Vendor-Id: org.apache.avalon