Home » geronimo-2.2-source-release » org.apache.geronimo.gbean.runtime » [javadoc | source]

    1   /**
    2    *  Licensed to the Apache Software Foundation (ASF) under one or more
    3    *  contributor license agreements.  See the NOTICE file distributed with
    4    *  this work for additional information regarding copyright ownership.
    5    *  The ASF licenses this file to You under the Apache License, Version 2.0
    6    *  (the "License"); you may not use this file except in compliance with
    7    *  the License.  You may obtain a copy of the License at
    8    *
    9    *     http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    *  Unless required by applicable law or agreed to in writing, software
   12    *  distributed under the License is distributed on an "AS IS" BASIS,
   13    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    *  See the License for the specific language governing permissions and
   15    *  limitations under the License.
   16    */
   17   package org.apache.geronimo.gbean.runtime;
   18   
   19   import org.slf4j.Logger;
   20   import org.slf4j.LoggerFactory;
   21   import org.apache.geronimo.gbean.AbstractName;
   22   import org.apache.geronimo.kernel.DependencyManager;
   23   import org.apache.geronimo.kernel.GBeanNotFoundException;
   24   import org.apache.geronimo.kernel.Kernel;
   25   import org.apache.geronimo.kernel.management.State;
   26   
   27   import java.util.Iterator;
   28   import java.util.Set;
   29   
   30   /**
   31    * @version $Rev: 732537 $ $Date: 2009-01-07 14:20:16 -0800 (Wed, 07 Jan 2009) $
   32    */
   33   public class GBeanInstanceState
   34   {
   35       private static final Logger log = LoggerFactory.getLogger(GBeanInstanceState.class);
   36   
   37       /**
   38        * The GBeanInstance in which this server is registered.
   39        */
   40       private final GBeanInstance gbeanInstance;
   41   
   42       /**
   43        * The kernel in which this server is registered.
   44        */
   45       private final Kernel kernel;
   46   
   47       /**
   48        * The unique name of this service.
   49        */
   50       private final AbstractName abstractName;
   51   
   52       /**
   53        * The dependency manager
   54        */
   55       private final DependencyManager dependencyManager;
   56   
   57       /**
   58        * The broadcaster of lifecycle events
   59        */
   60       private final LifecycleBroadcaster lifecycleBroadcaster;
   61   
   62       // This must be volatile otherwise getState must be synchronized which will result in deadlock as dependent
   63       // objects check if each other are in one state or another (i.e., classic A calls B while B calls A)
   64       private volatile State state = State.STOPPED;
   65   
   66       GBeanInstanceState(AbstractName abstractName, Kernel kernel, DependencyManager dependencyManager, GBeanInstance gbeanInstance, LifecycleBroadcaster lifecycleBroadcaster) {
   67           this.abstractName = abstractName;
   68           this.kernel = kernel;
   69           this.dependencyManager = dependencyManager;
   70           this.gbeanInstance = gbeanInstance;
   71           this.lifecycleBroadcaster = lifecycleBroadcaster;
   72       }
   73   
   74       /**
   75        * Moves this MBean to the {@link org.apache.geronimo.kernel.management.State#STARTING} state and then attempts to move this MBean immediately
   76        * to the {@link org.apache.geronimo.kernel.management.State#RUNNING} state.
   77        * <p/>
   78        * Note:  This method cannot be called while the current thread holds a synchronized lock on this MBean,
   79        * because this method sends JMX notifications. Sending a general notification from a synchronized block
   80        * is a bad idea and therefore not allowed.
   81        */
   82       public final void start() {
   83           assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";
   84   
   85           // Move to the starting state
   86           State originalState;
   87           synchronized (this) {
   88               originalState = getStateInstance();
   89               if (originalState == State.RUNNING) {
   90                   return;
   91               }
   92               // only try to change states if we are not already starting
   93               if (originalState != State.STARTING) {
   94                   setStateInstance(State.STARTING);
   95               }
   96           }
   97   
   98           // only fire a notification if we are not already starting
   99           if (originalState != State.STARTING) {
  100               lifecycleBroadcaster.fireStartingEvent();
  101           }
  102   
  103           attemptFullStart();
  104       }
  105   
  106       /**
  107        * Starts this MBean and then attempts to start all of its start dependent children.
  108        * <p/>
  109        * Note:  This method cannot be call while the current thread holds a synchronized lock on this MBean,
  110        * because this method sends JMX notifications.  Sending a general notification from a synchronized block
  111        * is a bad idea and therefore not allowed.
  112        */
  113       public final void startRecursive() {
  114           assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";
  115   
  116           State state = getStateInstance();
  117           if (state != State.STOPPED && state != State.FAILED && state != State.RUNNING) {
  118               // Cannot startRecursive while in the stopping state
  119               // Dain: I don't think we can throw an exception here because there is no way for the caller
  120               // to lock the instance and check the state before calling
  121               return;
  122           }
  123   
  124           // get myself starting
  125           start();
  126   
  127           // startRecursive all of objects that depend on me
  128           Set dependents = dependencyManager.getChildren(abstractName);
  129           for (Iterator iterator = dependents.iterator(); iterator.hasNext();) {
  130               AbstractName dependent = (AbstractName) iterator.next();
  131               try {
  132                   kernel.startRecursiveGBean(dependent);
  133               } catch (GBeanNotFoundException e) {
  134                   // this is ok the gbean died before we could start it
  135               } catch (Exception e) {
  136                   // there is something wrong with this gbean... skip it
  137               }
  138           }
  139       }
  140   
  141       /**
  142        * Moves this MBean to the STOPPING state, calls stop on all start dependent children, and then attempt
  143        * to move this MBean to the STOPPED state.
  144        * <p/>
  145        * Note:  This method can not be call while the current thread holds a syncronized lock on this MBean,
  146        * because this method sends JMX notifications.  Sending a general notification from a synchronized block
  147        * is a bad idea and therefore not allowed.
  148        */
  149       public final void stop() {
  150           assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";
  151   
  152           // move to the stopping state
  153           State originalState;
  154           synchronized (this) {
  155               originalState = getStateInstance();
  156               if (originalState == State.STOPPED || originalState == State.FAILED) {
  157                   return;
  158               }
  159   
  160               // only try to change states if we are not already stopping
  161               if (originalState != State.STOPPING) {
  162                   setStateInstance(State.STOPPING);
  163               }
  164           }
  165   
  166           // only fire a notification if we are not already stopping
  167           if (originalState != State.STOPPING) {
  168               lifecycleBroadcaster.fireStoppingEvent();
  169           }
  170   
  171           // Don't try to stop dependents from within a synchronized block... this should reduce deadlocks
  172   
  173           // stop all of my dependent objects
  174           Set dependents = dependencyManager.getChildren(abstractName);
  175           for (Iterator iterator = dependents.iterator(); iterator.hasNext();) {
  176               AbstractName child = (AbstractName) iterator.next();
  177               try {
  178                   log.trace("Checking if child is running: child={}", child);
  179                   if (kernel.getGBeanState(child) == State.RUNNING_INDEX) {
  180                       log.trace("Stopping child: child={}", child);
  181                       kernel.stopGBean(child);
  182                       log.trace("Stopped child: child={}", child);
  183                   }
  184               } catch (Exception ignore) {
  185                   // not a big deal... did my best
  186               }
  187           }
  188   
  189           attemptFullStop();
  190       }
  191   
  192       /**
  193        * Moves this MBean to the FAILED state.  There are no calls to dependent children, but they will be notified
  194        * using standard J2EE management notification.
  195        * <p/>
  196        * Note:  This method can not be call while the current thread holds a syncronized lock on this MBean,
  197        * because this method sends JMX notifications.  Sending a general notification from a synchronized block
  198        * is a bad idea and therefore not allowed.
  199        */
  200       final void fail() {
  201           assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";
  202   
  203           synchronized (this) {
  204               State state = getStateInstance();
  205               if (state == State.STOPPED || state == State.FAILED) {
  206                   return;
  207               }
  208           }
  209   
  210           try {
  211               if (gbeanInstance.destroyInstance(false)) {
  212                   // instance is not ready to destroyed... this is because another thread has
  213                   // already killed the gbean.
  214                   return;
  215               }
  216           } catch (Throwable e) {
  217               gbeanInstance.setStateReason(e.getMessage());
  218               log.warn("Problem in doFail", e);
  219           }
  220           setStateInstance(State.FAILED);
  221           lifecycleBroadcaster.fireFailedEvent();
  222       }
  223   
  224       /**
  225        * Attempts to bring the component into {@link org.apache.geronimo.kernel.management.State#RUNNING} state. If an Exception occurs while
  226        * starting the component, the component will be failed.
  227        * <p/>
  228        * <p/>
  229        * Note: Do not call this from within a synchronized block as it makes may send a JMX notification
  230        */
  231       void attemptFullStart() {
  232           assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";
  233   
  234           synchronized (this) {
  235               // if we are still trying to start and can start now... start
  236               if (getStateInstance() != State.STARTING) {
  237                   return;
  238               }
  239   
  240               // check if all of the gbeans we depend on are running
  241               Set parents = dependencyManager.getParents(abstractName);
  242               for (Iterator i = parents.iterator(); i.hasNext();) {
  243                   AbstractName parent = (AbstractName) i.next();
  244                   if (!kernel.isLoaded(parent)) {
  245                       log.trace("Cannot run because parent is not registered: parent={}", parent);
  246                       return;
  247                   }
  248                   try {
  249                       log.trace("Checking if parent is running: parent={}", parent);
  250                       if (kernel.getGBeanState(parent) != State.RUNNING_INDEX) {
  251                           log.trace("Cannot run because parent is not running: parent={}", parent);
  252                           return;
  253                       }
  254                       log.trace("Parent is running: parent={}", parent);
  255                   } catch (GBeanNotFoundException e) {
  256                       // depended on instance was removed bewteen the register check and the invoke
  257                       log.trace("Cannot run because parent is not registered: parent={}", parent);
  258                       return;
  259                   } catch (Exception e) {
  260                       // problem getting the attribute, parent has most likely failed
  261                       log.trace("Cannot run because an error occurred while checking if parent is running: parent={}", parent);
  262                       return;
  263                   }
  264               }
  265           }
  266   
  267           try {
  268               // try to create the instance
  269               if (!gbeanInstance.createInstance()) {
  270                   // instance is not ready to start... this is normally caused by references
  271                   // not being available, but could be because someone already started the gbean.
  272                   // in another thread.  The reference will log a debug message about why
  273                   // it could not start
  274                   return;
  275               }
  276           } catch (Throwable t) {
  277               // oops there was a problem and the gbean failed
  278               log.error("Error while starting; GBean is now in the FAILED state: abstractName=\"" + abstractName + "\"", t);
  279               setStateInstance(State.FAILED);
  280               lifecycleBroadcaster.fireFailedEvent();
  281   
  282               if (t instanceof Exception) {
  283                   // ignore - we only rethrow errors
  284                   gbeanInstance.setStateReason(t.getMessage());
  285                   return;
  286               } else if (t instanceof Error) {
  287                   throw (Error) t;
  288               } else {
  289                   throw new Error(t);
  290               }
  291           }
  292   
  293           // started successfully... notify everyone else
  294           setStateInstance(State.RUNNING);
  295           lifecycleBroadcaster.fireRunningEvent();
  296       }
  297   
  298       /**
  299        * Attempt to bring the component into the fully stopped state.
  300        * If an exception occurs while stopping the component, the component will be failed.
  301        * <p/>
  302        * <p/>
  303        * Note: Do not call this from within a synchronized block as it may send a JMX notification
  304        */
  305       void attemptFullStop() {
  306           assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";
  307   
  308           // check if we are able to stop
  309           synchronized (this) {
  310               // if we are still trying to stop...
  311               if (getStateInstance() != State.STOPPING) {
  312                   return;
  313               }
  314   
  315               // check if all of the mbeans depending on us are stopped
  316               Set children = dependencyManager.getChildren(abstractName);
  317               for (Iterator i = children.iterator(); i.hasNext();) {
  318                   AbstractName child = (AbstractName) i.next();
  319                   if (kernel.isLoaded(child)) {
  320                       try {
  321                           log.trace("Checking if child is stopped: child={}", child);
  322                           int state = kernel.getGBeanState(child);
  323                           if (state == State.RUNNING_INDEX) {
  324                               log.trace("Cannot stop because child is still running: child={}", child);
  325                               return;
  326                           }
  327                       } catch (GBeanNotFoundException e) {
  328                           // depended on instance was removed between the register check and the invoke
  329                       } catch (Exception e) {
  330                           // problem getting the attribute, depended on bean has most likely failed
  331                           log.trace("Cannot run because an error occurred while checking if child is stopped: child={}", child);
  332                           return;
  333                       }
  334                   }
  335               }
  336           }
  337   
  338           // all is clear to stop... try to stop
  339           try {
  340               if (!gbeanInstance.destroyInstance(true)) {
  341                   // instance is not ready to stop... this is because another thread has
  342                   // already stopped the gbean.
  343                   return;
  344               }
  345           } catch (Throwable t) {
  346               log.error("Error while stopping; GBean is now in the FAILED state: abstractName=\"" + abstractName + "\"", t);
  347               setStateInstance(State.FAILED);
  348               lifecycleBroadcaster.fireFailedEvent();
  349   
  350               if (t instanceof Exception) {
  351                   // ignore - we only rethrow errors
  352                   gbeanInstance.setStateReason(t.getMessage());
  353                   return;
  354               } else if (t instanceof Error) {
  355                   throw (Error) t;
  356               } else {
  357                   throw new Error(t);
  358               }
  359           }
  360   
  361           // we successfully stopped, notify everyone else
  362           setStateInstance(State.STOPPED);
  363           lifecycleBroadcaster.fireStoppedEvent();
  364       }
  365   
  366       public int getState() {
  367           return state.toInt();
  368       }
  369   
  370       public final State getStateInstance() {
  371           return state;
  372       }
  373   
  374       /**
  375        * Set the Component state.
  376        *
  377        * @param newState the target state to transition
  378        * @throws IllegalStateException Thrown if the transition is not supported by the J2EE Management lifecycle.
  379        */
  380       private synchronized void setStateInstance(State newState) throws IllegalStateException {
  381           switch (state.toInt()) {
  382               case State.STOPPED_INDEX:
  383                   switch (newState.toInt()) {
  384                       case State.STARTING_INDEX:
  385                           break;
  386                       case State.STOPPED_INDEX:
  387                       case State.RUNNING_INDEX:
  388                       case State.STOPPING_INDEX:
  389                       case State.FAILED_INDEX:
  390                           throw new IllegalStateException("Cannot transition to " + newState + " state from " + state);
  391                   }
  392                   break;
  393   
  394               case State.STARTING_INDEX:
  395                   switch (newState.toInt()) {
  396                       case State.RUNNING_INDEX:
  397                       case State.FAILED_INDEX:
  398                       case State.STOPPING_INDEX:
  399                           break;
  400                       case State.STOPPED_INDEX:
  401                       case State.STARTING_INDEX:
  402                           throw new IllegalStateException("Cannot transition to " + newState + " state from " + state);
  403                   }
  404                   break;
  405   
  406               case State.RUNNING_INDEX:
  407                   switch (newState.toInt()) {
  408                       case State.STOPPING_INDEX:
  409                       case State.FAILED_INDEX:
  410                           break;
  411                       case State.STOPPED_INDEX:
  412                       case State.STARTING_INDEX:
  413                       case State.RUNNING_INDEX:
  414                           throw new IllegalStateException("Cannot transition to " + newState + " state from " + state);
  415                   }
  416                   break;
  417   
  418               case State.STOPPING_INDEX:
  419                   switch (newState.toInt()) {
  420                       case State.STOPPED_INDEX:
  421                       case State.FAILED_INDEX:
  422                           break;
  423                       case State.STARTING_INDEX:
  424                       case State.RUNNING_INDEX:
  425                       case State.STOPPING_INDEX:
  426                           throw new IllegalStateException("Cannot transition to " + newState + " state from " + state);
  427                   }
  428                   break;
  429   
  430               case State.FAILED_INDEX:
  431                   switch (newState.toInt()) {
  432                       case State.STARTING_INDEX:
  433                       case State.STOPPING_INDEX:
  434                           break;
  435                       case State.RUNNING_INDEX:
  436                       case State.STOPPED_INDEX:
  437                       case State.FAILED_INDEX:
  438                           throw new IllegalStateException("Cannot transition to " + newState + " state from " + state);
  439                   }
  440                   break;
  441           }
  442           
  443           log.debug("{} State changed from {} to {}", new Object[] { toString(), state, newState });
  444           
  445           state = newState;
  446       }
  447   
  448       public String toString() {
  449           return "GBeanInstanceState for: " + abstractName;
  450       }
  451   
  452   }

Home » geronimo-2.2-source-release » org.apache.geronimo.gbean.runtime » [javadoc | source]