You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by da...@apache.org on 2003/11/06 20:51:44 UTC

cvs commit: incubator-geronimo/modules/kernel/src/java/org/apache/geronimo/kernel/service AbstractManagedObject2.java

dain        2003/11/06 11:51:44

  Added:       modules/kernel/src/java/org/apache/geronimo/kernel/service
                        AbstractManagedObject2.java
  Log:
  New AbstractManagedObject which respects the new dependency service. This
  should be moved to AbstractManagedObject.java when the subclasses of
  AbstractManagedObject are updated.
  
  Revision  Changes    Path
  1.1                  incubator-geronimo/modules/kernel/src/java/org/apache/geronimo/kernel/service/AbstractManagedObject2.java
  
  Index: AbstractManagedObject2.java
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2003 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 "Apache" and "Apache Software Foundation" and
   *    "Apache Geronimo" 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",
   *    "Apache Geronimo", 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.geronimo.kernel.service;
  
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.Set;
  import javax.management.ListenerNotFoundException;
  import javax.management.MBeanNotificationInfo;
  import javax.management.MBeanRegistration;
  import javax.management.MBeanServer;
  import javax.management.Notification;
  import javax.management.NotificationBroadcasterSupport;
  import javax.management.NotificationEmitter;
  import javax.management.NotificationFilter;
  import javax.management.NotificationListener;
  import javax.management.ObjectName;
  import javax.management.ReflectionException;
  import javax.management.AttributeNotFoundException;
  import javax.management.InstanceNotFoundException;
  import javax.management.MBeanServerNotification;
  import javax.management.NotificationFilterSupport;
  import javax.management.JMException;
  
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.apache.geronimo.kernel.jmx.MBeanProxyFactory;
  import org.apache.geronimo.kernel.jmx.JMXUtil;
  import org.apache.geronimo.kernel.management.EventProvider;
  import org.apache.geronimo.kernel.management.ManagedObject;
  import org.apache.geronimo.kernel.management.NotificationType;
  import org.apache.geronimo.kernel.management.State;
  import org.apache.geronimo.kernel.management.StateManageable;
  
  /**
   * Abstract implementation of JSR77 StateManageable.
   * Implementors of StateManageable may use this class and simply provide
   * doStart, doStop and sendNotification methods.
   *
   * @version $Revision: 1.1 $ $Date: 2003/11/06 19:51:43 $
   */
  public abstract class AbstractManagedObject2 implements ManagedObject, StateManageable, EventProvider, NotificationListener, MBeanRegistration, NotificationEmitter {
      protected final Log log = LogFactory.getLog(getClass());
  
      /**
       * The mbean server in which this server is registered.
       */
      protected MBeanServer server;
  
      /**
       * The unique name of this service.
       */
      protected ObjectName objectName;
  
      /**
       * The definitive list of notifications types supported by this service.
       */
      private final Set notificationTypes = new HashSet();
  
      /**
       * A dynamic proxy to the dependency service.
       */
      private DependencyService2MBean dependencyService;
  
      /**
       * The sequence number of the events.
       */
      private long sequenceNumber;
  
      /**
       * The time this application started.
       */
      private long startTime;
  
      /**
       * The name of the object blocking the start of this mbean.
       */
      private ObjectName blocker;
  
      // This must be volatile otherwise getState must be synchronized which will result in deadlock as dependent
      // objects check if each other are in one state or another (i.e., classic A calls B while B calls A)
      private volatile State state = State.STOPPED;
  
      public AbstractManagedObject2() {
          for (int i = 0; i < NotificationType.TYPES.length; i++) {
              notificationTypes.add(NotificationType.TYPES[i]);
          }
      }
  
      /**
       * The broadcaster for notifications
       */
      protected final NotificationBroadcasterSupport notificationBroadcaster = new NotificationBroadcasterSupport();
  
      /**
       * Check if component can start.  Dependencies in the dependency service have already been
       * checked at this point.
       *
       * Note: this method is called from within a synchronized block, so be careful what you call as you
       * may create a deadlock.
       *
       * @return true if the component can start; otherwise false
       */
      protected boolean canStart() {
          return true;
      }
  
      /**
       * Do the start tasks for the component.  Called in the STARTING state by
       * the start() and startRecursive() methods to perform the tasks required to
       * start the component.
       *
       * Note: this method is called from within a synchronized block, so be careful what you call as you
       * may create a deadlock.
       *
       * @throws Exception
       */
      protected void doStart() throws Exception {
      }
  
      /**
       * Check if component can stop.  Dependencies in the dependency service have already been
       * checked at this point.
       *
       * Note: this method is called from within a synchronized block, so be careful what you call as you
       * may create a deadlock.
       *
       * @return true if the component can stop; otherwise false
       */
      protected boolean canStop() {
          return true;
      }
  
      /**
       * Do the stop tasks for the component.  Called in the STOPPING state by the stop()
       * method to perform the tasks required to stop the component.
       *
       * Note: this method is called from within a synchronized block, so be careful what you call as you
       * may create a deadlock.
       *
       * @throws Exception
       */
      protected void doStop() throws Exception {
      }
  
      /**
       * Do the failure tasks for the component.  Called in the FAILED state by the fail()
       * method to perform the tasks required to cleanup a failed component.
       *
       * Note: this method is called from within a synchronized block, so be careful what you call as you
       * may create a deadlock.
       */
      protected void doFail() {
      }
  
      public synchronized ObjectName preRegister(MBeanServer server, ObjectName objectName) throws Exception {
          this.server = server;
          this.objectName = objectName;
          dependencyService = (DependencyService2MBean) MBeanProxyFactory.getProxy(
                  DependencyService2MBean.class,
                  server,
                  new ObjectName("geronimo.boot:role=DependencyService2"));
  
          return objectName;
      }
  
      public void postRegister(Boolean ignored) {
          sendNotification(NotificationType.OBJECT_CREATED);
      }
  
      public void preDeregister() throws Exception {
      }
  
      public void postDeregister() {
          sendNotification(NotificationType.OBJECT_DELETED);
          synchronized (this) {
              server = null;
              objectName = null;
              dependencyService = null;
          }
      }
  
      public final String getObjectName() {
          return objectName.toString();
      }
  
      public final boolean isStateManageable() {
          return true;
      }
  
      public boolean isStatisticsProvider() {
          return false;
      }
  
      public final boolean isEventProvider() {
          return true;
      }
  
      public final String[] getEventTypes() {
          return (String[]) notificationTypes.toArray(new String[notificationTypes.size()]);
      }
  
      public MBeanNotificationInfo[] getNotificationInfo() {
          return new MBeanNotificationInfo[]{
              new MBeanNotificationInfo(getEventTypes(), "javax.management.Notification", "J2EE Notifications")
          };
      }
  
      protected void addEventType(String eventType) {
          notificationTypes.add(eventType);
      }
  
      public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) {
          notificationBroadcaster.addNotificationListener(listener, filter, handback);
      }
  
      public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException {
          notificationBroadcaster.removeNotificationListener(listener);
      }
  
      public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException {
          notificationBroadcaster.removeNotificationListener(listener, filter, handback);
      }
  
      /**
       * Sends the specified MBean notification.
       *
       * Note:  This method can not be call while the current thread holds a syncronized lock on this MBean,
       * because this method sends JMX notifications.  Sending a general notification from a synchronized block
       * is a bad idea and therefore not allowed.
       *
       * @param type the notification type to send
       */
      public final void sendNotification(String type) {
          assert !Thread.holdsLock(this): "This method cannot be called while holding a syncrhonized lock on this";
          notificationBroadcaster.sendNotification(new Notification(type, objectName, sequenceNumber++));
      }
  
      public void sendNotification(Notification notification) {
          assert !Thread.holdsLock(this): "This method cannot be called while holding a syncrhonized lock on this";
          notificationBroadcaster.sendNotification(notification);
      }
  
      public synchronized final long getStartTime() {
          return startTime;
      }
  
      /**
       * Moves this MBean to the STARTING state and then attempst to move this MBean immedately to the STARTED
       * state.
       *
       * Note:  This method cannot be call while the current thread holds a syncronized lock on this MBean,
       * because this method sends JMX notifications.  Sending a general notification from a synchronized block
       * is a bad idea and therefore not allowed.
       *
       * @throws Exception  If an exception occurs while starting this MBean
       */
      public final void start() throws Exception {
          assert !Thread.holdsLock(this): "This method cannot be called while holding a syncrhonized lock on this";
  
          // Move to the starting state
          synchronized (this) {
              State state = getStateInstance();
              if (state == State.STARTING || state == State.RUNNING) {
                  return;
              }
              setStateInstance(State.STARTING);
          }
          sendNotification(State.STARTING.getEventTypeValue());
  
          attemptFullStart();
      }
  
      /**
       * Starts this MBean and then attempts to start all of the start dependent children of this MBean.
       *
       * Note:  This method can not be call while the current thread holds a syncronized lock on this MBean,
       * because this method sends JMX notifications.  Sending a general notification from a synchronized block
       * is a bad idea and therefore not allowed.
       *
       * @throws Exception  if a problem occurs will starting this MBean or any child MBean
       */
      public final void startRecursive() throws Exception {
          assert !Thread.holdsLock(this): "This method cannot be called while holding a syncrhonized lock on this";
  
          State state = getStateInstance();
          if (state != State.STOPPED && state != State.FAILED) {
              // Cannot startRecursive while in the stopping state
              // Dain: I don't think we can throw an exception here because there is no way for the caller
              // to lock the instance and check the state before calling
              return;
          }
  
          // get myself starting
          start();
  
          // startRecursive all of objects that depend on me
          Set dependents = dependencyService.getChildren(objectName);
          for (Iterator iterator = dependents.iterator(); iterator.hasNext();) {
              ObjectName dependent = (ObjectName) iterator.next();
              try {
                  server.invoke(dependent, "startRecursive", null, null);
              } catch (ReflectionException e) {
                  if (e.getTargetException() instanceof NoSuchMethodException) {
                      // did not have a startRecursive method - ok
                  } else {
                      throw e;
                  }
              }
          }
      }
  
      /**
       * Moves this MBean to the STOPPING state, calls stop on all start dependent children, and then attempt
       * to move this MBean to the STOPPED state.
       *
       * Note:  This method can not be call while the current thread holds a syncronized lock on this MBean,
       * because this method sends JMX notifications.  Sending a general notification from a synchronized block
       * is a bad idea and therefore not allowed.
       *
       * @throws Exception  If an exception occurs while stoping this MBean or any of the childern
       */
      public final void stop() throws Exception {
          assert !Thread.holdsLock(this): "This method cannot be called while holding a syncrhonized lock on this";
  
          // move to the stopping state
          synchronized (this) {
              State state = getStateInstance();
              if (state == State.STOPPED || state == State.STOPPING) {
                  return;
              }
              setStateInstance(State.STOPPING);
          }
          sendNotification(State.STOPPING.getEventTypeValue());
  
          // Don't try to stop dependents from within a synchronized block... this should reduce deadlocks
  
          // stop all of my dependent objects
          Set dependents = dependencyService.getChildren(objectName);
          for (Iterator iterator = dependents.iterator(); iterator.hasNext();) {
              ObjectName child = (ObjectName) iterator.next();
              try {
                  log.trace("Checking if child is running: child=" + child);
                  if (((Integer) server.getAttribute(child, "state")).intValue() == State.RUNNING_INDEX) {
                      log.trace("Stopping child: child=" + child);
                      server.invoke(child, "stop", null, null);
                      log.trace("Stopped child: child=" + child);
                  }
              } catch (Exception ignore) {
                  // not a big deal... did my best
              }
          }
  
          attemptFullStop();
      }
  
      /**
       * Moves this MBean to the FAILED state.  There are no calls to dependent children, but they will be notified
       * using standard J2EE management notification.
       *
       * Note:  This method can not be call while the current thread holds a syncronized lock on this MBean,
       * because this method sends JMX notifications.  Sending a general notification from a synchronized block
       * is a bad idea and therefore not allowed.
       */
      final void fail() {
          assert !Thread.holdsLock(this): "This method cannot be called while holding a syncrhonized lock on this";
  
          synchronized (this) {
              State state = getStateInstance();
              if (state == State.STOPPED || state == State.FAILED) {
                  return;
              }
              doSafeFail();
              setStateInstance(State.FAILED);
          }
          sendNotification(State.FAILED.getEventTypeValue());
      }
  
      /**
       * Attempts to bring the component into the fully running state. If an Exception occurs while
       * starting the component, the component will be failed.
       * @throws Exception if a problem occurs while starting the component
       *
       * Note: Do not call this from within a synchronized block as it makes may send a JMX notification
       */
      void attemptFullStart() throws Exception {
          assert !Thread.holdsLock(this): "This method cannot be called while holding a syncrhonized lock on this";
  
          State newState = null;
          try {
              synchronized (this) {
                  try {
                      // if we are still trying to start and can start now... start
                      if (getStateInstance() != State.STARTING) {
                          return;
                      }
  
                      // check if an mbean is blocking us from starting
                      blocker = dependencyService.checkBlocker(objectName);
                      if (blocker != null) {
                          try {
                              // register for state change with the blocker
                              NotificationFilterSupport stoppedFilter = new NotificationFilterSupport();
                              stoppedFilter.enableType(NotificationType.STATE_STOPPED);
                              stoppedFilter.enableType(NotificationType.STATE_FAILED);
                              stoppedFilter.enableType(NotificationType.OBJECT_DELETED);
                              server.addNotificationListener(blocker, this, stoppedFilter, null);
  
                              // watch for the blocker to unregister
                              NotificationFilterSupport mbeanServerFilter = new NotificationFilterSupport();
                              mbeanServerFilter.enableType(MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
                              server.addNotificationListener(JMXUtil.DELEGATE_NAME, this, mbeanServerFilter, null);
  
                              // done for now... wait for the blocker to die
                              return;
                          } catch (InstanceNotFoundException e) {
                              // blocker died before we could get going... not a big deal
                          }
                      }
  
                      // check if all of the mbeans we depend on are running
                      Set parents = dependencyService.getParents(objectName);
                      for (Iterator i = parents.iterator(); i.hasNext();) {
                          ObjectName parent = (ObjectName) i.next();
                          if (!server.isRegistered(parent)) {
                              log.trace("Cannot run because parent is not registered: parent=" + parent);
                              return;
                          }
                          try {
                              log.trace("Checking if parent is running: parent=" + parent);
                              if (((Integer) server.getAttribute(parent, "State")).intValue() != State.RUNNING_INDEX) {
                                  log.trace("Cannot run because parent is not running: parent=" + parent);
                                  return;
                              }
                              log.trace("Parent is running: parent=" + parent);
                          } catch (AttributeNotFoundException e) {
                              // ok -- parent is not a startable
                              log.trace("Parent does not have a State attibute");
                          } catch (InstanceNotFoundException e) {
                              // depended on instance was removed bewteen the register check and the invoke
                              log.trace("Cannot run because parent is not registered: parent=" + parent);
                              return;
                          } catch (Exception e) {
                              // problem getting the attribute, parent has most likely failed
                              log.trace("Cannot run because an error occurred while checking if parent is running: parent=" + parent);
                              return;
                          }
                      }
  
                      // check if our targets are ready to start
                      if (canStart()) {
                          if (blocker != null) {
                              // remove any open watches on a blocker
                              try {
                                  server.removeNotificationListener(blocker, this);
                              } catch (JMException ignore) {
                                  // don't care, just cleaning up... blocker is most likely dead
                              }
                              try {
                                  server.removeNotificationListener(JMXUtil.DELEGATE_NAME, this);
                              } catch (JMException ignore) {
                                  // this should never happen... maybe server is dead
                              }
                              blocker = null;
                          }
  
                          doStart();
                          setStateInstance(State.RUNNING);
                          newState = State.RUNNING;
                      }
                  } catch (Exception e) {
                      doSafeFail();
                      setStateInstance(State.FAILED);
                      newState = State.FAILED;
                      throw e;
                  } catch (Error e) {
                      doSafeFail();
                      setStateInstance(State.FAILED);
                      newState = State.FAILED;
                      throw e;
                  }
              }
          } finally {
              if (newState != null) {
                  sendNotification(newState.getEventTypeValue());
              }
          }
      }
  
      /**
       * Attempt to bring the component into the fully stopped state.
       * If an exception occurs while stopping the component, the component will be failed.
       * @throws Exception if a problem occurs while stopping the component
       *
       * Note: Do not call this from within a synchronized block as it makes may send a JMX notification
       */
      void attemptFullStop() throws Exception {
          assert !Thread.holdsLock(this): "This method cannot be called while holding a syncrhonized lock on this";
  
          State newState = null;
          try {
              synchronized (this) {
                  // if we are still trying to stop...
                  if (getStateInstance() != State.STOPPING) {
                      return;
                  }
                  try {
                      // check if all of the mbeans depending on us are stopped
                      Set children = dependencyService.getChildren(objectName);
                      for (Iterator i = children.iterator(); i.hasNext();) {
                          ObjectName child = (ObjectName) i.next();
                          if (server.isRegistered(child)) {
                              try {
                                  log.trace("Checking if child is stopped: child=" + child);
                                  int state = ((Integer) server.getAttribute(child, "State")).intValue();
                                  if (state == State.RUNNING_INDEX) {
                                      log.trace("Cannot stop because child is still running: child=" + child);
                                      return;
                                  }
                              } catch (AttributeNotFoundException e) {
                                  // ok -- dependect bean is not state manageable
                                  log.trace("Child does not have a State attibute");
                              } catch (InstanceNotFoundException e) {
                                  // depended on instance was removed between the register check and the invoke
                              } catch (Exception e) {
                                  // problem getting the attribute, depended on bean has most likely failed
                                  log.trace("Cannot run because an error occurred while checking if child is stopped: child=" + child);
                                  return;
                              }
                          }
                      }
  
                      // if we can stop, stop
                      if (canStop()) {
                          doStop();
                          setStateInstance(State.STOPPED);
                          newState = State.STOPPED;
                      }
                  } catch (Exception e) {
                      doSafeFail();
                      setStateInstance(State.FAILED);
                      newState = State.FAILED;
                      throw e;
                  } catch (Error e) {
                      doSafeFail();
                      setStateInstance(State.FAILED);
                      newState = State.FAILED;
                      throw e;
                  }
              }
          } finally {
              if (newState != null) {
                  sendNotification(newState.getEventTypeValue());
              }
          }
      }
  
      /**
       * Call safe fail, but catches all RutimeExceptions and Errors.
       * These problems are logged but ignored.
       *
       * Note: This must be called while holding a lock on this
       */
      private void doSafeFail() {
          assert Thread.holdsLock(this): "This method can only called while holding a syncrhonized lock on this";
  
          try {
              doFail();
          } catch (RuntimeException e) {
              log.warn("RuntimeError thrown from doFail", e);
          } catch (Error e) {
              log.warn("RuntimeError thrown from doFail", e);
          }
      }
  
      public void handleNotification(Notification n, Object o) {
          String type = n.getType();
          if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION.equals(type)) {
              MBeanServerNotification notification = (MBeanServerNotification) n;
              ObjectName source = notification.getMBeanName();
              if (source.equals(blocker)) {
                  try {
                      attemptFullStart();
                  } catch (Exception e) {
                      log.warn("A problem occured while attempting to start", e);
                  }
              }
          } else if (type.equals(NotificationType.STATE_STOPPED) ||
                  type.equals(NotificationType.STATE_FAILED) ||
                  type.equals(NotificationType.OBJECT_DELETED)) {
  
              ObjectName source = (ObjectName) n.getSource();
              if (source.equals(blocker)) {
                  try {
                      attemptFullStart();
                  } catch (Exception e) {
                      log.warn("A problem occured while attempting to start", e);
                  }
  
              }
          }
      }
  
      public int getState() {
          return state.toInt();
      }
  
      public final State getStateInstance() {
          return state;
      }
  
      /**
       * Set the Component state.
       * @param newState the target state to transition
       * @throws IllegalStateException Thrown if the transition is not supported by the J2EE Management lifecycle.
       */
      private synchronized void setStateInstance(State newState) throws IllegalStateException {
          switch (state.toInt()) {
          case State.STOPPED_INDEX:
              switch (newState.toInt()) {
              case State.STARTING_INDEX:
                  break;
              case State.STOPPED_INDEX:
              case State.RUNNING_INDEX:
              case State.STOPPING_INDEX:
              case State.FAILED_INDEX:
                  throw new IllegalStateException(
                          "Can not transition to " + newState + " state from " + state);
              }
              break;
  
          case State.STARTING_INDEX:
              switch (newState.toInt()) {
              case State.RUNNING_INDEX:
              case State.FAILED_INDEX:
              case State.STOPPING_INDEX:
                  break;
              case State.STOPPED_INDEX:
              case State.STARTING_INDEX:
                  throw new IllegalStateException(
                          "Can not transition to " + newState + " state from " + state);
              }
              break;
  
          case State.RUNNING_INDEX:
              switch (newState.toInt()) {
              case State.STOPPING_INDEX:
              case State.FAILED_INDEX:
                  break;
              case State.STOPPED_INDEX:
              case State.STARTING_INDEX:
              case State.RUNNING_INDEX:
                  throw new IllegalStateException(
                          "Can not transition to " + newState + " state from " + state);
              }
              break;
  
          case State.STOPPING_INDEX:
              switch (newState.toInt()) {
              case State.STOPPED_INDEX:
              case State.FAILED_INDEX:
                  break;
              case State.STARTING_INDEX:
              case State.RUNNING_INDEX:
              case State.STOPPING_INDEX:
                  throw new IllegalStateException(
                          "Can not transition to " + newState + " state from " + state);
              }
              break;
  
          case State.FAILED_INDEX:
              switch (newState.toInt()) {
              case State.STARTING_INDEX:
              case State.STOPPING_INDEX:
                  break;
              case State.RUNNING_INDEX:
              case State.STOPPED_INDEX:
              case State.FAILED_INDEX:
                  throw new IllegalStateException(
                          "Can not transition to " + newState + " state from " + state);
              }
              break;
          }
          log.debug(objectName.toString() + " State changed from " + state + " to " + newState);
          if (newState == State.RUNNING) {
              startTime = System.currentTimeMillis();
          }
          state = newState;
      }
  
      public String toString() {
          if (objectName == null) {
              return super.toString();
          }
          return objectName.toString();
      }
  }