leosimons    2003/02/09 06:08:52

  Added:       src/java/org/apache/avalon/framework State.java
  Log:
  Wrote a utility class which can be used to verify the lifecycle contract for 
a particular component is being properly followed.
  
  Revision  Changes    Path
  1.1                  avalon/src/java/org/apache/avalon/framework/State.java
  
  Index: State.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 "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 [EMAIL PROTECTED]
   *
   * 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.framework;
  
  /**
   * Encodes the lifecycle contract. Typical usage is to check whether a
   * change from one state to another is allowed. This makes it easy for
   * a component to protect itself against non-compliant containers.
   *
   * <pre>
   * class MyComponent implements Initializeable
   * {
   *     private m_state = State.STATIC;
   *
   *     MyComponent()
   *     {
   *         m_state = State.change( m_state, State.CONSTRUCTED, this );
   *     }
   *     initialize()
   *     {
   *         m_state = State.change( m_state, State.INITIALIZED, this );
   *     }
   * }
   * </pre>
   *
   * Note that this class currently doesn't support the Re* interfaces, as
   * their contract is insufficiently specified.
   *
   * @author <a href="mailto:dev@avalon.apache.org";>Avalon Development Team</a>
   * @version $Id: State.java,v 1.1 2003/02/09 14:08:52 leosimons Exp $
   * @since 4.1.4
   */
  public class State
  {
      // ----------------------------------------------------------------------
      //  Static Properties
      // ----------------------------------------------------------------------
  
      /**
       * Static state enumeration value indicating that the component has not
       * been constructed.
       */
      public static final int STATIC = 0;
  
      /**
       * Static state enumeration value indicating that the component has been
       * constructed
       */
      public static final int CONSTRUCTED = 1;
  
      /**
       * Static state enumeration value indicating that logging has been
       * enabled for this component
       */
      public static final int LOGENABLED = 2;
  
      /**
       * Static state enumeration value indicating that the component has been
       * contextualized
       */
      public static final int CONTEXTUALIZED = 3;
  
      /**
       * Static state enumeration value indicating that the component has been
       * serviced <i>or</i> it has been composed.
       */
      public static final int SERVICED = 4;
  
      /**
       * Static state enumeration value indicating that the component has been
       * serviced <i>or</i> it has been composed.
       */
      public static final int COMPOSED = 5;
  
      /**
       * Static state enumeration value indicating that the component has been
       * configured
       */
      public static final int CONFIGURED = 6;
  
      /**
       * Static state enumeration value indicating that the component has been
       * parameterized
       */
      public static final int PARAMETERIZED = 7;
  
      /**
       * Static state enumeration value indicating that the component has been
       * initialized
       */
      public static final int INITIALIZED = 8;
  
      /**
       * Static state enumeration value indicating that the component has been
       * started
       */
      public static final int STARTED = 9;
  
      /**
       * Static state enumeration value indicating that the component has been
       * suspended
       */
      public static final int SUSPENDED = 10;
  
      /* not supported because they're not supported anywhere:
  
          RECONTEXTUALIZED
          RECONFIGURED
          REPARAMETERIZED
      */
  
      /**
       * Static state enumeration value indicating that the component has been
       * resumed
       */
      public static final int RESUMED = 14;
  
      /**
       * Static state enumeration value indicating that the component has been
       * stopped
       */
      public static final int STOPPED = 15;
  
      /**
       * Static state enumeration value indicating that the component has been
       * disposed
       */
      public static final int DISPOSED = 16;
  
      /**
       * Static arraylist containing the string representations of the various 
states
       * Use a state enumeration value as the array index, ie:
       *
       * <pre>
       *     final String disposed = STATE_NAMES[DISPOSED];
       * </pre>
       */
      public static final String[] STATE_NAMES = new String[17];
  
      static
      {
          STATE_NAMES[STATIC] = "STATIC";
          STATE_NAMES[CONSTRUCTED] = "CONSTRUCTED";
          STATE_NAMES[LOGENABLED] = "LOGENABLED";
          STATE_NAMES[CONTEXTUALIZED] = "CONTEXTUALIZED";
          STATE_NAMES[SERVICED] = "SERVICED";
          STATE_NAMES[COMPOSED] = "COMPOSED";
          STATE_NAMES[CONFIGURED] = "CONFIGURED";
          STATE_NAMES[PARAMETERIZED] = "PARAMETERIZED";
          STATE_NAMES[INITIALIZED] = "INITIALIZED";
          STATE_NAMES[STARTED] = "STARTED";
          STATE_NAMES[SUSPENDED] = "SUSPENDED";
          STATE_NAMES[RESUMED] = "RESUMED";
          STATE_NAMES[STOPPED] = "STOPPED";
          STATE_NAMES[DISPOSED] = "DISPOSED";
      }
  
      /**
       * Static Map containing the class that a component must implement in 
order
       * for a lifecycle state to apply to it. Use a state enumeration value as 
the
       * key, ie:
       *
       * <pre>
       *     final Class disposable = STATE_INTERFACES[DISPOSABLE];
       *     final boolean isTrue = Disposable.class.equals(disposable);
       * </pre>
       */
      public static final Class[] STATE_INTERFACES = new Class[17];
  
      static
      {
          STATE_INTERFACES[STATIC] = Object.class;
          STATE_INTERFACES[CONSTRUCTED] = Object.class;
          STATE_INTERFACES[LOGENABLED] =
                  org.apache.avalon.framework.logger.LogEnabled.class;
          STATE_INTERFACES[CONTEXTUALIZED] =
                  org.apache.avalon.framework.context.Contextualizable.class;
          STATE_INTERFACES[SERVICED] =
                  org.apache.avalon.framework.service.Serviceable.class;
          STATE_INTERFACES[COMPOSED] =
                  org.apache.avalon.framework.component.Composable.class;
          STATE_INTERFACES[CONFIGURED] =
                  org.apache.avalon.framework.configuration.Configurable.class;
          STATE_INTERFACES[PARAMETERIZED] =
                  org.apache.avalon.framework.parameters.Parameterizable.class;
          STATE_INTERFACES[INITIALIZED] =
                  org.apache.avalon.framework.activity.Initializable.class;
          STATE_INTERFACES[STARTED] =
                  org.apache.avalon.framework.activity.Startable.class;
          STATE_INTERFACES[SUSPENDED] =
                  org.apache.avalon.framework.activity.Suspendable.class;
          STATE_INTERFACES[RESUMED] =
                  org.apache.avalon.framework.activity.Suspendable.class;
          STATE_INTERFACES[STOPPED] =
                  org.apache.avalon.framework.activity.Startable.class;
          STATE_INTERFACES[DISPOSED] =
                  org.apache.avalon.framework.activity.Disposable.class;
      }
  
      // ----------------------------------------------------------------------
      //  Static Methods
      // ----------------------------------------------------------------------
  
      /**
       * Test whether a specific change is allowed according to the lifecycle
       * contract,  provided that a component indeed supports the specified
       * lifecycle stages.
       *
       * @param from the static state enumeration value indicating the current
       *             component state
       * @param to the static state enumeration value indicating the proposed
       *             component state
       * @return true if the change is allowed, false otherwise
       */
      public static boolean isValidChange( int from, int to )
      {
          if( from > DISPOSED || to >= DISPOSED )
              return false;
  
          if( from < STATIC || to < STATIC )
              return false;
  
          if( from == RESUMED && to == SUSPENDED ) // you can re-suspend a 
resumed component
              return true;
  
          if( from == SERVICED && to == COMPOSED ) // cannot compose a 
serviceable!
              return true;
  
          if(from == to) // this is normally not accepted, but...
          {
              if((to != STATIC )  // classes are free to exist
                 && (to != CONSTRUCTED )  // as are objects
                 && (to != STARTED ) // start() may be called more than once
                 && (to != STOPPED ) // stop() may be called more than once
                 && (to != RESUMED ) // resume() may be called more than once
                )
                  return true;
              else
                  return false;
          }
  
          if( to != from+1 ) // allowed for suspension or stages which can be 
set multiple
                             // times in a row, but that is covered above
              return false;
  
          return true;
      }
  
      /**
       * Test whether a specific change is allowed according to the lifecycle
       * contract,  provided that a component indeed supports the specified
       * lifecycle stages, and throw an IllegalStateException with appropriate
       * error message if it is not.
       *
       * @param fromState the static state enumeration value indicating the
       *        current component state
       * @param toState the static state enumeration value indicating the
       *        proposed component state
       * @param component the component on which the change is proposed. May
       *        not be null; in that case, use isValidChange() instead
       * @throws IllegalStateException if the proposed change is not acceptable
       *         for this component
       */
      public static void testChange( int fromState, int toState, Object 
component ) throws IllegalStateException
      {
          // 1) make sure arguments make sense
          int from = ( fromState >= 0 )? fromState : 0 ; // no negative state 
please
          int to =  ( toState >= 0 )? toState : 0 ; // no negative state please
          from = ( from > DISPOSED )? DISPOSED : from ; // no bigger than 
DISPOSED please
          to = ( to > DISPOSED )? DISPOSED : to ; // no bigger than DISPOSED 
please
  
          final Class clazz = component.getClass();
  
          // 2) check if the proposed state is implemented in the component
          final Class fromClass = STATE_INTERFACES[from];
          final Class toClass = STATE_INTERFACES[to];
  
          if( fromClass.isAssignableFrom( clazz ) )
          {
              // sorry, were in a bad state already
              throw new IllegalStateException( "Cannot change to state " + 
STATE_NAMES[to] +
                      " because the current state is " + STATE_NAMES[from] +
                      ", which is not acceptable for this component!" );
          }
          if( toClass.isAssignableFrom( clazz ) )
          {
              // sorry, not acceptable!
              throw new IllegalStateException( "Cannot change to state " + 
STATE_NAMES[to] +
                      " because the current component doesn't support that 
state!" );
          }
  
          // 3) check that we're not DISPOSED already
          if( from == DISPOSED ) // last state already!
          {
              // sorry, not acceptable!
              throw new IllegalStateException( "Cannot change to state " + 
STATE_NAMES[to] +
                  " because the current state, " + STATE_NAMES[DISPOSED] + " is 
the final state!" );
          }
  
          // 4) check that we're not composing after servicing
          if( from == SERVICED && to == COMPOSED )
          {
              // sorry, not acceptable!
              throw new IllegalStateException( "Cannot change to state " + 
STATE_NAMES[to] +
                  " because " + STATE_NAMES[from] + " cannot be used along 
with" +
                  STATE_NAMES[to] + "!" );
          }
  
          // 5) check that the proposed new state isn't too small
          if( to < from ) // this is normally not okay, except for suspension,
                           // but that is covered inside
          {
               // you can re-suspend a resumed component
              if( from == RESUMED
                  && to == SUSPENDED
                )
                  return;
  
              // sorry, from always needs to be bigger than to
              throw new IllegalStateException( "Cannot change to state " + 
STATE_NAMES[to] +
                      " because the current state is " + STATE_NAMES[from] + 
"!" );
          }
  
          // 6) check that the proposed state isn't the same as the current 
state
          //    if that is not allowed
          if(from == to)        // this is normally not accepted, but...
          {
              if(
                 (to != STATIC )  // classes are free to exist
              && (to != CONSTRUCTED )  // as are objects
              && (to != STARTED ) // start() may be called more than once
              && (to != STOPPED ) // stop() may be called more than once
              && (to != RESUMED ) // resume() may be called more than once
                )
                  return;
  
              throw new IllegalStateException( "Cannot change to state " + 
STATE_NAMES[to] +
                      " because already in that state!" );
          }
  
          // 7) figure out what the next state now must be....
  
          // this indexing would fail for DISPOSED+1, but we've ruled out
          // from being DISPOSED already
          int nextState = from + 1;
          Class nextStateClass = STATE_INTERFACES[nextState];
          for( ;
               (
                  !nextStateClass.isAssignableFrom( clazz )
                  && nextState < DISPOSED
               );
               nextState = nextState + 1)
          {
              nextStateClass = STATE_INTERFACES[nextState];
          }
  
          // the proposed state is wrong!
          if( to != nextState )
              throw new IllegalStateException( "Cannot change to state " + 
STATE_NAMES[to] +
                      " because the current state is " + STATE_NAMES[from] + ", 
and the" +
                      " state " + STATE_NAMES[nextState] + " should come next!" 
);
      }
  
      /**
       * Test whether a specific change is allowed according to the lifecycle
       * contract,  provided that a component indeed supports the specified
       * lifecycle states, and throw an IllegalStateException with appropriate
       * error message if it is not. If it is, return the new state.
       *
       * @param fromState the static state enumeration value indicating the
       *        current component state
       * @param toState the static state enumeration value indicating the
       *        proposed component state
       * @param component the component on which the change is proposed. May
       *        not be null; in that case, use isValidChange() instead
       * @return the new state
       * @throws IllegalStateException if the proposed change is not acceptable
       *         for this component
       */
      public static int change( int from, int to, Object component ) throws 
IllegalStateException
      {
          testChange( from, to, component );
          return to;
      }
  
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to