You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@avalon.apache.org by mc...@apache.org on 2003/01/09 04:59:39 UTC

cvs commit: avalon-sandbox/merlin/src/test/org/apache/avalon/playground StandardComponent.xinfo

mcconnell    2003/01/08 19:59:39

  Modified:    merlin   kernel.xml
               merlin/src/java/org/apache/avalon/merlin/block Block.java
                        DefaultBlock.java
               merlin/src/java/org/apache/avalon/merlin/container
                        Container.java DefaultContainer.java
               merlin/src/test/org/apache/avalon/playground
                        StandardComponent.xinfo
  Added:       merlin/src/java/org/apache/avalon/merlin/container
                        StateEvent.java StateListener.java
  Log:
  Addition of support for per-thread containers.
  
  Revision  Changes    Path
  1.12      +1 -1      avalon-sandbox/merlin/kernel.xml
  
  Index: kernel.xml
  ===================================================================
  RCS file: /home/cvs/avalon-sandbox/merlin/kernel.xml,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- kernel.xml	8 Jan 2003 20:57:48 -0000	1.11
  +++ kernel.xml	9 Jan 2003 03:59:39 -0000	1.12
  @@ -15,7 +15,7 @@
      </logging>
   
      <categories>
  -      <category name="/sys" priority="DEBUG"/>
  +      <category name="/sys" priority="INFO"/>
      </categories>
   
      <!--
  
  
  
  1.6       +26 -2     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.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- Block.java	27 Dec 2002 16:38:54 -0000	1.5
  +++ Block.java	9 Jan 2003 03:59:39 -0000	1.6
  @@ -62,7 +62,31 @@
    * @author <a href="mailto:avalon-dev@jakarta.apache.org">Avalon Development Team</a>
    * @version $Revision$ $Date$
    */
  -public interface Block extends Appliance, Container
  +//public interface Block extends Appliance, Container
  +public interface Block extends Appliance
   {
       static final String AVALON_BLOCK_KEY = "Avalon-Block";
  +
  +   /**
  +    * Startup the block and startup all subsidiary blocks.
  +    * @exception Exception if a startup error occurs
  +    */
  +    void startup() throws Exception;
  +
  +   /**
  +    * Suspend the block and all subsidiary blocks.
  +    */
  +    void suspend();
  +
  +   /**
  +    * Resume the block and all subsidiary blocks.
  +    */
  +    void resume();
  +
  +   /**
  +    * Shutdown the block.
  +    * @exception Exception if a shutdown error occurs
  +    */
  +    void shutdown() throws Exception;
  +
   }
  
  
  
  1.11      +242 -14   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.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- DefaultBlock.java	7 Jan 2003 04:24:16 -0000	1.10
  +++ DefaultBlock.java	9 Jan 2003 03:59:39 -0000	1.11
  @@ -4,8 +4,9 @@
   
   import java.io.File;
   import java.io.IOException;
  -import java.util.jar.JarFile;
   import java.net.URL;
  +import java.util.List;
  +import java.util.ArrayList;
   import java.util.jar.Attributes;
   import java.util.jar.Manifest;
   import java.util.jar.JarFile;
  @@ -24,6 +25,7 @@
   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.Disposable;
   import org.apache.avalon.framework.context.Context;
   import org.apache.avalon.framework.context.Contextualizable;
   import org.apache.avalon.framework.context.ContextException;
  @@ -33,6 +35,8 @@
   import org.apache.avalon.merlin.container.DefaultContainer;
   import org.apache.avalon.merlin.container.ContainerDescriptor;
   import org.apache.avalon.merlin.container.Container;
  +import org.apache.avalon.merlin.container.StateListener;
  +import org.apache.avalon.merlin.container.StateEvent;
   import org.apache.avalon.meta.info.Type;
   import org.apache.avalon.meta.info.builder.XMLTypeCreator;
   import org.apache.avalon.meta.info.StageDescriptor;
  @@ -47,11 +51,11 @@
    * responsible for the establishment of a containment heirachy that 
    * represents the implemetation model for the block.
    */
  -public class DefaultBlock extends DefaultAppliance implements Block
  +public class DefaultBlock extends DefaultAppliance implements Block, Runnable, StateListener
   {
  -    //==============================================================
  +    //-------------------------------------------------------------------------------
       // static
  -    //==============================================================
  +    //-------------------------------------------------------------------------------
   
       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" );
  @@ -84,17 +88,52 @@
       }
   
   
  -    //==============================================================
  +    //-------------------------------------------------------------------------------
       // state
  -    //==============================================================
  +    //-------------------------------------------------------------------------------
   
  +   /**
  +    * The root container instance that this block is managing.
  +    */
       private Container m_container;
   
  +   /**
  +    * Flag holding the assembled state of the block.
  +    */
       private boolean m_assembled = false;
   
  -    //==============================================================
  +    /**
  +     * The thread in which we will run the container.
  +     */
  +    private Thread m_thread;
  +
  +   /**
  +    * Flag holding the running state of the block.
  +    */
  +    private boolean m_running = false;
  +
  +    /**
  +     * The container is managed as a thread under which the current state
  +     * is recorded in the m_state state member.
  +     */
  +    private int m_state = StateEvent.UNKNOWN;
  +
  +    /**
  +     * The thread periodically checks for state change requests enter in
  +     * the m_action state member and attempts to bring the m_state value to
  +     * be equal to the m_action value and once achieved goes off for a
  +     * sleep.
  +     */
  +    private Integer m_action = new Integer( StateEvent.UNKNOWN );
  +
  +    /**
  +     * An error raised by the managed container.
  +     */
  +    private BlockException m_error;
  +
  +    //-------------------------------------------------------------------------------
       // constructor
  -    //==============================================================
  +    //-------------------------------------------------------------------------------
   
       public DefaultBlock( 
         EngineClassLoader engine, LifestyleService lifestyle, AssemblyService assembly, 
  @@ -102,11 +141,88 @@
         throws ApplianceException
       {
           super( engine, lifestyle, assembly, context, system, logger );
  +        m_thread = new Thread( this, super.getPath() );
  +        m_thread.setContextClassLoader( engine );
  +        m_thread.start();
       }
   
  -    //==============================================================
  -    // Block
  -    //==============================================================
  +    //-------------------------------------------------------------------------------
  +    // Runnable
  +    //-------------------------------------------------------------------------------
  +
  +    /**
  +     * Starts the thread of execution for the block.  This operation is
  +     * invoked by the container constructor.
  +     */
  +    public void run()
  +    {
  +        if( !m_running )
  +        {
  +            m_running = true;
  +        }
  +
  +        try
  +        {
  +
  +            //
  +            // 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() < StateEvent.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:
  +                                if( ( m_state == StateEvent.STARTED )
  +                                    || ( m_state == StateEvent.SUSPENDED ) )
  +                                {
  +                                    m_container.shutdown();
  +                                }
  +                                break;
  +                        }
  +                    }
  +                }
  +                sleep();
  +            }
  +            if( m_container instanceof Disposable )
  +            {
  +                ((Disposable)m_container).dispose();
  +            }
  +        }
  +        catch( Throwable e )
  +        {
  +            final String error = "Unexpected error during container execution.";
  +            m_error = new BlockException( error, e );
  +            terminate();
  +        }
  +    }
  +
  +    //-------------------------------------------------------------------------------
  +    // Appliance
  +    //-------------------------------------------------------------------------------
   
      /**
       * Assemble the appliance.  The implementation extends the default 
  @@ -135,6 +251,7 @@
           {
               m_assembled = true;
               m_container = (Container) super.access();
  +            m_container.addStateListener( this );
               m_container.assemble();
           }
           catch( Throwable e )
  @@ -151,6 +268,29 @@
       */
       public void terminate()
       {
  +        synchronized( this )
  +        {
  +            if( m_error == null )
  +            {
  +                m_action = new Integer( StateEvent.DISPOSED );
  +                while( ( m_state < StateEvent.DISPOSED ) && ( m_error == null ) )
  +                {
  +                    sleep();
  +                }
  +            }
  +            else
  +            {
  +                if( getLogger().isErrorEnabled() )
  +                {
  +                    getLogger().error( "terminating due to container error", m_error );
  +                }
  +                if( m_container instanceof Disposable )
  +                {
  +                    ((Disposable)m_container).dispose();
  +                }
  +            }
  +        }
  +
           if( m_container != null )
           {
               release( m_container );
  @@ -160,7 +300,7 @@
       }
   
       //-------------------------------------------------------------------------------
  -    // Container
  +    // Block
       //-------------------------------------------------------------------------------
   
      /**
  @@ -173,7 +313,56 @@
           {
               throw new IllegalStateException( "assembly" );
           }
  -        m_container.startup();
  +
  +        synchronized( this )
  +        {
  +            m_action = new Integer( StateEvent.STARTED );
  +            while( ( m_state < StateEvent.STARTED ) && ( m_error == null ) )
  +            {
  +                sleep();
  +            }
  +            if( m_error != null )
  +            {
  +                throw m_error;
  +            }
  +        }
  +    }
  +
  +    /**
  +     * Request for the container to suspend all subsidiary containers
  +     * and all contained components.
  +     */
  +    public void suspend()
  +    {
  +        synchronized( this )
  +        {
  +            m_action = new Integer( StateEvent.SUSPENDED );
  +            while( ( m_state < StateEvent.SUSPENDED ) && ( m_error == null ) )
  +            {
  +                sleep();
  +            }
  +        }
  +    }
  +
  +    /**
  +     * Request for the container to resume all subsidiary containers
  +     * and all contained components.
  +     */
  +    public void resume()
  +    {
  +        if( m_state != StateEvent.SUSPENDED )
  +        {
  +            throw new IllegalStateException( "not-suspended." );
  +        }
  +
  +        synchronized( this )
  +        {
  +            m_action = new Integer( StateEvent.STARTED );
  +            while( ( m_state == StateEvent.SUSPENDED ) && ( m_error == null ) )
  +            {
  +                sleep();
  +            }
  +        }
       }
   
      /**
  @@ -186,6 +375,45 @@
           {
               throw new IllegalStateException( "assembly" );
           }
  -        m_container.shutdown();
  +        synchronized( this )
  +        {
  +            m_action = new Integer( StateEvent.STOPPED );
  +            while( ( m_state < StateEvent.STOPPED ) && ( m_error == null ) )
  +            {
  +                sleep();
  +            }
  +            if( m_error != null )
  +            {
  +                throw m_error;
  +            }
  +        }
  +    }
  +
  +    /**
  +     * Method invoked when a container state changes.
  +     * @param event the state event
  +     */
  +    public void stateChanged( StateEvent event )
  +    {
  +        if( getLogger().isDebugEnabled() )
  +        {
  +            getLogger().debug("state event: [" + event.getState() + "]" );
  +        }
  +        m_state = event.getState();
  +    }
  +
  +    /**
  +     * Internal utility to sleep a bit.
  +     */
  +    private void sleep()
  +    {
  +        try
  +        {
  +            Thread.currentThread().sleep( 100 );
  +        }
  +        catch( Throwable wakeup )
  +        {
  +            // return
  +        }
       }
   }
  
  
  
  1.4       +25 -1     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.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- Container.java	27 Dec 2002 16:41:06 -0000	1.3
  +++ Container.java	9 Jan 2003 03:59:39 -0000	1.4
  @@ -85,10 +85,34 @@
       */
       void startup() throws Exception;
   
  +    /**
  +     * Request for the container to suspend all subsidiary containers
  +     * and all contained components.
  +     */
  +    void suspend();
  +
  +    /**
  +     * Request for the container to resume all subsidiary containers
  +     * and all contained components.
  +     */
  +    void resume();
  +
      /**
       * Shutdown all subsidiary containers and all components in this container.
       * @exception Exception if a shutdown error occurs
       */
       void shutdown() throws Exception;
  +
  +    /**
  +     * Adds a <code>StateListener</code> to the container.
  +     * @param listener the state listener to add
  +     */
  +    void addStateListener( StateListener listener );
  +
  +    /**
  +     * Removes a <code>StateListener</code> from the container.
  +     * @param listener the state listener to remove
  +     */
  +    void removeStateListener( StateListener listener );
   
   }
  
  
  
  1.12      +324 -57   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.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- DefaultContainer.java	28 Dec 2002 08:06:43 -0000	1.11
  +++ DefaultContainer.java	9 Jan 2003 03:59:39 -0000	1.12
  @@ -140,6 +140,17 @@
       */
       private Configuration m_targets;
   
  +   /**
  +    * State event listener list.
  +    */
  +    private List m_listeners = new ArrayList();
  +
  +    /**
  +     * The container is managed as a thread under which the current state
  +     * is recorded in the m_state state member.
  +     */
  +    private int m_state = StateEvent.UNKNOWN;
  +
       //==============================================================
       // Contextualizable
       //==============================================================
  @@ -193,7 +204,6 @@
           if( getLogger().isDebugEnabled() )
           {
               getLogger().debug( "initialization: " + m_path + " (" + profiles.length + ")" );
  -            //getLogger().debug( "configuration array:\n" + ConfigurationUtil.list( m_targets ) );
           }
   
           for( int i=0; i<profiles.length; i++ )
  @@ -269,13 +279,199 @@
       */
       public void assemble() throws AssemblyException
       {
  +        if( m_state < StateEvent.INITIALIZED )
  +        {
  +            if( getLogger().isDebugEnabled() )
  +            {
  +                getLogger().debug( "assembly" );
  +            }
  +        
  +            assembleComponents();
  +            assembleContainers();
  +
  +            m_state = StateEvent.INITIALIZED;
  +            fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.INITIALIZED ) );
  +        }
  +    }
  +
  +   /**
  +    * Startup the components in this container and startup all subsidiary 
  +    * containers.
  +    * @exception Exception if a startup error occurs
  +    */
  +    public void startup() throws Exception
  +    {
  +        if( m_state < StateEvent.INITIALIZED )
  +        {
  +            throw new IllegalStateException(
  +                "Container has not been initialized." );
  +        }
  +
  +        if( m_state == StateEvent.STARTED )
  +        {
  +            return;
  +        }
  +
  +        if( m_state > StateEvent.STARTED )
  +        {
  +            throw new IllegalStateException(
  +                "Container has already passed through start phase." );
  +        }
  +
           if( getLogger().isDebugEnabled() )
           {
  -            getLogger().debug( "assembly" );
  +            getLogger().debug( "startup" );
           }
  -        
  -        assembleComponents();
  -        assembleContainers();
  +
  +        startupComponents();
  +        startupContainers();
  +
  +        m_state = StateEvent.STARTED;
  +        fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.STARTED ) );
  +    }
  +
  +    /**
  +     * Request for the container to suspend all subsidiary containers
  +     * and all contained components.
  +     */
  +    public void suspend()
  +    {
  +        if( m_state < StateEvent.INITIALIZED )
  +        {
  +            throw new IllegalStateException(
  +                "Container has not been initialized." );
  +        }
  +
  +        if( m_state == StateEvent.SUSPENDED )
  +        {
  +            return;
  +        }
  +
  +        if( m_state > StateEvent.STOPPED )
  +        {
  +            throw new IllegalStateException(
  +                "Container has been stopped." );
  +        }
  +
  +        if( getLogger().isDebugEnabled() )
  +        {
  +            getLogger().debug( "container suspension" );
  +        }
  +
  +        suspendContainers();
  +        suspendComponents();
  +
  +        getLogger().debug( "container suspension complete" );
  +        m_state = StateEvent.SUSPENDED;
  +        fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.SUSPENDED ) );
  +    }
  +
  +    /**
  +     * Request for the container to resume all subsidiary containers
  +     * and all contained components.
  +     */
  +    public void resume() 
  +    {
  +        if( m_state != StateEvent.SUSPENDED )
  +        {
  +            throw new IllegalStateException(
  +                "Container is not suspended." );
  +        }
  +
  +        if( getLogger().isDebugEnabled() )
  +        {
  +            getLogger().debug( "container resuming" );
  +        }
  +
  +        //
  +        // resume subsidiary containers
  +        //
  +
  +        resumeComponents();
  +        resumeContainers();
  +
  +        getLogger().debug( "container resumption complete" );
  +        m_state = StateEvent.STARTED;
  +        fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.STARTED ) );
  +    }
  +
  +
  +   /**
  +    * Shutdown all subsidiary containers and all components in this container.
  +    * @exception Exception if a shutdown error occurs
  +    */
  +    public void shutdown() throws Exception
  +    {
  +        if( m_state < StateEvent.STARTED )
  +        {
  +            return;
  +        }
  +
  +        if( m_state == StateEvent.STOPPED )
  +        {
  +            return;
  +        }
  +
  +        if( m_state > StateEvent.STOPPED )
  +        {
  +            throw new IllegalStateException( "Already stopped." );
  +        }
  +
  +        if( getLogger().isDebugEnabled() )
  +        {
  +            getLogger().debug( "shutdown" );
  +        }
  +
  +        //
  +        // shutdown all of the nested containers before stopping
  +        // the components in this container
  +        //
  +
  +        shutdownContainers();
  +        shutdownComponents();
  +
  +        m_state = StateEvent.STOPPED;
  +        fireStateChange( new StateEvent( this, m_descriptor.getName(), StateEvent.STOPPED ) );
  +    }
  +
  +   /**
  +    * Disposal of the container.
  +    */
  +    public void dispose()
  +    {
  +        if( m_state == StateEvent.DISPOSED )
  +        {
  +            return;
  +        }
  +
  +        if( getLogger().isDebugEnabled() )
  +        {
  +            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;
       }
   
      /**
  @@ -308,21 +504,6 @@
       }
   
      /**
  -    * Startup the components in this container and startup all subsidiary 
  -    * containers.
  -    * @exception Exception if a startup error occurs
  -    */
  -    public void startup() throws Exception
  -    {
  -        if( getLogger().isDebugEnabled() )
  -        {
  -            getLogger().debug( "startup" );
  -        }
  -        startupComponents();
  -        startupContainers();
  -    }
  -
  -   /**
       * Startup the components in this container.
       * @exception Exception if a startup error occurs
       */
  @@ -383,17 +564,75 @@
       }
   
      /**
  -    * Shutdown all subsidiary containers and all components in this container.
  -    * @exception Exception if a shutdown error occurs
  +    * Startup the components in this container.
  +    * @exception Exception if a startup error occurs
       */
  -    public void shutdown() throws Exception
  +    private void suspendComponents()
       {
  -        if( getLogger().isDebugEnabled() )
  +        // not implemented - more thinking needed about the 
  +        // concepts of deployment strategies
  +    }
  +
  +   /**
  +    * Startup the containers in this container.
  +    * @exception Exception if a startup error occurs
  +    */
  +    private void suspendContainers()
  +    {
  +        Iterator iterator = m_containers.iterator();
  +        while( iterator.hasNext() )
           {
  -            getLogger().debug( "shutdown" );
  +            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
  +    */
  +    private void resumeComponents()
  +    {
  +        // not implemented - more thinking needed about the 
  +        // concepts of deployment strategies
  +    }
  +
  +   /**
  +    * 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 );
  +            }
           }
  -        shutdownContainers();
  -        shutdownComponents();
       }
   
      /**
  @@ -454,33 +693,6 @@
       }
   
      /**
  -    * Disposal of the container.
  -    */
  -    public void dispose()
  -    {
  -        if( getLogger().isDebugEnabled() )
  -        {
  -            getLogger().debug( "disposal" );
  -        }
  -
  -        disposeContainers();
  -        disposeComponents();
  -
  -        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;
  -    }
  -
  -   /**
       * Release all of the components in this container.
       */
       private void disposeComponents()
  @@ -527,9 +739,64 @@
               {
                   final String error =
                     "Could not shutdown a subsidiary container: " 
  -                     + appliance.getName() 
  -                     + " in container: " + this;
  +                     + appliance.getName();
                   getLogger().warn( error, e );
  +            }
  +        }
  +    }
  +
  +    /**
  +     * Adds a <code>StateListener</code>.
  +     * @param listener the state listener to add
  +     */
  +    public void addStateListener( StateListener listener )
  +    {
  +        synchronized( m_listeners )
  +        {
  +            m_listeners.add( listener );
  +        }
  +    }
  +
  +    /**
  +     * Removes a <code>StateListener</code>.
  +     * @param listener the state listener to remove
  +     */
  +    public void removeStateListener( StateListener listener )
  +    {
  +        synchronized( m_listeners )
  +        {
  +            m_listeners.remove( listener );
  +        }
  +    }
  +
  +    /**
  +     * Notifies all state listeners of a change in the state of the container.
  +     * @param event the state event.
  +     */
  +    protected void fireStateChange( StateEvent event )
  +    {
  +        synchronized( m_listeners )
  +        {
  +            StateListener[] listeners =
  +                (StateListener[])m_listeners.toArray( new StateListener[ 0 ] );
  +            for( int i = 0; i < listeners.length; i++ )
  +            {
  +                StateListener listener = listeners[ i ];
  +                try
  +                {
  +                    listener.stateChanged( event );
  +                }
  +                catch( Exception e )
  +                {
  +                    m_listeners.remove( listener );
  +                    final String warning =
  +                        "State listener raised on error on notification. Removing listener: "
  +                        + listener;
  +                    if( getLogger().isWarnEnabled() )
  +                    {
  +                        getLogger().warn( warning );
  +                    }
  +                }
               }
           }
       }
  
  
  
  1.1                  avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/container/StateEvent.java
  
  Index: StateEvent.java
  ===================================================================
  /* ==================================================================== 
   * The Apache Software License, Version 1.1 
   * 
   * Copyright (c) 2002 The Apache Software Foundation. All rights 
   * reserved. 
   * 
   * Redistribution and use in source and binary forms, with or without 
   * modification, are permitted provided that the following conditions 
   * are met: 
   * 
   * 1. Redistributions of source code must retain the above copyright 
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *    "This product includes software developed by the
   *    Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software 
   *    itself, if and wherever such third-party acknowledgments  
   *    normally appear.
   *
   * 4. The names "Jakarta", "Avalon", and "Apache Software Foundation" 
   *    must not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation. For more
   * information on the Apache Software Foundation, please see 
   * <http://www.apache.org/>.
   */ 
  
  package org.apache.avalon.merlin.container;
  
  import java.util.EventObject;
  
  /**
   * The <code>StateEvent</code> is an event raised by a component
   * signally a change in state.
   * @author <a href="mailto:mcconnell@apache.org">Stephen McConnell</a>
   * @version $Revision: 1.1 $ $Date: 2003/01/09 03:59:39 $
   */
  public class StateEvent extends EventObject
  {
      //============================================================
      // static
      //============================================================
  
      /**
       * Static state enumeration value indicating that the state of a container is unknown.
       */
      public static final int UNKNOWN = 0;
  
      /**
       * Static state enumeration value indicating that a container has been initialized,
       * all of its subsidiary containers have been initialized, and all component profiles
       * have been assembled.
       */
      public static final int INITIALIZED = 1;
  
      /**
       * Static state enumeration value indicating that a container has completed
       * lifecycle processing on all components requesting startup establishment.
       */
      public static final int STARTED = 2;
  
      /**
       * Static state enumeration value indicating that a container has been suspended.
       */
      public static final int SUSPENDED = 3;
  
      /**
       * Static state enumeration value indicating that a container has completed
       * the shutdown of all subsidiary containers and the internal components
       * have been shutdown.
       */
      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
      //============================================================
  
      /**
       * Container state.
       */
      private final int m_state;
  
      /**
       * Error condition
       */
      private Throwable m_error;
  
      /**
       * The name of the container.
       */
      private String m_name;
  
      //============================================================
      // constructor
      //============================================================
  
      /**
       * Creation of a new StateEvent.
       *
       * @param source the source container
       * @param name the name of the soource container
       * @param state int value of (@link #INITIALIZED}, {@link #STARTED},
       *    {@link #SUSPENDED}, {@link #STOPPED} or {@link #DISPOSED}
       */
      public StateEvent( Object source, String name, int state )
      {
          this( source, name, state, null );
      }
  
      /**
       * Creation of a new StateEvent.
       *
       * @param source the source container
       * @param name the name of the soource container
       * @param state int value of (@link #INITIALIZED}, {@link #STARTED},
       *    {@link #STOPPED} or {@link #DISPOSED}
       * @param error an error condition triggering the state change
       */
      public StateEvent( Object source, String name, int state, Throwable error )
      {
          super( source );
          m_state = state;
          m_name = name;
          m_error = error;
      }
  
      //============================================================
      // StateEvent
      //============================================================
  
      /**
       * Returns the name of the container raising the event.
       * @return the container name
       */
      public String getName()
      {
          return m_name;
      }
  
      /**
       * Returns the state that the component has raised inidicating
       * that the component has completed transitioning to that state.
       * @return the state
       */
      public int getState()
      {
          return m_state;
      }
  
      /**
       * Returns a throwable causing the state change.  This is normally a
       * null value.  If not null, the transition was a result of an error
       * condition.
       * @return the error condition
       */
      public Throwable getError()
      {
          return m_error;
      }
  
  }
  
  
  
  
  1.1                  avalon-sandbox/merlin/src/java/org/apache/avalon/merlin/container/StateListener.java
  
  Index: StateListener.java
  ===================================================================
  /* ==================================================================== 
   * The Apache Software License, Version 1.1 
   * 
   * Copyright (c) 2002 The Apache Software Foundation. All rights 
   * reserved. 
   * 
   * Redistribution and use in source and binary forms, with or without 
   * modification, are permitted provided that the following conditions 
   * are met: 
   * 
   * 1. Redistributions of source code must retain the above copyright 
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *    "This product includes software developed by the
   *    Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software 
   *    itself, if and wherever such third-party acknowledgments  
   *    normally appear.
   *
   * 4. The names "Jakarta", "Avalon", and "Apache Software Foundation" 
   *    must not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation. For more
   * information on the Apache Software Foundation, please see 
   * <http://www.apache.org/>.
   */ 
  
  package org.apache.avalon.merlin.container;
  
  /**
   * A <code>StateListener</code> listens to <code>StateEvent</code> events.
   * @author <a href="mailto:mcconnell@apache.org">Stephen McConnell</a>
   * @version $Revision: 1.1 $ $Date: 2003/01/09 03:59:39 $
   */
  
  public interface StateListener
  {
  
      /**
       * Method invoked when a container state changes.
       * @param event the state event
       */
      void stateChanged( StateEvent event );
  
  }
  
  
  
  1.6       +1 -1      avalon-sandbox/merlin/src/test/org/apache/avalon/playground/StandardComponent.xinfo
  
  Index: StandardComponent.xinfo
  ===================================================================
  RCS file: /home/cvs/avalon-sandbox/merlin/src/test/org/apache/avalon/playground/StandardComponent.xinfo,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- StandardComponent.xinfo	20 Dec 2002 11:58:01 -0000	1.5
  +++ StandardComponent.xinfo	9 Jan 2003 03:59:39 -0000	1.6
  @@ -29,7 +29,7 @@
   
     <context type="org.apache.avalon.playground.StandardContext">
       <attributes>
  -      <attribute key="urn:assembly:lifecycle.context.extension"
  +      <attribute key="urn:assembly:lifecycle.context.strategy"
             value="org.apache.avalon.playground.Contextualizable"/>
       </attributes>
     </context>
  
  
  

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