mcconnell 2003/01/15 01:14:52 Modified: merlin .cvsignore blocks.xml build.xml default.properties kernel.xml merlin.properties merlin/src/java/org/apache/avalon/merlin/block Block.java DefaultBlock.java merlin/src/java/org/apache/avalon/merlin/container Container.java ContainerLoader.java DefaultContainer.java DefaultContainer.xinfo StateEvent.java merlin/src/java/org/apache/avalon/merlin/kernel DefaultKernel.java KernelLoader.java merlin/src/test/config block.xml merlin/src/test/org/apache/avalon/playground StandardComponent.java Removed: merlin external.xml Log: Rationalization of the containment strategy enabling simplicifation of the container interface and better seperation of container concerns (under Block) as distinct from runtime managemnt (uner Container). Revision Changes Path 1.3 +1 -0 avalon-sandbox/merlin/.cvsignore Index: .cvsignore =================================================================== RCS file: /home/cvs/avalon-sandbox/merlin/.cvsignore,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- .cvsignore 3 Dec 2002 07:08:02 -0000 1.2 +++ .cvsignore 15 Jan 2003 09:14:50 -0000 1.3 @@ -7,3 +7,4 @@ extensions distributions common +working \ No newline at end of file 1.3 +14 -10 avalon-sandbox/merlin/blocks.xml Index: blocks.xml =================================================================== RCS file: /home/cvs/avalon-sandbox/merlin/blocks.xml,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- blocks.xml 28 Dec 2002 01:35:38 -0000 1.2 +++ blocks.xml 15 Jan 2003 09:14:50 -0000 1.3 @@ -3,22 +3,26 @@ The blocks.xml file contains the declaration of the blocks to be loaded and assembled by the kernel. Each block element declares a block name, a path to a single jar file, an engine configuration (which may -include declaration of additional extension directories and a block -classpath), anad tagged configuration fragments. +include declaration of additional extension directories a block +classpath), and tagged configuration fragments. --> <blocks> - <!-- - DEV NOTE: - Path statements need to declared as just the filename and - the kernel should be resonsible for resolving the full path. The current - inclusion of a directory is temporary because it is making assumptions - about the deployment context. - --> + <block name="james" src="src/test/config/james.xml" enabled="false" > + <configuration target="dns"> + <servers> + <server>212.198.0.66</server> + <server>212.198.0.67</server> + <server>194.51.32.65</server> + </servers> + <authoritative>false</authoritative> + </configuration> + </block> - <block name="playground" path="../assembly/build/lib/avalon-assembly-demo-1.0.jar"/> + <block name="playground" path="../assembly/build/lib/avalon-assembly-demo-1.0.jar" enabled="true"/> - <block name="demo" path="../merlin/build/lib/avalon-merlin-demo-1.0.jar"> + <block name="demo" path="../merlin/build/lib/avalon-merlin-demo-1.0.jar" enabled="true"> <configuration target="test"> <configuration target="standard"> <message>Client supplied message.</message> 1.10 +6 -2 avalon-sandbox/merlin/build.xml Index: build.xml =================================================================== RCS file: /home/cvs/avalon-sandbox/merlin/build.xml,v retrieving revision 1.9 retrieving revision 1.10 diff -u -r1.9 -r1.10 --- build.xml 7 Jan 2003 04:24:16 -0000 1.9 +++ build.xml 15 Jan 2003 09:14:50 -0000 1.10 @@ -28,6 +28,7 @@ <pathelement location="${avalon-assembly.jar}"/> <pathelement location="${excalibur-logger.jar}"/> <pathelement location="${excalibur-configuration.jar}"/> + <pathelement location="${excalibur-pool.jar}"/> <pathelement location="${excalibur-thread.jar}"/> <pathelement location="${excalibur-event.jar}"/> <pathelement location="${util.concurrent.jar}"/> @@ -204,17 +205,20 @@ <mkdir dir="${common.dir}"/> <copy todir="${common.dir}" file="${avalon-framework.jar}"/> <copy todir="${common.dir}" file="${excalibur-i18n.jar}"/> - <copy todir="${common.dir}" file="${excalibur-configuration.jar}"/> <copy todir="${common.dir}" file="${avalon-meta.jar}"/> <copy todir="${common.dir}" file="${logkit.jar}"/> + <copy todir="${common.dir}" file="${excalibur-configuration.jar}"/> <mkdir dir="${lib.dir}"/> <copy todir="${lib.dir}" file="${build.lib}/${jar.name}"/> + <copy todir="${lib.dir}" file="${excalibur-collections.jar}"/> <copy todir="${lib.dir}" file="${excalibur-extension.jar}"/> <copy todir="${lib.dir}" file="${excalibur-logger.jar}"/> <copy todir="${lib.dir}" file="${excalibur-event.jar}"/> <copy todir="${lib.dir}" file="${commons-collections.jar}"/> <copy todir="${lib.dir}" file="${util.concurrent.jar}"/> + <copy todir="${lib.dir}" file="${excalibur-threadcontext.jar}"/> + <copy todir="${lib.dir}" file="${excalibur-pool.jar}"/> <copy todir="${lib.dir}" file="${excalibur-thread.jar}"/> <copy todir="${lib.dir}" file="${avalon-lifecycle.jar}"/> <copy todir="${lib.dir}" file="${avalon-assembly.jar}"/> @@ -414,7 +418,7 @@ debug="${build.debug}" optimize="${build.optimize}" deprecation="${build.deprecation}" - target="1.2"> + target="1.3"> <classpath refid="project.class.path" /> <include name="**/playground/*.java"/> </javac> 1.6 +19 -4 avalon-sandbox/merlin/default.properties Index: default.properties =================================================================== RCS file: /home/cvs/avalon-sandbox/merlin/default.properties,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- default.properties 6 Jan 2003 01:05:53 -0000 1.5 +++ default.properties 15 Jan 2003 09:14:50 -0000 1.6 @@ -8,7 +8,7 @@ name=avalon-merlin Name=Merlin Service Management System dir-name=merlin -version=2.1 +version=1.0 package-version=2.1 demo-version=1.0 year=2002 @@ -72,10 +72,25 @@ commons-collections.jar=${excalibur-event.home}/lib/commons-collections-20021002.jar # ----- Excalibur thread, version 1.0 or later ----- +excalibur-collections.home=${excalibur.home}/collections +excalibur-collections.lib=${excalibur-collections.home}/build/lib +excalibur-collections.jar=${excalibur-collections.lib}/excalibur-collections-1.0.jar + +# ----- Excalibur thread, version 1.0 or later ----- excalibur-thread.home=${excalibur.home}/thread excalibur-thread.lib=${excalibur-thread.home}/build/lib excalibur-thread.jar=${excalibur-thread.lib}/excalibur-thread-1.0.jar +# ----- Excalibur threadcontext, version 1.0 or later ----- +excalibur-threadcontext.home=${excalibur.home}/threadcontext +excalibur-threadcontext.lib=${excalibur-threadcontext.home}/build/lib +excalibur-threadcontext.jar=${excalibur-threadcontext.lib}/excalibur-threadcontext-1.0.jar + +# ----- Excalibur pool ----- +excalibur-pool.home=${excalibur.home}/pool +excalibur-pool.lib=${excalibur-pool.home}/build/lib +excalibur-pool.jar=${excalibur-pool.lib}/excalibur-pool-1.1.jar + # ----- Logkit ----- logkit.home=${basedir}/../../jakarta-avalon-logkit logkit.lib=${logkit.home}/build/lib @@ -123,8 +138,8 @@ dist.name = ${name}-${version} # name of jar file -jar.name = ${name}-${version}.jar -bootstrap.jar = ${name}-bootstrap-${version}.jar +jar.name = ${name}-${package-version}.jar +bootstrap.jar = ${name}-bootstrap-${package-version}.jar # property indicating directory where all distribution archives are placed dist.base = distributions @@ -135,7 +150,7 @@ lib.dir = lib demo.name = demo demo.jar = ${name}-${demo.name}-${demo-version}.jar -sar.name = ${name}-${version}.sar +sar.name = ${name}-${package-version}.sar # misc. 1.13 +11 -23 avalon-sandbox/merlin/kernel.xml Index: kernel.xml =================================================================== RCS file: /home/cvs/avalon-sandbox/merlin/kernel.xml,v retrieving revision 1.12 retrieving revision 1.13 diff -u -r1.12 -r1.13 --- kernel.xml 9 Jan 2003 03:59:39 -0000 1.12 +++ kernel.xml 15 Jan 2003 09:14:50 -0000 1.13 @@ -1,37 +1,25 @@ <kernel> - <system host="localhost"> - <configuration path="configuration.xml"/> - </system> + <!-- + The kerenel can contain the following declarations: + 1. a system descriptor + 2. a logging descriptor + 3. a logging catagories descriptor that establishes logging priority + for the kernel messages + 4. an engine descriptor + --> + + <system host="localhost"/> <logging target="default" priority="DEBUG"> <category name="/sys/logger" priority="WARN"/> - <!-- - <target name="default"> - <file location="kernel.log" /> - </target> - --> </logging> <categories> <category name="/sys" priority="INFO"/> </categories> - <!-- - Definition of the root engine classloader. - --> - <!-- - <engine> - <library dir="."> - <include name="some-additional-extension-dir"/> - </library> - <classpath> - <fileset dir="some-directory"> - <include name="something.jar"/> - </fileset> - </classpath> - </engine> - --> + <engine/> </kernel> 1.2 +2 -2 avalon-sandbox/merlin/merlin.properties Index: merlin.properties =================================================================== RCS file: /home/cvs/avalon-sandbox/merlin/merlin.properties,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- merlin.properties 6 Jan 2003 01:05:53 -0000 1.1 +++ merlin.properties 15 Jan 2003 09:14:50 -0000 1.2 @@ -34,7 +34,7 @@ # Filename of the blocks configuration. The file will be resolved relative # to the Merlin home directory. The default value is [blocks.xml] # -#merlin.blocks.xml=blocks.xml +#merlin.blocks.xml=./blocks.xml # # merlin.bootstrap.debug @@ -42,6 +42,6 @@ # of a logger) will be sent to System.out. If false, not bootstrap messages will # be displayed. Default value is false. # -#merlin.bootstrap.debug=false +merlin.bootstrap.debug=false 1.8 +20 -5 avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/block/Block.java Index: Block.java =================================================================== RCS file: /home/cvs/avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/block/Block.java,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- Block.java 9 Jan 2003 11:26:25 -0000 1.7 +++ Block.java 15 Jan 2003 09:14:51 -0000 1.8 @@ -67,25 +67,40 @@ static final String AVALON_BLOCK_KEY = "Avalon-Block"; /** + * Deploy the block during which appliances managed by the block + * will be established as executing components. + * + * @exception Exception if a deployment error occurs + */ + void deploy() throws Exception; + + /** + * Decommission the block. + * + * @exception Exception if a deployment error occurs + */ + void decommission() throws Exception; + + /** * Startup the block and startup all subsidiary blocks. * @exception Exception if a startup error occurs */ - void startup() throws Exception; + //void startup() throws Exception; /** * Suspend the block and all subsidiary blocks. */ - void suspend(); + //void suspend(); /** * Resume the block and all subsidiary blocks. */ - void resume(); + //void resume(); /** * Shutdown the block. * @exception Exception if a shutdown error occurs */ - void shutdown() throws Exception; + //void shutdown() throws Exception; } 1.13 +508 -141 avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/block/DefaultBlock.java Index: DefaultBlock.java =================================================================== RCS file: /home/cvs/avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/block/DefaultBlock.java,v retrieving revision 1.12 retrieving revision 1.13 diff -u -r1.12 -r1.13 --- DefaultBlock.java 9 Jan 2003 11:26:25 -0000 1.12 +++ DefaultBlock.java 15 Jan 2003 09:14:51 -0000 1.13 @@ -6,6 +6,7 @@ import java.io.IOException; import java.net.URL; import java.util.List; +import java.util.Iterator; import java.util.ArrayList; import java.util.jar.Attributes; import java.util.jar.Manifest; @@ -17,6 +18,7 @@ import org.apache.avalon.assembly.appliance.DefaultAppliance; import org.apache.avalon.assembly.appliance.ApplianceContext; import org.apache.avalon.assembly.appliance.ApplianceException; +import org.apache.avalon.assembly.appliance.DependencyGraph; import org.apache.avalon.assembly.engine.EngineClassLoader; import org.apache.avalon.assembly.locator.Locator; import org.apache.avalon.assembly.locator.Contextualizable; @@ -24,6 +26,7 @@ import org.apache.avalon.assembly.lifestyle.LifestyleService; import org.apache.avalon.assembly.lifecycle.AssemblyService; import org.apache.avalon.assembly.lifecycle.AssemblyException; +import org.apache.avalon.assembly.util.ExceptionHelper; import org.apache.avalon.framework.logger.Logger; import org.apache.avalon.framework.logger.AbstractLogEnabled; import org.apache.avalon.framework.activity.Initializable; @@ -44,6 +47,10 @@ import org.apache.avalon.meta.info.DependencyDescriptor; import org.apache.avalon.meta.model.builder.XMLProfileCreator; import org.apache.avalon.meta.model.Profile; +import org.apache.avalon.merlin.service.Registry; +import org.apache.avalon.merlin.kernel.DefaultKernel; +import org.apache.avalon.assembly.appliance.DefaultApplianceContext; +import org.apache.excalibur.configuration.ConfigurationUtil; /** * The default implementation of a Block. The implementation provides @@ -61,6 +68,10 @@ public static final Attributes.Name BLOCK_PACKAGE = new Attributes.Name( "Block-Package" ); public static final Attributes.Name OLD_BLOCK_NAME = new Attributes.Name( "Block-Name" ); + private static final int DISASSEMBLED = 1000; + private static final int TERMINATED = 2000; + private static final int DISPOSED = 3000; + public static String getPackageName( Manifest manifest ) { Attributes attributes = manifest.getAttributes( Block.AVALON_BLOCK_KEY ); @@ -93,6 +104,9 @@ // state //------------------------------------------------------------------------------- + /** + * The engine classloader. + */ EngineClassLoader m_engine; /** @@ -134,6 +148,74 @@ */ private BlockException m_error; + /** + * The apliance context. + */ + private ApplianceContext m_applianceContext; + + /** + * The container meta info descriptor. + */ + private ContainerDescriptor m_descriptor; + + /** + * The block configuration. + */ + private Configuration m_config; + + /** + * The service registry assigned to the block. + */ + private Registry m_registry; + + /** + * List of the appliance instances that the block is + * managing. + */ + private List m_appliances = new ArrayList(); + + /** + * List of the subsidiary blocks. + */ + private List m_blocks = new ArrayList(); + + /** + * Map of container objects keyed by block. + */ + private Map m_containers = new Hashtable(); + + /** + * Reference to the set of appliances to be terminated. The set + * appliances is based on the shutdown graph resolved during the + * disassembly phase. + */ + private Appliance[] m_shutdown; + + /** + * List of the commonents that have been established explicity by the + * block during deployment. + */ + private Map m_components = new Hashtable(); + + /** + * Flag holding the deoplyed state of the block. + */ + private boolean m_deployed = false; + + /** + * Flag holding the decommissioned state of the block. + */ + private boolean m_decommissioned = false; + + /** + * Flag holding the disassembled state of the block. + */ + private boolean m_disassembled = false; + + /** + * Flag holding the terminated state of the block. + */ + private boolean m_terminated = false; //===================================================================== // Contextualizable @@ -148,12 +230,23 @@ { super.contextualize( context ); m_engine = (EngineClassLoader) context.get( "urn:assembly:engine" ); + m_applianceContext = (ApplianceContext) context.get( "urn:assembly:appliance.context" ); + m_descriptor = (ContainerDescriptor) context.get( "urn:merlin:container.descriptor" ); + m_registry = (Registry) context.get( "urn:merlin:container.registry" ); + m_config = (Configuration) context.get( "urn:merlin:container.configuration" ); + m_blocks = (List) context.get( "urn:merlin:container.containers" ); } //===================================================================== // Initializable //===================================================================== + /** + * Initialization of the blocks during which appliance instances managed by the blocks + * will be established. + * + * @exception Exception if a block initialization error occurs + */ public void initialize() throws Exception { super.initialize(); @@ -161,162 +254,237 @@ { throw new IllegalStateException( "context" ); } - m_thread = new Thread( this, super.getPath() ); - m_thread.setContextClassLoader( m_engine ); - m_thread.start(); - } + + // + // construct the appliance instances managed by this block + // + + Profile[] profiles = m_descriptor.getComponents(); + for( int i=0; i<profiles.length; i++ ) + { + Profile profile = profiles[i]; + if( getLogger().isDebugEnabled() ) + { + final String message = "profile: " + profile; + getLogger().debug( message ); + } + + Configuration config = + DefaultKernel.getNamedConfiguration( m_config, profile.getName() ); + + DefaultApplianceContext context = + new DefaultApplianceContext( profile ); + context.setPartitionName( getPath() ); + context.setConfiguration( config ); + context.makeReadOnly(); + + Appliance appliance = m_engine.createAppliance( context, true ); + m_appliances.add( appliance ); + m_registry.bind( appliance ); + } + } //------------------------------------------------------------------------------- - // Runnable + // Appliance //------------------------------------------------------------------------------- - /** - * Starts the thread of execution for the block. This operation is - * invoked by the container constructor. - */ - public void run() + /** + * Assemble the appliance. The implementation extends the default + * appliance behavior by instantiating the container it is managing, + * and applying assembly to the container instance. This results in + * container creating appliance instances for the component is is + * managing together with assembly of its sub-containers. + * + * @exception AssemblyException if an error occurs during appliance assembly + */ + public void assemble( DependencyGraph graph ) throws AssemblyException { - if( !m_running ) + if( m_assembled ) { - m_running = true; + return; + } + + if( getLogger().isDebugEnabled() ) + { + getLogger().debug("block assembly: " + System.identityHashCode( graph ) ); } + // + // add the dependecy graph to the context that will be used to establish + // the container so that the container can handle orderly startup and + // shutdown of compoents + // + + Map context = m_applianceContext.getDeploymentContext(); + context.put( "urn:merlin:container.dependency-graph", graph ); + context.put( "urn:merlin:container.listener", this ); + + // + // assemble the components handled by the block + // + try { + assembleComponents( graph ); + } + catch( AssemblyException e ) + { + final String error = + "Assembly failure attributable to embedded appliance."; + throw new AssemblyException( error, e ); + } - // - // while desired state of the hosted container is something - // other than DISPOSED, check if the desired state is different - // from the current state reported by the container, and if - // so initiate a container state change - // + // + // assemble the subsidiary blocks + // - while( m_action.intValue() < StateEvent.DISPOSED ) + Iterator iterator = m_blocks.iterator(); + while( iterator.hasNext() ) + { + Block block = (Block) iterator.next(); + + if( getLogger().isDebugEnabled() ) { - synchronized( m_action ) - { - if( m_state != m_action.intValue() ) - { - switch( m_action.intValue() ) - { - case StateEvent.STARTED: - if( m_state == StateEvent.INITIALIZED ) - { - m_container.startup(); - } - else if( m_state == StateEvent.SUSPENDED ) - { - m_container.resume(); - } - break; - case StateEvent.SUSPENDED: - if( m_state == StateEvent.STARTED ) - { - m_container.suspend(); - } - break; - case StateEvent.STOPPED: - if( ( m_state == StateEvent.STARTED ) - || ( m_state == StateEvent.SUSPENDED ) ) - { - m_container.shutdown(); - } - break; - } - } - } - sleep(); + final String message = + "activating block: " + block; + getLogger().debug( message ); + } + + try + { + block.assemble( new DependencyGraph( graph ) ); + Container container = (Container) block.resolve( this ); + m_containers.put( block, container ); } - if( m_container instanceof Disposable ) + catch( Throwable e ) { - ((Disposable)m_container).dispose(); + final String error = + "Could not establish a sub-container: " + + block + + " in block: " + this; + throw new AssemblyException( error, e ); } } - catch( Throwable e ) + + super.assemble( graph ); + + if( getLogger().isDebugEnabled() ) { - final String error = "Unexpected error during container execution."; - m_error = new BlockException( error, e ); - terminate(); + Appliance[] startup = graph.getStartupGraph( ); + StringBuffer buffer = new StringBuffer( "startup sequence: " ); + for( int p=0; p<startup.length; p++ ) + { + buffer.append( ", " + startup[p] ); + } + getLogger().debug( buffer.toString() ); } } - //------------------------------------------------------------------------------- - // Appliance - //------------------------------------------------------------------------------- + /** + * Startup the components in this container. + * @exception Exception if a startup error occurs + */ + private void assembleComponents( DependencyGraph graph ) throws AssemblyException + { + Iterator iterator = m_appliances.iterator(); + while( iterator.hasNext() ) + { + Appliance appliance = (Appliance) iterator.next(); + graph.add( appliance ); + appliance.assemble( graph ); + } + } /** - * Assemble the appliance. The implementation extends the default - * appliance behavior by instantiating the container it is managing, - * and applying assembly to the container instance. This results in - * container creating appliance instances for the component is is - * managing together with assembly of its sub-containers. - * - * @exception AssemblyException if an error occurs during appliance assembly + * Disassemble the block. */ - public void assemble() throws AssemblyException + public void disassemble() { - if( m_assembled ) + if( m_disassembled ) { return; } if( getLogger().isDebugEnabled() ) { - getLogger().debug("block assembly" ); + getLogger().debug( "disassemble" ); } - super.assemble(); - - try + if( !m_deployed ) { - m_assembled = true; - m_container = (Container) super.access(); - m_container.addStateListener( this ); - m_container.assemble(); + throw new IllegalStateException( "deployment" ); } - catch( Throwable e ) + + synchronized( m_action ) { - m_assembled = false; - final String error = "Unable to establish containment implemetation."; - throw new AssemblyException( error, e ); + m_action = new Integer( StateEvent.STOPPED ); + while( ( m_state < StateEvent.STOPPED ) && ( m_error == null ) ) + { + sleep(); + } + + if( m_error != null ) + { + if( getLogger().isErrorEnabled() ) + { + final String msg = "Unexpected error during disassembly."; + String error = ExceptionHelper.packException( msg, m_error ); + getLogger().error( error ); + m_error = null; + } + } } } /** + * Startup the components in this container. + * @exception Exception if a startup error occurs + */ + //private void disassembleComponents() + //{ + // Iterator iterator = m_appliances.iterator(); + // while( iterator.hasNext() ) + // { + // Appliance appliance = (Appliance) iterator.next(); + // appliance.disassemble(); + // } + //} + + /** * Terminate the block. If a container has been established, the implemetation * will relase the container and continue with appliance termination. */ public void terminate() { - synchronized( this ) + if( m_terminated ) { - if( m_error == null ) + return; + } + + if( !m_deployed ) + { + handleTermination(); + } + + synchronized( m_action ) + { + m_action = new Integer( TERMINATED ); + while( ( m_state < TERMINATED ) && ( m_error == null ) ) { - m_action = new Integer( StateEvent.DISPOSED ); - while( ( m_state < StateEvent.DISPOSED ) && ( m_error == null ) ) - { - sleep(); - } + sleep(); } - else + + if( m_error != null ) { if( getLogger().isErrorEnabled() ) { - getLogger().error( "terminating due to container error", m_error ); - } - if( m_container instanceof Disposable ) - { - ((Disposable)m_container).dispose(); + final String msg = "Unexpected error during termination."; + String error = ExceptionHelper.packException( msg, m_error ); + getLogger().error( error ); + m_error = null; } } } - - if( m_container != null ) - { - release( m_container ); - m_container = null; - } - super.terminate(); } //------------------------------------------------------------------------------- @@ -324,91 +492,290 @@ //------------------------------------------------------------------------------- /** - * Convinience operation to startup the components in the container. - * @exception Exception if a container startup error occurs + * Deploy the block during which appliances managed by the block + * will be established as executing components. + * + * @exception Exception if a deployment error occurs */ - public void startup() throws Exception + public void deploy() throws Exception { - if( !m_assembled ) + if( getDependencyGraph() == null ) { - throw new IllegalStateException( "assembly" ); + final String error = + "Cannot deploy block " + this + " before assembly."; + throw new IllegalStateException( error ); } - synchronized( this ) + if( getLogger().isDebugEnabled() ) + { + getLogger().debug( "deployment" ); + } + + // + // construct and start a thread in which this block will execute + // + + m_thread = new Thread( this, super.getPath() ); + m_thread.setContextClassLoader( m_engine ); + m_thread.start(); + + synchronized( m_action ) { m_action = new Integer( StateEvent.STARTED ); while( ( m_state < StateEvent.STARTED ) && ( m_error == null ) ) { sleep(); } + if( m_error != null ) { - throw m_error; + m_state = StateEvent.STOPPED; + final String error = + "Unexpected deployment error."; + Exception exception = new BlockException( error, m_error ); + m_error = null; + throw exception; + } + } + + // + // deploy the subsidiary blocks + // + + Iterator iterator = m_blocks.iterator(); + while( iterator.hasNext() ) + { + Block block = (Block) iterator.next(); + + try + { + block.deploy(); + Container container = (Container) block.resolve( this ); + m_containers.put( block, container ); + } + catch( Throwable e ) + { + final String error = + "Subsidiary block deployment error in: " + block + + " in parent block: " + this; + m_error = new BlockException( error, e ); + break; } } + + m_deployed = true; } - /** - * Request for the container to suspend all subsidiary containers - * and all contained components. - */ - public void suspend() + /** + * Decommission the block. + * + * @exception Exception if a deployment error occurs + */ + public void decommission() { - synchronized( this ) + if( !m_deployed ) + { + throw new IllegalStateException( "deployment" ); + } + + synchronized( m_action ) { - m_action = new Integer( StateEvent.SUSPENDED ); - while( ( m_state < StateEvent.SUSPENDED ) && ( m_error == null ) ) + m_action = new Integer( StateEvent.STOPPED ); + while( ( m_state < StateEvent.STOPPED ) && ( m_error == null ) ) { sleep(); } + + if( m_error != null ) + { + if( getLogger().isErrorEnabled() ) + { + final String msg = "Unexpected error during decommissioning."; + String error = ExceptionHelper.packException( msg, m_error ); + getLogger().error( error ); + } + m_state = StateEvent.STOPPED; + } } } + //------------------------------------------------------------------------------- + // Runnable + //------------------------------------------------------------------------------- + /** - * Request for the container to resume all subsidiary containers - * and all contained components. + * Starts the thread of execution for the block. This operation is + * invoked by the container constructor. */ - public void resume() + public void run() { - if( m_state != StateEvent.SUSPENDED ) + if( !m_running ) { - throw new IllegalStateException( "not-suspended." ); + m_running = true; } - synchronized( this ) + try { - m_action = new Integer( StateEvent.STARTED ); - while( ( m_state == StateEvent.SUSPENDED ) && ( m_error == null ) ) + + m_container = (Container) resolve( this ); + + // + // while desired state of the hosted container is something + // other than DISPOSED, check if the desired state is different + // from the current state reported by the container, and if + // so initiate a container state change + // + + while( m_action.intValue() < DISPOSED ) { + synchronized( m_action ) + { + if( m_state != m_action.intValue() ) + { + switch( m_action.intValue() ) + { + case StateEvent.STARTED: + if( m_state == StateEvent.INITIALIZED ) + { + m_container.startup(); + } + else if( m_state == StateEvent.SUSPENDED ) + { + m_container.resume(); + } + break; + case StateEvent.SUSPENDED: + if( m_state == StateEvent.STARTED ) + { + m_container.suspend(); + } + break; + case StateEvent.STOPPED: + handleDecommissioning(); + break; + case DISASSEMBLED: + handleDisassembly(); + break; + case TERMINATED: + handleTermination(); + break; + } + } + } sleep(); } + + getLogger().debug( "exit: " + m_state ); + } + catch( Throwable e ) + { + final String error = "Unexpected runtime error in block:" + this; + m_error = new BlockException( error, e ); } } - /** - * Convinience operation to shutdown the components in the container. - * @exception Exception if a container shutdown error occurs - */ - public void shutdown() throws Exception + private void handleDecommissioning() { - if( !m_assembled ) - { - throw new IllegalStateException( "assembly" ); - } - synchronized( this ) + if( m_container != null ) { - m_action = new Integer( StateEvent.STOPPED ); - while( ( m_state < StateEvent.STOPPED ) && ( m_error == null ) ) + if( ( m_state == StateEvent.STARTED ) + || ( m_state == StateEvent.SUSPENDED ) ) { - sleep(); + m_container.shutdown(); } - if( m_error != null ) + } + else + { + m_state = StateEvent.STOPPED; + } + m_decommissioned = true; + } + + private void handleDisassembly() + { + if( !m_decommissioned ) + { + handleDecommissioning(); + } + + m_shutdown = getDependencyGraph().getShutdownGraph(); + + if( getLogger().isDebugEnabled() ) + { + StringBuffer buffer = new StringBuffer( "dissassemble: " ); + for( int p=0; p<m_shutdown.length; p++ ) { - throw m_error; + buffer.append( ", " + m_shutdown[p] ); } + getLogger().debug( buffer.toString() ); } + + for( int i=0; i<m_shutdown.length; i++ ) + { + Appliance appliance = m_shutdown[i]; + appliance.disassemble(); + } + + Iterator iterator = m_blocks.iterator(); + while( iterator.hasNext() ) + { + Block block = (Block) iterator.next(); + block.disassemble(); + } + + super.disassemble(); + m_disassembled = true; + m_state = DISASSEMBLED; } + private void handleTermination() + { + if( m_terminated ) + { + // already terminated + return; + } + + if( !m_disassembled ) + { + handleDisassembly(); + } + + if( getLogger().isDebugEnabled() ) + { + getLogger().debug( "termination" ); + } + + for( int i=0; i<m_shutdown.length; i++ ) + { + Appliance appliance = m_shutdown[i]; + appliance.terminate(); + } + + m_appliances = null; + + Iterator iterator = m_blocks.iterator(); + while( iterator.hasNext() ) + { + Block block = (Block) iterator.next(); + block.terminate(); + } + + m_terminated = true; + m_state = TERMINATED; + super.terminate(); + + // + // trigger termination of the thread + // + + m_action = new Integer( DISPOSED ); + } + + //------------------------------------------------------------------------------- + // internal + //------------------------------------------------------------------------------- + /** * Method invoked when a container state changes. * @param event the state event @@ -417,7 +784,7 @@ { if( getLogger().isDebugEnabled() ) { - getLogger().debug("state event: [" + event.getState() + "]" ); + getLogger().debug("state event: " + event ); } m_state = event.getState(); } 1.6 +4 -3 avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/container/Container.java Index: Container.java =================================================================== RCS file: /home/cvs/avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/container/Container.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- Container.java 9 Jan 2003 15:19:59 -0000 1.5 +++ Container.java 15 Jan 2003 09:14:51 -0000 1.6 @@ -57,6 +57,7 @@ import org.apache.avalon.framework.Version; import org.apache.avalon.assembly.lifecycle.AssemblyException; +import org.apache.avalon.assembly.appliance.DependencyGraph; /** * <p>A container is node in a containment heirachy. It defines a scope @@ -76,7 +77,7 @@ * all subsidiary containers. * @exception Exception if a assembly error occurs */ - void assemble() throws AssemblyException; + //void assemble( DependencyGraph graph ) throws AssemblyException; /** * Startup the components in this container and startup all subsidiary @@ -101,7 +102,7 @@ * Shutdown all subsidiary containers and all components in this container. * @exception Exception if a shutdown error occurs */ - void shutdown() throws Exception; + void shutdown(); /** * Adds a <code>StateListener</code> to the container. 1.11 +26 -5 avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/container/ContainerLoader.java Index: ContainerLoader.java =================================================================== RCS file: /home/cvs/avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/container/ContainerLoader.java,v retrieving revision 1.10 retrieving revision 1.11 diff -u -r1.10 -r1.11 --- ContainerLoader.java 9 Jan 2003 15:19:59 -0000 1.10 +++ ContainerLoader.java 15 Jan 2003 09:14:51 -0000 1.11 @@ -77,7 +77,6 @@ import org.apache.avalon.framework.configuration.DefaultConfiguration; import org.apache.avalon.framework.context.Context; import org.apache.avalon.framework.context.DefaultContext; -//import org.apache.avalon.framework.context.Contextualizable; import org.apache.avalon.framework.context.ContextException; import org.apache.avalon.framework.logger.AbstractLogEnabled; import org.apache.avalon.framework.service.DefaultServiceManager; @@ -103,6 +102,7 @@ import org.apache.avalon.assembly.engine.model.ClasspathDescriptor; import org.apache.avalon.assembly.util.ExceptionHelper; import org.apache.excalibur.configuration.ConfigurationUtil; +import org.apache.avalon.meta.model.builder.XMLProfileCreator; /** * An abstract utility class that provides support for the creation of subsidiary @@ -120,6 +120,7 @@ //============================================================== protected static final XMLContainerCreator CREATOR = new XMLContainerCreator(); + private static final XMLProfileCreator BUILDER = new XMLProfileCreator(); //============================================================== // ContainerLoader @@ -156,7 +157,10 @@ try { - return CREATOR.createContainerDescriptor( type, config ); + Profile profile = BUILDER.createProfile( type, config ); + boolean policy = getActivationPolicy( config ); + profile.setActivationPolicy( policy ); + return profile; } catch( Throwable e ) { @@ -168,6 +172,22 @@ } } + private boolean getActivationPolicy( Configuration config ) + { + final String value = config.getAttribute( "activation", null ); + if( value == null ) + { + return false; + } + final String string = value.toLowerCase().trim(); + if( string.equals( "startup" ) ) + { + return true; + } + return string.equals( "true" ); + } + + protected EngineClassLoader createChildEngine( EngineClassLoader parent, File home, Configuration config, Logger logger ) throws ContainerException, ConfigurationException @@ -188,7 +208,8 @@ } catch( Throwable e ) { - final String error = "Bad library descriptor."; + final String error = "Bad library descriptor in configuration: \n" + + ConfigurationUtil.list( config ); throw new ContainerException( error, e ); } @@ -216,7 +237,7 @@ { engine = new EngineClassLoader( parent ); } - engine.enableLogging( logger.getChildLogger( "engine" ) ); + engine.enableLogging( logger ); DefaultLocator context = new DefaultLocator(); context.put( "urn:assembly:home", home ); context.put( "urn:assembly:engine.extensions", extensions ); 1.14 +129 -414 avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/container/DefaultContainer.java Index: DefaultContainer.java =================================================================== RCS file: /home/cvs/avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/container/DefaultContainer.java,v retrieving revision 1.13 retrieving revision 1.14 diff -u -r1.13 -r1.14 --- DefaultContainer.java 9 Jan 2003 15:19:59 -0000 1.13 +++ DefaultContainer.java 15 Jan 2003 09:14:51 -0000 1.14 @@ -66,6 +66,7 @@ import org.apache.avalon.assembly.appliance.ApplianceContext; import org.apache.avalon.assembly.appliance.DefaultApplianceContext; import org.apache.avalon.assembly.appliance.ApplianceException; +import org.apache.avalon.assembly.appliance.DependencyGraph; import org.apache.avalon.assembly.lifecycle.AssemblyException; import org.apache.avalon.framework.logger.AbstractLogEnabled; import org.apache.avalon.framework.configuration.Configuration; @@ -97,48 +98,14 @@ // state //============================================================== - private EngineClassLoader m_engine; + private DependencyGraph m_graph; private ContainerDescriptor m_descriptor; - private Registry m_registry; - - /** - * List of the appliance instances that the container is - * holding. Each appliance instance is a contained component. - */ - private List m_components = new ArrayList(); - - /** - * List of the containment appliance instances that the container is - * holding. Each appliance instance is a subsidiary container. - */ - private List m_containers; - - /** - * Map of container objects keyed by containment appliance. - */ - private Map m_containerMap = new Hashtable(); - /** * Map of components keyed by appliance. */ - private Map m_componentMap = new Hashtable(); - - /** - * Container partition name. - */ - private String m_partition; - - /** - * Container path name. - */ - private String m_path; - - /** - * Component configuration. - */ - private Configuration m_targets; + private Map m_components = new Hashtable(); /** * State event listener list. @@ -151,6 +118,11 @@ */ private int m_state = StateEvent.UNKNOWN; + /** + * Flag holding the disposed state of the container. + */ + private boolean m_disposed = false; + //============================================================== // Contextualizable //============================================================== @@ -161,142 +133,44 @@ */ public void contextualize( Context context ) throws ContextException { - m_engine = (EngineClassLoader) context.get( "urn:assembly:engine.classloader" ); - m_containers = (List) context.get( "urn:merlin:container.containers" ); m_descriptor = (ContainerDescriptor) context.get( "urn:merlin:container.descriptor" ); - m_partition = (String) context.get( "urn:avalon:partition.name" ); - m_registry = (Registry) context.get( "urn:merlin:container.registry" ); - m_targets = (Configuration) context.get( "urn:merlin:container.configuration" ); + m_graph = (DependencyGraph) context.get( "urn:merlin:container.dependency-graph" ); + addStateListener( (StateListener) context.get( "urn:merlin:container.listener" ) ); } //============================================================== // Initializable //============================================================== - public void initialize() throws Exception + /** + * Initialization of the container. The implememtation uses the intilization + * phase to verify correct setup including validation of logging channel assignment + * and container context. + * @exception IllegalStateException if the logging or context phases have not been + * applied + */ + public void initialize() throws IllegalStateException { if( getLogger() == null ) { - throw new IllegalStateException("logger"); - } - if( m_engine == null ) - { - throw new IllegalStateException("context"); - } - - if( m_partition != null ) - { - if( m_partition.endsWith("/") ) - { - m_path = m_partition + m_descriptor.getName() + "/"; - } - else - { - m_path = m_partition + "/" + m_descriptor.getName() + "/"; - } - } - else - { - m_path = m_descriptor.getName() + "/"; - } - - Profile[] profiles = m_descriptor.getComponents(); - if( getLogger().isDebugEnabled() ) - { - getLogger().debug( "initialization: " + m_path + " (" + profiles.length + ")" ); + throw new IllegalStateException("logging"); } - for( int i=0; i<profiles.length; i++ ) + if( m_graph == null ) { - Profile profile = profiles[i]; - if( getLogger().isDebugEnabled() ) - { - final String message = - "appliance appliance: " + profile; - getLogger().debug( message ); - } - DefaultApplianceContext context = new DefaultApplianceContext( profile ); - Configuration config = DefaultKernel.getNamedConfiguration( m_targets, profile.getName() ); - context.setPartitionName( m_path ); - context.setConfiguration( config ); - context.makeReadOnly(); - Appliance appliance = createAppliance( context, true ); - m_components.add( appliance ); - m_registry.bind( appliance ); - } - - Iterator iterator = m_containers.iterator(); - while( iterator.hasNext() ) - { - Appliance appliance = (Appliance) iterator.next(); - - if( getLogger().isDebugEnabled() ) - { - final String message = - "activating container: " + appliance; - getLogger().debug( message ); - } - - try - { - appliance.assemble(); - Container container = (Container) appliance.access(); - m_containerMap.put( appliance, container ); - } - catch( Throwable e ) - { - final String error = - "Could not establish a sub-container: " - + appliance - + " in container: " + this; - throw new ContainerException( error, e ); - } + throw new IllegalStateException("context"); } - } - - /** - * Interception point in the component deployment process where specilization of - * this class can modify the criteria supplied under the appliance context. The - * default implementation invokes appliance creation on the service management - * engine and returns the non-assembled appliance result. - * - * @param context the appliance context - * @param shared boolean value indicating if the appliance is to be shared - * (i.e. available to other components as a depedency solution condidate) - * @return the new appliance - * @exception ApplianceException if an appliance creation error occurs - */ - protected Appliance createAppliance( ApplianceContext context, boolean shared ) - throws ApplianceException - { - return m_engine.createAppliance( context, shared ); - } - /** - * Assemble all of the componets in this container and invoke assembly on - * all subsidiary containers. - * @exception Exception if a assembly error occurs - */ - public void assemble() throws AssemblyException - { - if( m_state < StateEvent.INITIALIZED ) - { - if( getLogger().isDebugEnabled() ) - { - getLogger().debug( "assembly" ); - } - - assembleComponents(); - assembleContainers(); + // + // issue an initialization event back to the block + // - m_state = StateEvent.INITIALIZED; - fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.INITIALIZED ) ); - } + m_state = StateEvent.INITIALIZED; + fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.INITIALIZED ) ); } /** - * Startup the components in this container and startup all subsidiary - * containers. + * Startup the components in this container. * @exception Exception if a startup error occurs */ public void startup() throws Exception @@ -312,22 +186,23 @@ return; } - if( m_state > StateEvent.STARTED ) + if( m_state == StateEvent.STOPPED ) { - throw new IllegalStateException( - "Container has already passed through start phase." ); + final String error = "Cannot restart a stopped container."; + throw new IllegalStateException( error ); } - if( getLogger().isDebugEnabled() ) + try { - getLogger().debug( "startup" ); + startupComponents(); + m_state = StateEvent.STARTED; + fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.STARTED ) ); + } + catch( Throwable e ) + { + m_state = StateEvent.STOPPED; + fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.STOPPED, e ) ); } - - startupComponents(); - startupContainers(); - - m_state = StateEvent.STARTED; - fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.STARTED ) ); } /** @@ -358,12 +233,17 @@ getLogger().debug( "container suspension" ); } - suspendContainers(); - suspendComponents(); - - getLogger().debug( "container suspension complete" ); - m_state = StateEvent.SUSPENDED; - fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.SUSPENDED ) ); + try + { + suspendComponents(); + m_state = StateEvent.SUSPENDED; + fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.SUSPENDED ) ); + } + catch( Throwable e ) + { + m_state = StateEvent.STOPPED; + fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.STOPPED, e ) ); + } } /** @@ -387,12 +267,17 @@ // resume subsidiary containers // - resumeComponents(); - resumeContainers(); - - getLogger().debug( "container resumption complete" ); - m_state = StateEvent.STARTED; - fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.STARTED ) ); + try + { + resumeComponents(); + m_state = StateEvent.STARTED; + fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.STARTED ) ); + } + catch( Throwable e ) + { + m_state = StateEvent.STOPPED; + fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.STOPPED, e ) ); + } } @@ -400,7 +285,7 @@ * Shutdown all subsidiary containers and all components in this container. * @exception Exception if a shutdown error occurs */ - public void shutdown() throws Exception + public void shutdown() { if( m_state < StateEvent.STARTED ) { @@ -427,11 +312,17 @@ // the components in this container // - shutdownContainers(); - shutdownComponents(); - - m_state = StateEvent.STOPPED; - fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.STOPPED ) ); + try + { + shutdownComponents(); + m_state = StateEvent.STARTED; + fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.STOPPED ) ); + } + catch( Throwable e ) + { + m_state = StateEvent.STOPPED; + fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.STOPPED, e ) ); + } } /** @@ -439,7 +330,7 @@ */ public void dispose() { - if( m_state == StateEvent.DISPOSED ) + if( m_disposed ) { return; } @@ -449,58 +340,10 @@ getLogger().debug( "disposal" ); } - disposeContainers(); - disposeComponents(); - - if( getLogger().isDebugEnabled() ) - { - getLogger().debug( "cleanup" ); - } - - m_state = StateEvent.DISPOSED; - fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.DISPOSED, null ) ); - - m_engine = null; m_descriptor = null; - m_componentMap.clear(); m_components.clear(); - m_componentMap = null; m_components = null; - m_containerMap.clear(); - m_containers.clear(); - m_containerMap = null; - m_containers = null; - m_partition = null; - m_path = null; - } - - /** - * Startup the components in this container. - * @exception Exception if a startup error occurs - */ - private void assembleComponents() throws AssemblyException - { - Iterator iterator = m_components.iterator(); - while( iterator.hasNext() ) - { - Appliance appliance = (Appliance) iterator.next(); - appliance.assemble(); - } - } - - /** - * Invoke assembly on the nested containers in this container. - * @exception Exception if an assembly error occurs - */ - private void assembleContainers() throws AssemblyException - { - Iterator iterator = m_containers.iterator(); - while( iterator.hasNext() ) - { - Appliance appliance = (Appliance) iterator.next(); - Container container = (Container) m_containerMap.get( appliance ); - container.assemble(); - } + m_disposed = true; } /** @@ -509,56 +352,56 @@ */ private void startupComponents() throws Exception { - Iterator iterator = m_components.iterator(); - while( iterator.hasNext() ) - { - Appliance appliance = (Appliance) iterator.next(); + Appliance[] appliances = m_graph.getStartupGraph(); - if( getLogger().isDebugEnabled() ) - { - final String message = - "activating component: [" + appliance.getName() + "]"; - getLogger().debug( message ); - } - - try + if( getLogger().isDebugEnabled() ) + { + if( appliances.length > 0 ) { - Object object = appliance.access(); - m_componentMap.put( appliance, object ); + StringBuffer buffer = new StringBuffer( "startup: " ); + for( int p=0; p<appliances.length; p++ ) + { + if( p == 0 ) + { + buffer.append( appliances[p] ); + } + else + { + buffer.append( ", " + appliances[p] ); + } + } + getLogger().debug( buffer.toString() ); } - catch( Throwable e ) + else { - final String error = - "Could not establish a component: " - + appliance.getName() - + " in container: " + this; - throw new ContainerException( error, e ); + getLogger().debug( "startup" ); } } - } - /** - * Startup the containers in this container. - * @exception Exception if a startup error occurs - */ - private void startupContainers() throws Exception - { - Iterator iterator = m_containers.iterator(); - while( iterator.hasNext() ) + for( int i=0; i<appliances.length; i++ ) { - Appliance appliance = (Appliance) iterator.next(); - Container container = (Container) m_containerMap.get( appliance ); - try - { - container.startup(); - } - catch( Throwable e ) + Appliance appliance = appliances[i]; + if( appliance.getActivationPolicy() ) { - final String error = - "Could not start a subsidiary container: " - + appliance.getName() - + " in container: " + this; - throw new ContainerException( error, e ); + if( getLogger().isDebugEnabled() ) + { + final String message = + "activating component: [" + appliance.getName() + "]"; + getLogger().debug( message ); + } + try + { + Object object = appliance.resolve( this ); + m_components.put( appliance, object ); + } + catch( Throwable e ) + { + final String error = + "Could not establish a component: " + + appliance.getName() + + " in container: " + this; + throw new ContainerException( error, e ); + } } } } @@ -574,32 +417,6 @@ } /** - * Startup the containers in this container. - * @exception Exception if a startup error occurs - */ - private void suspendContainers() - { - Iterator iterator = m_containers.iterator(); - while( iterator.hasNext() ) - { - Appliance appliance = (Appliance) iterator.next(); - Container container = (Container) m_containerMap.get( appliance ); - try - { - container.suspend(); - } - catch( Throwable e ) - { - final String error = - "Could not suspend a subsidiary container: " - + appliance.getName() - + " in container: " + this; - throw new ContainerRuntimeException( error, e ); - } - } - } - - /** * Startup the components in this container. * @exception Exception if a startup error occurs */ @@ -610,42 +427,21 @@ } /** - * Startup the containers in this container. - * @exception Exception if a startup error occurs - */ - private void resumeContainers() - { - Iterator iterator = m_containers.iterator(); - while( iterator.hasNext() ) - { - Appliance appliance = (Appliance) iterator.next(); - Container container = (Container) m_containerMap.get( appliance ); - try - { - container.resume(); - } - catch( Throwable e ) - { - final String error = - "Could not resume of a subsidiary container: " - + appliance.getName() - + " in container: " + this; - throw new ContainerRuntimeException( error, e ); - } - } - } - - /** * Shutdown all subsidiary containers and all components in this container. * @exception Exception if a shutdown error occurs */ private void shutdownComponents() throws Exception { - Iterator iterator = m_components.iterator(); - while( iterator.hasNext() ) + if( m_graph == null ) + { + throw new IllegalStateException( "Container has not been contextualized: " + this ); + } + + Appliance[] appliances = m_graph.getShutdownGraph(); + for( int i=0; i<appliances.length; i++ ) { - Appliance appliance = (Appliance) iterator.next(); - Object object = m_componentMap.get( appliance ); + Appliance appliance = appliances[i]; + Object object = m_components.get( appliance ); if( object != null ) { if( object instanceof Startable ) @@ -658,89 +454,7 @@ } ((Startable)object).stop(); } - else - { - appliance.release( object ); - } - } - } - } - - /** - * Shutdown the containers in this container. - * @exception Exception if a startup error occurs - */ - private void shutdownContainers() throws Exception - { - Iterator iterator = m_containers.iterator(); - while( iterator.hasNext() ) - { - Appliance appliance = (Appliance) iterator.next(); - Container container = (Container) m_containerMap.get( appliance ); - try - { - container.shutdown(); - } - catch( Throwable e ) - { - final String error = - "Could not shutdown a subsidiary container: " - + appliance.getName() - + " in container: " + this; - throw new ContainerException( error, e ); - } - } - } - - /** - * Release all of the components in this container. - */ - private void disposeComponents() - { - Iterator iterator = m_components.iterator(); - while( iterator.hasNext() ) - { - Appliance appliance = (Appliance) iterator.next(); - Object object = m_componentMap.get( appliance ); - if( object != null ) - { - if( getLogger().isDebugEnabled() ) - { - final String message = - "disposing of: " + appliance; - getLogger().debug( message ); - } - - m_componentMap.remove( appliance ); - appliance.release( object ); - appliance.terminate(); - } - } - } - - /** - * Shutdown the containers in this container. - * @exception Exception if a startup error occurs - */ - private void disposeContainers() - { - Iterator iterator = m_containers.iterator(); - while( iterator.hasNext() ) - { - Appliance appliance = (Appliance) iterator.next(); - Container container = (Container) m_containerMap.get( appliance ); - try - { - m_containerMap.remove( appliance ); - appliance.release( container ); - appliance.terminate(); - } - catch( Throwable e ) - { - final String error = - "Could not shutdown a subsidiary container: " - + appliance.getName(); - getLogger().warn( error, e ); + m_components.remove( appliance ); } } } @@ -779,6 +493,7 @@ { StateListener[] listeners = (StateListener[])m_listeners.toArray( new StateListener[ 0 ] ); + for( int i = 0; i < listeners.length; i++ ) { StateListener listener = listeners[ i ]; @@ -790,7 +505,7 @@ { m_listeners.remove( listener ); final String warning = - "State listener raised on error on notification. Removing listener: " + "State listener raised on error on notification - removing listener: " + listener; if( getLogger().isWarnEnabled() ) { 1.7 +4 -0 avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/container/DefaultContainer.xinfo Index: DefaultContainer.xinfo =================================================================== RCS file: /home/cvs/avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/container/DefaultContainer.xinfo,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- DefaultContainer.xinfo 9 Jan 2003 15:19:59 -0000 1.6 +++ DefaultContainer.xinfo 15 Jan 2003 09:14:51 -0000 1.7 @@ -31,6 +31,10 @@ <entry key="urn:avalon:partition.name"/> <entry key="urn:merlin:container.configuration" type="org.apache.avalon.framework.configuration.Configuration"/> + <entry key="urn:merlin:container.listener" + type="org.apache.avalon.merlin.container.StateListener"/> + <entry key="urn:merlin:container.dependency-graph" + type="org.apache.avalon.assembly.appliance.DependencyGraph"/> </context> </type> 1.3 +32 -9 avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/container/StateEvent.java Index: StateEvent.java =================================================================== RCS file: /home/cvs/avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/container/StateEvent.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- StateEvent.java 9 Jan 2003 15:19:59 -0000 1.2 +++ StateEvent.java 15 Jan 2003 09:14:51 -0000 1.3 @@ -99,12 +99,6 @@ */ public static final int STOPPED = 4; - /** - * Static state enumeration value indicating that a container and all - * subsidiary containers have been disposed of. - */ - public static final int DISPOSED = 5; - //============================================================ // state @@ -135,7 +129,7 @@ * @param source the source container * @param name the name of the soource container * @param state int value of (@link #INITIALIZED}, [EMAIL PROTECTED] #STARTED}, - * [EMAIL PROTECTED] #SUSPENDED}, [EMAIL PROTECTED] #STOPPED} or [EMAIL PROTECTED] #DISPOSED} + * [EMAIL PROTECTED] #SUSPENDED}, or [EMAIL PROTECTED] #STOPPED} */ public StateEvent( Object source, String name, int state ) { @@ -148,7 +142,7 @@ * @param source the source container * @param name the name of the soource container * @param state int value of (@link #INITIALIZED}, [EMAIL PROTECTED] #STARTED}, - * [EMAIL PROTECTED] #STOPPED} or [EMAIL PROTECTED] #DISPOSED} + * [EMAIL PROTECTED] #SUSPENDED}, or [EMAIL PROTECTED] #STOPPED} * @param error an error condition triggering the state change */ public StateEvent( Object source, String name, int state, Throwable error ) @@ -191,6 +185,35 @@ public Throwable getError() { return m_error; + } + + /** + * Return a string representation of the event. + */ + public String toString() + { + return "[" + getName() + ", " + getStateAsString() + "]"; + } + + private String getStateAsString() + { + if( m_state == INITIALIZED ) + { + return "INITIALIZED"; + } + else if( m_state == STARTED ) + { + return "STARTED"; + } + else if( m_state == SUSPENDED ) + { + return "SUSPENDED"; + } + else if( m_state == STOPPED ) + { + return "STOPPED"; + } + return "UNKNOWN"; } } 1.26 +107 -60 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.25 retrieving revision 1.26 diff -u -r1.25 -r1.26 --- DefaultKernel.java 9 Jan 2003 15:20:00 -0000 1.25 +++ DefaultKernel.java 15 Jan 2003 09:14:51 -0000 1.26 @@ -79,6 +79,7 @@ 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.assembly.appliance.DependencyGraph; import org.apache.avalon.assembly.locator.Contextualizable; import org.apache.avalon.assembly.locator.Locator; import org.apache.avalon.assembly.locator.DefaultLocator; @@ -148,6 +149,7 @@ protected static final String BLOCK_XML_ENTRY = "BLOCK-INF/block.xml"; + /** * Return a configuration element relative to a target name. The implementation * will locate a configuration child in the supplied configuration with an element name @@ -223,6 +225,8 @@ private DefaultRegistry m_registry; + private final DependencyGraph m_graph = new DependencyGraph(); + //============================================================== // Contextualizable //============================================================== @@ -407,7 +411,7 @@ Block block = blocks[i]; try { - block.assemble(); + block.assemble( new DependencyGraph( m_graph ) ); } catch( Throwable e ) { @@ -463,7 +467,7 @@ while( iterator.hasNext() ) { Block block = (Block) iterator.next(); - block.startup(); + block.deploy(); } } @@ -478,7 +482,7 @@ { if( getLogger().isInfoEnabled() ) { - getLogger().info( "commencing shutdown phase" ); + getLogger().info( "decommissioning" ); } } @@ -486,7 +490,7 @@ while( iterator.hasNext() ) { Block block = (Block) iterator.next(); - block.shutdown(); + block.decommission(); } } @@ -503,7 +507,7 @@ { if( getLogger().isInfoEnabled() ) { - getLogger().info( "commencing disposal phase" ); + getLogger().info( "termination" ); } } @@ -514,7 +518,7 @@ block.terminate(); } - if( getLogger() != null ) + if( getLogger() != null ) { if( getLogger().isInfoEnabled() ) { @@ -561,16 +565,21 @@ for( int i=0; i<configs.length; i++ ) { Configuration config = configs[i]; - try - { - Block block = loadPhysicalBlock( config, system ); - blocks.add( block ); - } - catch( Throwable e ) + String name = config.getAttribute( "name", null ); + boolean enabled = config.getAttributeAsBoolean( "enabled", true ); + if( enabled ) { - final String error = - "Error during block deployment for block: " + i; - throw new BlockException( error, e ); + try + { + Block block = loadPhysicalBlock( config, system ); + blocks.add( block ); + } + catch( Throwable e ) + { + final String error = + "Error during block deployment for block: " + (i+1) + " [" + name + "]"; + throw new BlockException( error, e ); + } } } return (Block[]) blocks.toArray( new Block[0] ); @@ -578,13 +587,19 @@ /** * 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. + * element named "block" that either: + * <ul> + * <li>a reference to a block configuration file</li> + * <li>a reference to a package black in the form of a jar file</li> + * </ul> + * + * The block name is defined by the block name attribute declared under the + * block element in the blocks.xml configuration. In the case of the + * configuration based block defintion, the configuration is derived from a + * and explicit file reference. In the case of a packaged block, the + * configuration is resolved from a packaged xmml file name + * /BLOCK-INFO/block.xml with a jar file. + * * * @param config the block declaration * @param system the system context @@ -604,43 +619,68 @@ // in the block.xml file // + URL url = null; + Configuration base; String name = config.getAttribute( "name" ); - String path = config.getAttribute("path"); Logger logger = getLogger().getChildLogger( name ); - logger.debug( "[" + path + "] as [" + name + "]"); + if( config.getAttribute( "path", null ) != null ) + { - // - // load the jar file referenced by the block declaration and get its - // manifest - // + // + // It is a packaged block within a jar file. + // - 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 path = config.getAttribute("path"); + if( getLogger().isDebugEnabled() ) + { + logger.debug( "[" + path + "] as [" + name + "]"); + } - String packageName = DefaultBlock.getPackageName( manifest ); - if( packageName == null ) - { - final String error = "Missing package declaration in block: " + url; - throw new IllegalArgumentException( error ); + // + // load the jar file referenced by the block declaration and get its + // manifest + // + + url = new File( m_home, path ).getCanonicalFile().toURL(); + JarFile jar = getJarFile( url ); + + // + // get the blocks packaged configuration and use that to establish + // the engine to be supplied to the block + // + + base = getBlockConfiguration( jar ); } + else + { + // + // The block configuration is declared in an external file. + // - // - // get the blocks packaged configuration and use that to establish - // the engine to be supplied to the block - // + String src = config.getAttribute( "src", null ); + if( src == null ) + { + final String error = + "Block does not contain a 'src' or 'path' attribute: \n" + + ConfigurationUtil.list( config ); + throw new BlockException( error ); + } + + File file = new File( m_home, src ); + DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder(); + InputStream is = new FileInputStream( file ); + if( is == null ) + { + throw new BlockException( + "Could not load the configuration resource \"" + file + "\"" ); + } + base = builder.build( is ); + } - Configuration base = getBlockConfiguration( jar ); - Configuration containment = + Configuration implementation = getImplementationConfiguration( base.getChild( "implementation" ) ); - Configuration engineConfig = containment.getChild( "engine" ); + Configuration engineConfig = implementation.getChild( "engine" ); EngineClassLoader engine = createChildEngine( m_engine, m_home, engineConfig, url, logger ); // @@ -649,9 +689,10 @@ // String partition = name + Container.PATH_SEPERATOR; - ContainerDescriptor descriptor = createContainerDescriptor( name, engine, containment ); + ContainerDescriptor descriptor = createContainerDescriptor( name, engine, implementation ); Registry registry = m_registry.createChild( name ); - List list = createChildContainers( engine, registry, partition, containment, config, logger ); + + List list = createChildBlocks( engine, registry, partition, implementation, config, logger ); // // create the appliance context for the container @@ -687,7 +728,7 @@ } /** - * Creation of a set of child container relative to a set of parent parameters. + * Creation of a set of child blocks relative to a set of parent parameters. * * @param engine the parent classloader * @param registry the parent registry @@ -696,7 +737,7 @@ * @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( + private List createChildBlocks( EngineClassLoader engine, Registry registry, String partition, Configuration config, Configuration custom, Logger logger ) throws BlockException @@ -720,7 +761,7 @@ catch( ConfigurationException ce ) { final String error = - "Cannot create a subsidiary container due to missing container name in configuration:\n" + "Cannot create a subsidiary container due to missing name attribute:\n" + ConfigurationUtil.list( child ); throw new BlockException( error ); } @@ -773,7 +814,7 @@ * Create a single containment block. * * @param engine the containers classloader - * @param registry the compoent registry to apply to the container + * @param registry the component 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 @@ -787,7 +828,7 @@ throws BlockException { String subPartition = partition + name + Container.PATH_SEPERATOR; - List list = createChildContainers( engine, registry, subPartition, config, custom, logger ); + List list = createChildBlocks( engine, registry, subPartition, config, custom, logger ); ContainerDescriptor descriptor; try @@ -842,10 +883,16 @@ map.put("urn:merlin:container.configuration", target ); DefaultApplianceContext context = new DefaultApplianceContext( descriptor ); + context.put("urn:merlin:container.descriptor", descriptor ); + context.put("urn:merlin:container.registry", registry ); + context.put("urn:merlin:container.configuration", target ); + context.put("urn:merlin:container.containers", containers ); + context.setName( name ); context.setDeploymentContext( map ); context.setPartitionName( partition ); context.setApplianceClassname( DefaultBlock.class.getName() ); + context.makeReadOnly(); // // create the containement appliance @@ -853,8 +900,8 @@ try { - context.makeReadOnly(); - return (Block) engine.createAppliance( context, false ); + getLogger().debug( "creating block with context: " + context ); + return (Block) engine.createAppliance( context, false, false ); } catch( Throwable e ) { @@ -935,7 +982,7 @@ try { EngineClassLoader engine = new EngineClassLoader( m_bootstrap ); - engine.enableLogging( getLogger().getChildLogger( "engine" ) ); + engine.enableLogging( getLogger() ); Locator system = getSystemContext(); DefaultLocator context = new DefaultLocator( system ); context.put( "urn:assembly:engine.bootstrap", "true" ); 1.3 +5 -2 avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/kernel/KernelLoader.java Index: KernelLoader.java =================================================================== RCS file: /home/cvs/avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/kernel/KernelLoader.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- KernelLoader.java 9 Jan 2003 15:20:00 -0000 1.2 +++ KernelLoader.java 15 Jan 2003 09:14:51 -0000 1.3 @@ -28,10 +28,10 @@ import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; import org.apache.avalon.framework.context.Context; import org.apache.avalon.framework.context.DefaultContext; -//import org.apache.avalon.framework.context.Contextualizable; import org.apache.avalon.framework.context.ContextException; 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.kernel.Kernel; import org.apache.avalon.merlin.kernel.DefaultKernel; import org.apache.avalon.merlin.kernel.KernelException; @@ -121,7 +121,10 @@ catch( KernelException e ) { // its already been logged - System.err.println( e.toString() ); + } + catch( BlockException e ) + { + // its already been logged } catch( Throwable e ) { 1.14 +3 -18 avalon-sandbox/merlin/src/test/config/block.xml Index: block.xml =================================================================== RCS file: /home/cvs/avalon-sandbox/merlin/src/test/config/block.xml,v retrieving revision 1.13 retrieving revision 1.14 diff -u -r1.13 -r1.14 --- block.xml 8 Jan 2003 23:21:52 -0000 1.13 +++ block.xml 15 Jan 2003 09:14:52 -0000 1.14 @@ -13,24 +13,9 @@ </services> <!-- - Block implementation. The implementation element may contain component - and container entities. The block implementation statement is equivalent - to a root container who's name is implied by the name of the block. + Block implementation. The following declaration demonstrates a + reference to an externally packaged implemetation descriptor. --> - <implementation src="external.xml"> - - <!-- use external file or embedded defintion --> - <!-- a container can contain a classpath declaration --> - <!-- a container can contain nested appliance declarations --> - <!-- a container can contain nested containers --> - <!-- if the container used the "src" attribute the following content is ignored --> - <!-- - <container name="test"> - <appliance name="standard" class="org.apache.avalon.playground.StandardComponent" activation="startup"> - <context class="org.apache.avalon.playground.StandardContextImp"/> - </appliance> - </container> - --> - </implementation> + <implementation src="src/test/config/external.xml" /> </block> 1.6 +56 -1 avalon-sandbox/merlin/src/test/org/apache/avalon/playground/StandardComponent.java Index: StandardComponent.java =================================================================== RCS file: /home/cvs/avalon-sandbox/merlin/src/test/org/apache/avalon/playground/StandardComponent.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- StandardComponent.java 6 Jan 2003 01:05:53 -0000 1.5 +++ StandardComponent.java 15 Jan 2003 09:14:52 -0000 1.6 @@ -56,6 +56,9 @@ package org.apache.avalon.playground; import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; + import org.apache.avalon.framework.activity.Disposable; import org.apache.avalon.framework.activity.Initializable; import org.apache.avalon.framework.activity.Startable; @@ -63,6 +66,8 @@ import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.logger.AbstractLogEnabled; +//import org.apache.avalon.excalibur.thread.impl.DefaultThreadPool; + /** * This is a minimal demonstration component that implements the * <code>BasicService</code> interface and has no dependencies. @@ -117,8 +122,9 @@ /** * Initialization of the component type by its container. */ - public void initialize() + public void initialize() throws Exception { + //doThreadTest(); m_message = "\n home: " + m_home + "\n work: " + m_work @@ -167,5 +173,54 @@ getLogger().info( m_config.getChild("message").getValue("") ); getLogger().info( "listing context\n" + m_message + "\n"); } + + /** + * Do a test related to the thread library. + */ +/* + public void doTreadTest() throws Exception + { + try + { + getLogger().error( "START" ); + DefaultThreadPool pool = new DefaultThreadPool( "test", 12 ); + } + catch( Throwable e ) + { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + try + { + loader.loadClass( DefaultThreadPool.class.getName() ); + } + catch( Throwable ex ) + { + getLogger().error( "CONTEXT: " + ex.toString() ); + } + + if( loader instanceof URLClassLoader ) + { + listURLs( (URLClassLoader) loader ); + } + getLogger().error( "THROWABLE: " + e ); + getLogger().error( "LOADER:" + loader ); + throw new Exception( "zutt", e ); + } + } + private void listURLs( URLClassLoader loader ) + { + if( loader != null ) + { + URL[] urls = loader.getURLs(); + for( int i=0; i<urls.length; i++ ) + { + getLogger().debug( "URL: " + urls[i] ); + } + if( loader.getParent() instanceof URLClassLoader ) + { + listURLs( (URLClassLoader) loader.getParent() ); + } + } + } +*/ }
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>