Package org.apache.geronimo.gbean.runtime

Source Code of org.apache.geronimo.gbean.runtime.GBeanInstanceState

/**
*
* Copyright 2004 The Apache Software Foundation
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
package org.apache.geronimo.gbean.runtime;

import java.util.Iterator;
import java.util.Set;
import javax.management.ObjectName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geronimo.kernel.DependencyManager;
import org.apache.geronimo.kernel.GBeanNotFoundException;
import org.apache.geronimo.kernel.Kernel;
import org.apache.geronimo.kernel.lifecycle.LifecycleAdapter;
import org.apache.geronimo.kernel.lifecycle.LifecycleListener;
import org.apache.geronimo.kernel.management.State;

/**
* @version $Rev$ $Date$
*/
public class GBeanInstanceState {
    private static final Log log = LogFactory.getLog(GBeanInstanceState.class);

    /**
     * The GBeanInstance in which this server is registered.
     */
    private final GBeanInstance gbeanInstance;

    /**
     * The kernel in which this server is registered.
     */
    private final Kernel kernel;

    /**
     * The unique name of this service.
     */
    private final ObjectName objectName;

    /**
     * The dependency manager
     */
    private final DependencyManager dependencyManager;

    /**
     * The broadcaster of lifecycle events
     */
    private final LifecycleBroadcaster lifecycleBroadcaster;

    /**
     * The listener for the of the object blocking the start of this gbean.
     * When the blocker dies we attempt to start.
     */
    private LifecycleListener blockerListener;

    // 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;

    GBeanInstanceState(ObjectName objectName, Kernel kernel, DependencyManager dependencyManager, GBeanInstance gbeanInstance, LifecycleBroadcaster lifecycleBroadcaster) {
        this.objectName = objectName;
        this.kernel = kernel;
        this.dependencyManager = dependencyManager;
        this.gbeanInstance = gbeanInstance;
        this.lifecycleBroadcaster = lifecycleBroadcaster;
    }

    /**
     * Moves this MBean to the {@link org.apache.geronimo.kernel.management.State#STARTING} state and then attempts to move this MBean immediately
     * to the {@link org.apache.geronimo.kernel.management.State#RUNNING} state.
     * <p/>
     * Note:  This method cannot be called while the current thread holds a synchronized 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.
     */
    public final void start() {
        assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";

        // Move to the starting state
        State originalState;
        synchronized (this) {
            originalState = getStateInstance();
            if (originalState == State.RUNNING) {
                return;
            }
            // only try to change states if we are not already starting
            if (originalState != State.STARTING) {
                setStateInstance(State.STARTING);
            }
        }

        // only fire a notification if we are not already starting
        if (originalState != State.STARTING) {
            lifecycleBroadcaster.fireStartingEvent();
        }

        attemptFullStart();
    }

    /**
     * Starts this MBean and then attempts to start all of its start dependent children.
     * <p/>
     * Note:  This method cannot be call while the current thread holds a synchronized 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.
     */
    public final void startRecursive() {
        assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized 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 = dependencyManager.getChildren(objectName);
        for (Iterator iterator = dependents.iterator(); iterator.hasNext();) {
            ObjectName dependent = (ObjectName) iterator.next();
            try {
                if (kernel.isGBeanEnabled(dependent)) {
                    kernel.startRecursiveGBean(dependent);
                }
            } catch (GBeanNotFoundException e) {
                // this is ok the gbean died before we could start it
                continue;
            } catch (Exception e) {
                // the is something wrong with this gbean... skip it
                continue;
            }
        }
    }

    /**
     * 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.
     * <p/>
     * 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.
     */
    public final void stop() {
        assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";

        // move to the stopping state
        State originalState;
        synchronized (this) {
            originalState = getStateInstance();
            if (originalState == State.STOPPED) {
                return;
            }

            // only try to change states if we are not already stopping
            if (originalState != State.STOPPING) {
                setStateInstance(State.STOPPING);
            }
        }

        // only fire a notification if we are not already stopping
        if (originalState != State.STOPPING) {
            lifecycleBroadcaster.fireStoppingEvent();
        }

        // Don't try to stop dependents from within a synchronized block... this should reduce deadlocks

        // stop all of my dependent objects
        Set dependents = dependencyManager.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 (kernel.getGBeanState(child) == State.RUNNING_INDEX) {
                    log.trace("Stopping child: child=" + child);
                    kernel.stopGBean(child);
                    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.
     * <p/>
     * 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 synchronized lock on this";

        synchronized (this) {
            State state = getStateInstance();
            if (state == State.STOPPED || state == State.FAILED) {
                return;
            }
        }

        try {
            if (gbeanInstance.destroyInstance(false)) {
                // instance is not ready to destroyed... this is because another thread has
                // already killed the gbean.
                return;
            }
        } catch (Throwable e) {
            log.warn("Problem in doFail", e);
        }
        setStateInstance(State.FAILED);
        lifecycleBroadcaster.fireFailedEvent();
    }

    /**
     * Attempts to bring the component into {@link org.apache.geronimo.kernel.management.State#RUNNING} state. If an Exception occurs while
     * starting the component, the component will be failed.
     * <p/>
     * <p/>
     * Note: Do not call this from within a synchronized block as it makes may send a JMX notification
     */
    void attemptFullStart() {
        assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";

        synchronized (this) {
            // if we are still trying to start and can start now... start
            if (getStateInstance() != State.STARTING) {
                return;
            }

            if (blockerListener != null) {
                log.trace("Cannot run because gbean is still being blocked");
                return;
            }

            // check if an gbean is blocking us from starting
            final ObjectName blocker = dependencyManager.checkBlocker(objectName);
            if (blocker != null) {
                blockerListener = new LifecycleAdapter() {

                    public void stopped(ObjectName objectName) {
                        checkBlocker(objectName);
                    }

                    public void failed(ObjectName objectName) {
                        checkBlocker(objectName);
                    }

                    public void unloaded(ObjectName objectName) {
                        checkBlocker(objectName);
                    }

                    private void checkBlocker(ObjectName objectName) {
                        synchronized (GBeanInstanceState.this) {
                            if (!objectName.equals(blocker)) {
                                // it did not start so just exit this method
                                return;
                            }

                            // it started, so remove the blocker and attempt a full start
                            kernel.getLifecycleMonitor().removeLifecycleListener(this);
                            GBeanInstanceState.this.blockerListener = null;
                        }

                        try {
                            attemptFullStart();
                        } catch (Exception e) {
                            log.warn("A problem occured while attempting to start", e);
                        }
                    }
                };
                // register the listener and return
                kernel.getLifecycleMonitor().addLifecycleListener(blockerListener, blocker);
                return;
            }

            // check if all of the gbeans we depend on are running
            Set parents = dependencyManager.getParents(objectName);
            for (Iterator i = parents.iterator(); i.hasNext();) {
                ObjectName parent = (ObjectName) i.next();
                if (!kernel.isLoaded(parent)) {
                    log.trace("Cannot run because parent is not registered: parent=" + parent);
                    return;
                }
                try {
                    log.trace("Checking if parent is running: parent=" + parent);
                    if (kernel.getGBeanState(parent) != State.RUNNING_INDEX) {
                        log.trace("Cannot run because parent is not running: parent=" + parent);
                        return;
                    }
                    log.trace("Parent is running: parent=" + parent);
                } catch (GBeanNotFoundException 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;
                }
            }
        }

        try {
            // try to create the instance
            if (!gbeanInstance.createInstance()) {
                // instance is not ready to start... this is normally caused by references
                // not being available, but could be because someone already started the gbean.
                // in another thread.  The reference will log a debug message about why
                // it could not start
                return;
            }
        } catch (Throwable t) {
            // oops there was a problem and the gbean failed
            log.error("Error while starting; GBean is now in the FAILED state: objectName=\"" + objectName + "\"", t);
            setStateInstance(State.FAILED);
            lifecycleBroadcaster.fireFailedEvent();

            if (t instanceof Exception) {
                // ignore - we only rethrow errors
                return;
            } else if (t instanceof Error) {
                throw (Error) t;
            } else {
                throw new Error(t);
            }
        }

        // started successfully... notify everyone else
        setStateInstance(State.RUNNING);
        lifecycleBroadcaster.fireRunningEvent();
    }

    /**
     * Attempt to bring the component into the fully stopped state.
     * If an exception occurs while stopping the component, the component will be failed.
     * <p/>
     * <p/>
     * Note: Do not call this from within a synchronized block as it may send a JMX notification
     */
    void attemptFullStop() {
        assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";

        // check if we are able to stop
        synchronized (this) {
            // if we are still trying to stop...
            if (getStateInstance() != State.STOPPING) {
                return;
            }

            // check if all of the mbeans depending on us are stopped
            Set children = dependencyManager.getChildren(objectName);
            for (Iterator i = children.iterator(); i.hasNext();) {
                ObjectName child = (ObjectName) i.next();
                if (kernel.isLoaded(child)) {
                    try {
                        log.trace("Checking if child is stopped: child=" + child);
                        int state = kernel.getGBeanState(child);
                        if (state == State.RUNNING_INDEX) {
                            log.trace("Cannot stop because child is still running: child=" + child);
                            return;
                        }
                    } catch (GBeanNotFoundException 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;
                    }
                }
            }
        }

        // all is clear to stop... try to stop
        try {
            if (!gbeanInstance.destroyInstance(true)) {
                // instance is not ready to stop... this is because another thread has
                // already stopped the gbean.
                return;
            }
        } catch (Throwable t) {
            log.error("Error while stopping; GBean is now in the FAILED state: objectName=\"" + objectName + "\"", t);
            setStateInstance(State.FAILED);
            lifecycleBroadcaster.fireFailedEvent();

            if (t instanceof Exception) {
                // ignore - we only rethrow errors
                return;
            } else if (t instanceof Error) {
                throw (Error) t;
            } else {
                throw new Error(t);
            }
        }

        // we successfully stopped, notify everyone else
        setStateInstance(State.STOPPED);
        lifecycleBroadcaster.fireStoppedEvent();
    }

    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("Cannot 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("Cannot 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("Cannot 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("Cannot 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("Cannot transition to " + newState + " state from " + state);
                }
                break;
        }
        log.debug(toString() + " State changed from " + state + " to " + newState);
        state = newState;
    }

    public String toString() {
        return "GBeanInstanceState for: " + objectName;
    }

}
TOP

Related Classes of org.apache.geronimo.gbean.runtime.GBeanInstanceState

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.