mcconnell 2002/12/27 08:44:02
Modified: merlin/src/java/org/apache/avalon/merlin/kernel
DefaultKernel.java
Log:
Rewrite of the block loading approach to leverage notion of Block as an extended
appliance.
Revision Changes Path
1.16 +425 -101
avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/kernel/DefaultKernel.java
Index: DefaultKernel.java
===================================================================
RCS file:
/home/cvs/avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/kernel/DefaultKernel.java,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -r1.15 -r1.16
--- DefaultKernel.java 21 Dec 2002 05:18:22 -0000 1.15
+++ DefaultKernel.java 27 Dec 2002 16:44:02 -0000 1.16
@@ -55,6 +55,7 @@
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
+import java.net.MalformedURLException;
import java.util.List;
import java.util.Iterator;
import java.util.Map;
@@ -67,8 +68,20 @@
import java.util.zip.ZipEntry;
import java.net.JarURLConnection;
+import org.apache.avalon.assembly.logging.LoggingManager;
+import org.apache.avalon.assembly.logging.LoggingDescriptor;
+import org.apache.avalon.assembly.logging.DefaultLoggingManager;
+import org.apache.avalon.assembly.engine.EngineClassLoader;
+import org.apache.avalon.assembly.engine.Engine;
+import org.apache.avalon.assembly.engine.model.LibraryDescriptor;
+import org.apache.avalon.assembly.engine.model.ClasspathDescriptor;
+import org.apache.avalon.assembly.util.ExceptionHelper;
+import org.apache.avalon.assembly.appliance.Appliance;
+import org.apache.avalon.assembly.appliance.ApplianceContext;
+import org.apache.avalon.assembly.appliance.DefaultApplianceContext;
import org.apache.avalon.framework.CascadingException;
import org.apache.avalon.framework.logger.Logger;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.activity.Startable;
import org.apache.avalon.framework.activity.Disposable;
@@ -82,46 +95,44 @@
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.service.DefaultServiceManager;
-import org.apache.avalon.meta.model.LoggingDirective;
import org.apache.avalon.merlin.block.Block;
import org.apache.avalon.merlin.block.DefaultBlock;
+import org.apache.avalon.merlin.block.BlockException;
import org.apache.avalon.merlin.container.builder.XMLContainerCreator;
-import org.apache.avalon.assembly.logging.LoggingManager;
-import org.apache.avalon.assembly.logging.LoggingDescriptor;
-import org.apache.avalon.assembly.logging.DefaultLoggingManager;
-import org.apache.avalon.assembly.engine.EngineClassLoader;
-import org.apache.avalon.assembly.engine.Engine;
-import org.apache.avalon.assembly.engine.model.LibraryDescriptor;
-import org.apache.avalon.assembly.engine.model.ClasspathDescriptor;
-import org.apache.avalon.assembly.util.ExceptionHelper;
-import org.apache.avalon.assembly.appliance.Appliance;
-import org.apache.avalon.merlin.block.Block;
-import org.apache.avalon.merlin.block.DefaultBlock;
-import org.apache.avalon.merlin.block.BlockLoader;
+import org.apache.avalon.merlin.container.ContainerDescriptor;
+import org.apache.avalon.merlin.container.ContainerLoader;
import org.apache.avalon.merlin.container.Container;
+import org.apache.avalon.merlin.container.ContainerException;
+import org.apache.avalon.merlin.container.DefaultContainer;
import org.apache.avalon.merlin.service.DefaultRegistry;
import org.apache.avalon.merlin.service.Registry;
+import org.apache.avalon.meta.model.LoggingDirective;
+import org.apache.avalon.meta.model.Profile;
+import org.apache.avalon.meta.info.Type;
/**
* Default kernel implementation. The implementation provides support for
* the following sequence of activties:
*
* <ul>
+ * <li>loading kernel.xml configuration
+ * <li>loading blocks.xml configuration
+ * <li>establishment of the logging system
* <li>loading internal jar-file dependencies
- * <li>establishment of a logging system
- * <li>creation of a root container
- * <li>startup of the container
- * <li>management of interupt conditions or request for kernel disposal
- * <li>error reporting
+ * <li>establishment of a root component management engine
+ * <li>block abstraction that encapsulates a heirachical container model
+ * <li>block configuration repository
+ * <li>block assembly, deployment and decommissioning
+ * <li>management of interupt conditions and kernel disposal
+ * <li>error management
* </ul>
*
- * @see DefaultKernel
- *
* @author <a href="mailto:[EMAIL PROTECTED]">Avalon Development
Team</a>
* @version $Revision$ $Date$
+ * @see DefaultBlock
*/
-public class DefaultKernel extends BlockLoader implements Kernel, Contextualizable,
Configurable, Initializable, Startable, Disposable
+public class DefaultKernel extends ContainerLoader implements Kernel,
Contextualizable, Initializable, Startable, Disposable
{
//==============================================================
// static
@@ -133,16 +144,23 @@
*/
private static LoggingManager LOGGING;
+ protected static final String BLOCK_XML_ENTRY = "BLOCK-INF/block.xml";
+
//==============================================================
// state
//==============================================================
/**
- * The supplied configuration.
+ * The kernel configuration.
*/
private Configuration m_config;
/**
+ * The block configuration.
+ */
+ private Configuration m_configuration;
+
+ /**
* The application home directory.
*/
private File m_home;
@@ -184,20 +202,21 @@
//==============================================================
/**
- * <p>Application of a runtime context to this appliance.
+ * <p>Application of a runtime context to the kernel.
* Context entries that may be supplied to an appliance manager are
* detailed in the following table.</p>
- * <table>
- * <tr>
- *
<td><strong>key</strong></td><td><strong>type</strong></td><td><strong>description</strong></td>
+ * <table BORDER="1" CELLSPACING="0" CELLPADDING="3" WIDTH="90%" >
+ * <tr BGCOLOR="#EEEEFF">
+ * <td><b>key</b></td><td><b>type</b></td><td><b>description</b></td>
* </tr>
* <tr>
- * <td>urn:assembly:home</td>
+ * <td><code>urn:assembly:home</code></td>
* <td>{@link java.io.File}</td>
* <td>The kernel home directory.</td>
* </tr>
* </table>
* @param context the runtime context
+ * @exception ContextException if an error occurs during the contextualization
phase
*/
public void contextualize( Context context ) throws ContextException
{
@@ -205,30 +224,16 @@
}
//==============================================================
- // Configurable
- //==============================================================
-
- /**
- * Supply of the static configuration for the kernel.
- * @param config the static configuration
- */
- public void configure( Configuration config ) throws ConfigurationException
- {
- if( config == null )
- {
- throw new NullPointerException( "config" );
- }
- m_config = config;
- }
-
- //==============================================================
// Initializable
//==============================================================
/**
- * Initialization of the kernel.
+ * Initialization of the kernel. The implementation hanldes the
+ * establishment of the logging system, bootstraps the assembly
+ * engine, loads all blocks declared in the kernel configuration,
+ * initiates block assembly and list available services.
*
- * @exception if an initialization error occurs.
+ * @exception Exception if an initialization error occurs.
*/
public void initialize() throws Exception
{
@@ -236,9 +241,39 @@
{
throw new IllegalStateException("context");
}
- if( m_config == null )
+
+ //
+ // load the kernel configuration
+ //
+
+ File kernelFile = new File( m_home, "kernel.xml" );
+ try
{
- throw new IllegalStateException("configuration");
+ m_config = getConfiguration( kernelFile );
+ }
+ catch( Throwable e )
+ {
+ final String error =
+ "Unable to load kernel configuration from file: "
+ + kernelFile;
+ throw new CascadingException( error, e );
+ }
+
+ //
+ // load the blocks configuration
+ //
+
+ File blocksFile = new File( m_home, "blocks.xml" );
+ try
+ {
+ m_configuration = getConfiguration( blocksFile );
+ }
+ catch( Throwable e )
+ {
+ final String error =
+ "Unable to load blocks configuration from file: "
+ + kernelFile;
+ throw new CascadingException( error, e );
}
//
@@ -247,14 +282,13 @@
try
{
- final String root = "/";
if( LOGGING == null )
{
- LOGGING = bootstrapLoggingManager( root );
+ LOGGING = bootstrapLoggingManager( Container.PATH_SEPERATOR );
Configuration categoriesConfig = m_config.getChild( "categories" );
LoggingDirective categories =
- CREATOR.createLoggingDirective( root, categoriesConfig );
- LOGGING.addCategories( root, categories );
+ CREATOR.createLoggingDirective( Container.PATH_SEPERATOR,
categoriesConfig );
+ LOGGING.addCategories( Container.PATH_SEPERATOR, categories );
}
final String sys = "/sys";
m_local = LOGGING.getLoggerForCategory( sys );
@@ -267,7 +301,7 @@
throw new CascadingException( error, e );
}
- String domain = m_config.getChild( "system" ).getChild( "host" ).getValue(
"localhost" );
+ String domain = m_config.getChild( "system" ).getAttribute( "host",
"localhost" );
m_registry = new DefaultRegistry( domain );
//
@@ -276,7 +310,7 @@
if( getLogger().isInfoEnabled() )
{
- getLogger().info( "initialization from: " + m_home );
+ getLogger().info( "initialization from: " + m_home + " on " + domain );
}
//
@@ -314,14 +348,12 @@
Block[] blocks;
try
{
- ClasspathDescriptor path =
- CREATOR.createClasspathDescriptor( m_config.getChild( "blocks" ) );
- URL[] urls = ClasspathDescriptor.expand( m_home, path );
Context system = getSystemContext();
- blocks = loadBlocks( m_engine, m_registry, m_home, urls, system );
+ blocks = loadPhysicalBlocks( m_configuration, system );
for( int i=0; i<blocks.length; i++ )
{
- m_blocks.add( blocks[i] );
+ Block block = blocks[i];
+ m_blocks.add( block );
}
}
catch( Throwable e )
@@ -343,21 +375,18 @@
{
getLogger().info( "commencing structural assembly phase" );
}
-
+
for( int i=0; i<blocks.length; i++ )
{
Block block = blocks[i];
- Appliance appliance = block.getAppliance();
try
{
- appliance.assemble();
- Container container = (Container)appliance.access();
- m_containers.put( block, container );
+ block.assemble();
}
catch( Throwable e )
{
final String error =
- "Unable to deploy root container: " + appliance;
+ "Unable to deploy block: " + block + " due to a structural
assembly failure.";
String log = ExceptionHelper.packException( error, e );
if( getLogger().isErrorEnabled() )
{
@@ -366,22 +395,7 @@
throw new KernelException( error, e );
}
}
-
- //
- // Initiate component assembly on all blocks.
- //
-
- if( getLogger().isInfoEnabled() )
- {
- getLogger().info( "commencing composite assembly phase" );
- }
- for( int i=0; i<blocks.length; i++ )
- {
- Block block = blocks[i];
- Container container = (Container) m_containers.get( block );
- container.assemble();
- }
-
+
//
// list the registered services
//
@@ -407,7 +421,7 @@
/**
* Start the kernel.
*
- * @exception if an startup error occurs.
+ * @exception Exception if an startup error occurs.
*/
public void start() throws Exception
{
@@ -423,18 +437,14 @@
while( iterator.hasNext() )
{
Block block = (Block) iterator.next();
- Container container = (Container) m_containers.get( block );
- if( container != null )
- {
- container.startup();
- }
+ block.startup();
}
}
/**
* Stop the kernel.
*
- * @exception if an shutdown error occurs.
+ * @exception Exception if an shutdown error occurs.
*/
public void stop() throws Exception
{
@@ -450,11 +460,7 @@
while( iterator.hasNext() )
{
Block block = (Block) iterator.next();
- Container container = (Container) m_containers.get( block );
- if( container != null )
- {
- container.shutdown();
- }
+ block.shutdown();
}
}
@@ -479,13 +485,7 @@
while( iterator.hasNext() )
{
Block block = (Block) iterator.next();
- Container container = (Container) m_containers.get( block );
- if( container != null )
- {
- m_containers.remove( block );
- block.getAppliance().release( container );
- block.getAppliance().terminate();
- }
+ block.terminate();
}
if( getLogger() != null )
@@ -501,6 +501,247 @@
// internals
//==============================================================
+ /**
+ * Load all of the blocks declared in the block.xml configuration.
+ * The configuration contains an arbitary number of "block" elements
+ * each containing the name and path of a jar file to be loaded together
+ * with client supplied configuration data.
+ *
+ * @param conf the configuration loaded from the blocks.xml file
+ * @param system the system context
+ * @return the set of blocks
+ */
+ private Block[] loadPhysicalBlocks( Configuration conf, Context system )
+ throws Exception
+ {
+ Configuration[] configs = conf.getChildren("block");
+ getLogger().info("block count = " + configs.length );
+
+ ArrayList blocks = new ArrayList();
+ for( int i=0; i<configs.length; i++ )
+ {
+ Configuration config = configs[i];
+ try
+ {
+ Block block = loadPhysicalBlock( config, system );
+ blocks.add( block );
+ }
+ catch( Throwable e )
+ {
+ final String error =
+ "Error during block deployment for block: " + i;
+ throw new BlockException( error, e );
+ }
+ }
+ return (Block[]) blocks.toArray( new Block[0] );
+ }
+
+ /**
+ * Load a single block defintion. The defintion is a configuration
+ * element named "block" that contains a name and a jar file location.
+ * The block name is defined by the block name attribute and the location
+ * of the jar file if defined by the value of the "path" attribute. The
+ * implementation loads a block defintion for the supplied jar file
+ * and uses this to establish a subsidiary classloader and appliance
+ * to handle the implicit implementation container. The block returned
+ * from this method is the implicit appliance handling the root container.
+ *
+ * @param config the block declaration
+ * @param system the system context
+ * @return the block
+ * @exception Exception if a block loading error occurs
+ */
+ private Block loadPhysicalBlock( Configuration config, Context system )
+ throws Exception
+ {
+
+ //
+ // get the basic information for the block declaration
+ // in the block.xml file
+ //
+
+ String name = config.getAttribute( "name" );
+ String path = config.getAttribute("path");
+ Logger logger = getLogger().getChildLogger( name );
+ logger.debug( "[" + path + "] as [" + name + "]");
+
+ //
+ // load the jar file referenced by the block declaration and get its
+ // manifest
+ //
+
+ URL url = new File( m_home, path ).getCanonicalFile().toURL();
+ JarFile jar = getJarFile( url );
+ Manifest manifest = jar.getManifest();
+ if( !DefaultBlock.isBlock( manifest ) )
+ {
+ final String warning =
+ "Manifest does not declare a block within the resource: " + url;
+ throw new IllegalArgumentException( warning );
+ }
+
+ String packageName = DefaultBlock.getPackageName( manifest );
+ if( packageName == null )
+ {
+ final String error = "Missing package declaration in block: " + url;
+ throw new IllegalArgumentException( error );
+ }
+
+ //
+ // get the blocks packaged configuration and use that to establish
+ // the engine to supplied to the block
+ //
+
+ Configuration base = getBlockConfiguration( jar );
+ Configuration engineConfig = base.getChild( "engine" );
+ EngineClassLoader engine = createChildEngine( m_engine, m_home,
engineConfig, url, logger );
+
+ //
+ // create an appliance context and add to it a registry derived from the
+ // parent registry
+ //
+
+ String partition = name + Container.PATH_SEPERATOR;
+ Configuration containment = base.getChild( "implementation" );
+ ContainerDescriptor descriptor = createContainerDescriptor( name, engine,
containment );
+ Registry registry = m_registry.createChild( name );
+ List list = createChildContainers( engine, registry, partition,
containment, logger );
+
+ //
+ // create the appliance context for the container
+ //
+
+ Map map = new Hashtable();
+ map.put("urn:assembly:engine.classloader", engine );
+ map.put("urn:merlin:container.containers", list );
+ map.put("urn:merlin:container.descriptor", descriptor );
+ map.put("urn:merlin:container.registry", registry );
+
+ DefaultApplianceContext context = new DefaultApplianceContext( descriptor );
+ context.setName( name );
+ context.setDeploymentContext( map );
+ context.setApplianceClassname( DefaultBlock.class.getName() );
+
+ return (Block) engine.createAppliance( context, false );
+
+ }
+
+ /**
+ * Create a descriptor of a container populated with the defintintion
+ * of the components it is managing.
+ *
+ * @param name the container name
+ * @param engine the classloader to use
+ * @param config the confuration describing the containment scenario
+ * @return the container descriptor
+ */
+ private ContainerDescriptor createContainerDescriptor(
+ String name, EngineClassLoader engine, Configuration config )
+ throws Exception
+ {
+ String classname = config.getAttribute( "class",
DefaultContainer.class.getName() );
+ Type type = engine.getRepository().getTypeManager().getType( classname );
+ ContainerDescriptor descriptor = CREATOR.createContainerDescriptor( type,
config, name );
+ Configuration[] children = config.getChildren( "appliance" );
+ for( int i=0; i<children.length; i++ )
+ {
+ Profile profile = createProfile( name, engine, children[i] );
+ engine.getRepository().getProfileManager().addProfile( profile );
+ descriptor.addComponent( profile );
+ }
+ return descriptor;
+ }
+
+ /**
+ * Creation of a set of child container relative to a set of parent parameters.
+ *
+ * @param engine the parent classloader
+ * @param registry the parent registry
+ * @param partition the partition to be applied to the child containers
+ * @param config a confiuration containing a set of subsidiary container elements
+ * @param logger the logger from which child loggers shall be created
+ * @return a list of appliance instances each representing a container container
+ */
+ private List createChildContainers(
+ EngineClassLoader engine, Registry registry, String partition,
+ Configuration config, Logger logger )
+ throws Exception
+ {
+ List list = new ArrayList();
+ Configuration[] children = config.getChildren( "container" );
+
+ for( int i=0; i<children.length; i++ )
+ {
+ Configuration child = children[i];
+ String name = child.getAttribute( "name" );
+ Logger log = logger.getChildLogger( name );
+ EngineClassLoader loader = createChildEngine(
+ engine, m_home, child.getChild("engine"), log );
+ Registry reg = registry.createChild( name );
+
+ Appliance appliance = createContainerAppliance(
+ loader, reg, partition, name, child, log );
+ list.add( appliance );
+ }
+
+ return list;
+ }
+
+ /**
+ * Create a single containment appliance.
+ *
+ * @param engine the containers classloader
+ * @param registry the compoent registry to apply to the container
+ * @param partition the partition to assigne to the container
+ * @param name the appliance name
+ * @param config the configuration of the container
+ * @param logger the logging channel to apply to classloaders created for child
containers
+ * @return the containment appliance
+ * @exception if an error occurs during creation of the containment appliance
+ */
+ private Appliance createContainerAppliance(
+ EngineClassLoader engine, Registry registry, String partition, String name,
+ Configuration config, Logger logger )
+ throws Exception
+ {
+ String subPartition = partition + name + Container.PATH_SEPERATOR;
+ List list = createChildContainers( engine, registry, subPartition, config,
logger );
+ ContainerDescriptor descriptor = createContainerDescriptor( name, engine,
config );
+
+ //
+ // create the appliance context for the container
+ //
+
+ Map map = new Hashtable();
+ map.put("urn:assembly:engine.classloader", engine );
+ map.put("urn:merlin:container.containers", list );
+ map.put("urn:merlin:container.descriptor", descriptor );
+ map.put("urn:merlin:container.registry", registry );
+ map.put("urn:avalon:partition.name", partition );
+
+ DefaultApplianceContext context = new DefaultApplianceContext( descriptor );
+ context.setName( name );
+ context.setDeploymentContext( map );
+ context.setPartitionName( partition );
+
+ //
+ // create the containement appliance
+ //
+
+ try
+ {
+ context.makeReadOnly();
+ return engine.createAppliance( context, false );
+ }
+ catch( Throwable e )
+ {
+ final String error =
+ "Unable to create containment appliance: " + name;
+ throw new ContainerException( error, e );
+ }
+
+ }
+
private LoggingManager bootstrapLoggingManager( String root ) throws Exception
{
if( m_config == null )
@@ -620,5 +861,88 @@
m_system = context;
}
return m_system;
+ }
+
+ private Configuration getConfiguration( final File file ) throws
ConfigurationException
+ {
+ try
+ {
+ DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
+ InputStream is = new FileInputStream( file );
+ if( is == null )
+ {
+ throw new ConfigurationException(
+ "Could not load the configuration resource \"" + file + "\"" );
+ }
+ return builder.build( is );
+ }
+ catch( Throwable e )
+ {
+ final String error = "Unable to create configuration from file: " +
file;
+ throw new ConfigurationException( error, e );
+ }
+ }
+
+ private JarFile getJarFile( URL url ) throws MalformedURLException
+ {
+ URL xurl;
+
+ String protocol = url.getProtocol();
+ if( protocol.equals("jar") )
+ {
+ xurl = url;
+ }
+ else
+ {
+ xurl = new URL( "jar:" + url.toString() + "!/" );
+ }
+
+ try
+ {
+ JarURLConnection connection = (JarURLConnection) xurl.openConnection();
+ return connection.getJarFile();
+ }
+ catch( Throwable ioe )
+ {
+ final String error =
+ "Unexpected Exception while reading jar file from url: " + xurl + "
cause: " + ioe;
+ throw new RuntimeException( error );
+ }
+ }
+
+ private Configuration getBlockConfiguration( JarFile jar ) throws Exception
+ {
+ if( jar == null )
+ {
+ throw new NullPointerException( "jar" );
+ }
+
+ ZipEntry entry = jar.getEntry( BLOCK_XML_ENTRY );
+ if( entry == null )
+ {
+ if( getLogger().isDebugEnabled() )
+ {
+ final String msg = "No block configuration - applying defaults.";
+ getLogger().debug( msg );
+ }
+ return new DefaultConfiguration( "block", jar.getName() );
+ }
+
+ try
+ {
+ DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
+ InputStream is = jar.getInputStream( entry );
+ if( is == null )
+ {
+ throw new RuntimeException(
+ "Could not load the configuration resource \"" + jar.getName()
+ "\"" );
+ }
+ return builder.build( is );
+ }
+ catch( Throwable e )
+ {
+ final String error = "Unable to create configuration from jar file: " +
jar.getName();
+ throw new BlockException( error, e );
+ }
}
}
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>