Package org.globus.workspace.service.impls

Source Code of org.globus.workspace.service.impls.StateTransition

/*
* Copyright 1999-2008 University of Chicago
*
* 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.globus.workspace.service.impls;

import edu.emory.mathcs.backport.java.util.concurrent.locks.Lock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.globus.workspace.Lager;
import org.globus.workspace.LockManager;
import org.globus.workspace.ProgrammingError;
import org.globus.workspace.WorkspaceConstants;
import org.globus.workspace.persistence.DataConvert;
import org.globus.workspace.service.impls.async.RequestDispatch;
import org.globus.workspace.service.impls.async.RequestFactory;
import org.globus.workspace.service.impls.async.TaskNotImplementedException;
import org.globus.workspace.service.impls.async.WorkspaceRequest;
import org.globus.workspace.service.impls.async.WorkspaceRequestContext;
import org.globus.workspace.service.binding.GlobalPolicies;
import org.globus.workspace.TempLocator;
import org.nimbustools.api.services.rm.ManageException;

/**
* Evaluate current and target states and adds tasks for the
* WorkspaceRequest execution thread pool to execute.  Caller
* is responsible for locking appropriately.
*/
public class StateTransition implements WorkspaceConstants {

    // -------------------------------------------------------------------------
    // STATIC VARIABLES
    // -------------------------------------------------------------------------

    private static final Log logger =
                        LogFactory.getLog(StateTransition.class.getName());


    // -------------------------------------------------------------------------
    // INSTANCE VARIABLES
    // -------------------------------------------------------------------------

    protected final RequestFactory reqFactory;
    protected final DataConvert dataConvert;
    protected final GlobalPolicies globals;
    protected final TempLocator locator;
    protected final Lager lager;

    protected final boolean trace;
    protected final boolean event;

   
    // -------------------------------------------------------------------------
    // CONSTRUCTOR
    // -------------------------------------------------------------------------

    public StateTransition(RequestFactory requestFactory,
                           DataConvert dataConvertImpl,
                           GlobalPolicies globalPolicies,
                           TempLocator locatorImpl,
                           Lager lagerImpl) {

        if (requestFactory == null) {
            throw new IllegalArgumentException("requestFactory may not be null");
        }
        this.reqFactory = requestFactory;

        if (dataConvertImpl == null) {
            throw new IllegalArgumentException("dataConvertImpl may not be null");
        }
        this.dataConvert = dataConvertImpl;

        if (globalPolicies == null) {
            throw new IllegalArgumentException("globalPolicies may not be null");
        }
        this.globals = globalPolicies;

        if (locatorImpl == null) {
            throw new IllegalArgumentException("locatorImpl may not be null");
        }
        this.locator = locatorImpl;

        if (lagerImpl == null) {
            throw new IllegalArgumentException("lagerImpl may not be null");
        }
        this.trace = lagerImpl.traceLog;
        this.event = lagerImpl.eventLog;
        this.lager = lagerImpl;
    }


    // -------------------------------------------------------------------------
    // ENTRY
    // -------------------------------------------------------------------------

    /**
     * Evaluate current and target states and adds tasks for the
     * WorkspaceRequest execution thread pool to execute.  Caller
     * is responsible for locking access (in StatefulResourceImpl,
     * setState and setTargetState lock based on resource id for
     * example).
     *
     * Only public method in class.
     *
     * @param resource resource to inspect
     * @throws ManageException problem
     */
    public void run(final StatefulResourceImpl resource)
            throws ManageException {

        if (resource == null) {
            throw new IllegalArgumentException("resource is null");
        }
       
        final int id = resource.getID();
        final String idStr = Lager.id(id);

        if (this.trace) {
            logger.trace(idStr + ": request state transition");
        }

        final int state = resource.getState();
        final int targetState = resource.getTargetState();

        _run(resource,
             state,
             targetState,
             id,
             idStr,
             this.dataConvert.stateName(state),
             this.dataConvert.stateName(targetState));

        if (this.trace) {
            logger.trace(idStr + ": state transition over");
        }
    }

    /*
     * Runs a hardcoded "chain of command" pattern (with jumps).
     * Originally used Apache Commons chain library, but this works well.
     */
    private boolean _run(final StatefulResourceImpl res,
                         final int cur,
                         final int tar,
                         final int id,
                         final String idStr,
                         final String curStr,
                         final String tarStr)

            throws ManageException {

        if (cur == tar) {
            return false;
        }

        return corrupted(    cur,tar,   idStr,curStr,tarStr) ||
                  remove(res,cur,tar,id,idStr,curStr,tarStr) ||
                 stageIn(res,cur,tar,id,idStr,curStr,tarStr) ||
               propagate(res,cur,tar,id,idStr,curStr,tarStr) ||
                   start(res,cur,tar,id,idStr,curStr,tarStr) ||
                shutdown(res,cur,tar,id,idStr,curStr,tarStr) ||
       readyForTransport(res,cur,tar,id,idStr,curStr,tarStr) ||
                stageOut(res,cur,tar,id,idStr,curStr,tarStr);
    }

    // allows a curstate jump, see stageIn handler
    private boolean _runProp(final StatefulResourceImpl res,
                             final int cur,
                             final int tar,
                             final int id,
                             final String idStr,
                             final String curStr,
                             final String tarStr)

            throws ManageException {

        return     propagate(res,cur,tar,id,idStr,curStr,tarStr) ||
                       start(res,cur,tar,id,idStr,curStr,tarStr) ||
                    shutdown(res,cur,tar,id,idStr,curStr,tarStr) ||
           readyForTransport(res,cur,tar,id,idStr,curStr,tarStr) ||
                    stageOut(res,cur,tar,id,idStr,curStr,tarStr);
    }

    // allows a curstate jump, see propagate handler
    private boolean _runStart(final StatefulResourceImpl res,
                              final int cur,
                              final int tar,
                              final int id,
                              final String idStr,
                              final String curStr,
                              final String tarStr)

            throws ManageException {

        return         start(res,cur,tar,id,idStr,curStr,tarStr) ||
                    shutdown(res,cur,tar,id,idStr,curStr,tarStr) ||
           readyForTransport(res,cur,tar,id,idStr,curStr,tarStr) ||
                    stageOut(res,cur,tar,id,idStr,curStr,tarStr);
    }


    /* Callers of StateTransition.run make these guarantees */

    /* The only possible current state values, ordered, no gaps: */

    /* STATE_FIRST_LEGAL --> STATE_LAST_LEGAL

    /* STATE_UNSTAGED
       STATE_STAGING_IN
       STATE_UNPROPAGATED
       STATE_PROPAGATING
       STATE_PROPAGATING_TO_START
       STATE_PROPAGATING_TO_PAUSE

       STATE_PROPAGATED
       STATE_STARTING
       STATE_STARTED
       STATE_SERIALIZING
       STATE_SERIALIZED
       STATE_PAUSING
       STATE_PAUSED
       STATE_REBOOT
       STATE_SHUTTING_DOWN

       STATE_READYING_FOR_TRANSPORT
       STATE_READY_FOR_TRANSPORT
       STATE_STAGING_OUT
       STATE_STAGED_OUT

       // see top of remove handler for note about the cancelling states:
       STATE_CANCELLING_STAGING_IN
       STATE_CANCELLING_UNPROPAGATED
       STATE_CANCELLING_PROPAGATING
       STATE_CANCELLING_PROPAGATING_TO_START
       STATE_CANCELLING_PROPAGATING_TO_PAUSE
       STATE_CANCELLING_AT_VMM
       STATE_CANCELLING_READYING_FOR_TRANSPORT
       STATE_CANCELLING_READY_FOR_TRANSPORT
       STATE_CANCELLING_STAGING_OUT

       STATE_DESTROYING
       STATE_CORRUPTED_GENERIC */

    /* (and then from STATE_CORRUPTED to STATE_LAST_LEGAL) */



    /* The only possible target state values, ordered: */

    /* STATE_UNSTAGED
       STATE_UNPROPAGATED
       STATE_PROPAGATED
       STATE_STARTED
       STATE_SERIALIZED
       STATE_PAUSED
       STATE_REBOOT
       STATE_READY_FOR_TRANSPORT
       STATE_STAGED_OUT
       STATE_DESTROYING
       STATE_CORRUPTED_GENERIC */

    /* (and then from STATE_CORRUPTED to STATE_LAST_LEGAL) */



    /* ********* */
    /* corrupted */
    /* ********* */

    private boolean corrupted(final int current,
                              final int target,
                              final String idStr,
                              final String curStr,
                              final String tarStr) {

        if (current < STATE_CORRUPTED_GENERIC) {
            return false;
        }

        if (target == STATE_DESTROYING) {
            return false;
        }

        if (this.trace) {
            logger.trace("\n\n   ***** ST--corrupted: processing " +
                    idStr + ", current = " + curStr + ", target = " +
                    tarStr + "\n");
        }

        // Currently can not move from corrupted-* to anything except
        // removal.

        if (current == STATE_CORRUPTED_GENERIC) {
            logger.warn("Workspace is corrupted: can not change state " +
                "anymore unless workspace is going to be destroyed");
        } else {
            logger.warn("Workspace was corrupted (when moving to state " +
                        this.dataConvert.stateName(current - STATE_CORRUPTED) +
                        "): can not change state anymore unless workspace " +
                        "is going to be destroyed");
        }
        return true;
    }



    /* ****** */
    /* remove */
    /* ****** */

    // Can block waiting for work to complete (the only potentially blocking
    // command in chain)
    private boolean remove(final StatefulResourceImpl resource,
                           final int current,
                           final int target,
                           final int id,
                           final String idStr,
                           final String curStr,
                           final String tarStr)

            throws ManageException {

        final LockManager lockManager = resource.getLockManager();
        final Lock lock = lockManager.getLock(id);
        final Lock destroy_lock = lockManager.getLock("destroy_" + id);

        if (current >= STATE_CANCELLING_STAGING_IN
                        && current <= STATE_CANCELLING_STAGING_OUT) {


            // since setState(STATE_DESTROYING) is called before any
            // invocation to a CANCELLING command, this should never be
            // the case (because setState doesn't call StateTransition.run()
            // if target has already been set to DESTROYING  (target =
            // STATE_DESTROYING is only the case here in this handler when
            // setTargetState first changes it to DESTROYING

            logger.fatal("programming error, stopping state transition");
            // stop processing
            return true;


            // TODO: In the future an action should be allowed to be
            //       cancelled without its target being set to DESTROYING
            //       first.  When that happens, this assumption above will
            //       need to change (add another handler before this remove
            //       handler to handle resources with a cancelling state or
            //       cancel target state.  A smart scheduler could for
            //       example just want cancelPropagate to execute because
            //       it got a priority request and needs the network, i.e.,
            //       postpone functionality.
        }


        if (target != STATE_DESTROYING) {
            return false;
        }

        if (this.trace) {
            logger.trace("\n\n   ***** ST--remove: processing " +
                    idStr + ", current = " + curStr + ", target = " +
                    tarStr + "\n");
        }

        final WorkspaceRequestContext requestContext =
                new WorkspaceRequestContext(id, resource.getName(),
                                            this.locator, this.lager);
       
        requestContext.setGroupID(resource.getGroupId());
        requestContext.setGroupSize(resource.getGroupSize());
        if (resource.isLastInGroup()) {
            requestContext.setLastInGroup(true);
            resource.setLastInGroup(false);
        }
        requestContext.setPartOfGroupRequest(resource.isPartOfGroupRequest());

        WorkspaceRequest req = null;
        int nextstate = STATE_INVALID;

        switch (current) {
            case STATE_DESTROY_SUCCEEDED:
                break;
            case STATE_DESTROY_FAILED:
                req = reqFactory.cancelAllAtVMM();
                nextstate = STATE_CANCELLING_AT_VMM;
                requestContext.setVm(resource.getVM());
                break;
            case STATE_STAGING_IN: // now unused
            case STATE_UNPROPAGATED:
                req = reqFactory.cancelUnpropagated();
                nextstate = STATE_CANCELLING_UNPROPAGATED;
                requestContext.setVm(resource.getVM());
                break;
            case STATE_PROPAGATING:
                req = reqFactory.cancelPropagating();
                nextstate = STATE_CANCELLING_PROPAGATING;
                requestContext.setVm(resource.getVM());
                break;
            case STATE_PROPAGATING_TO_START:
                req = reqFactory.cancelPropagatingToStart();
                nextstate = STATE_CANCELLING_PROPAGATING_TO_START;
                requestContext.setVm(resource.getVM());
                break;
            case STATE_PROPAGATING_TO_PAUSE:   
                req = reqFactory.cancelPropagatingToPause();
                nextstate = STATE_CANCELLING_PROPAGATING_TO_PAUSE;
                requestContext.setVm(resource.getVM());
                break;
            case STATE_PROPAGATED:
            case STATE_STARTING:
            case STATE_STARTED:
            case STATE_SERIALIZING:
            case STATE_SERIALIZED:
            case STATE_PAUSING:
            case STATE_PAUSED:
            case STATE_SHUTTING_DOWN:
                req = reqFactory.cancelAllAtVMM();
                nextstate = STATE_CANCELLING_AT_VMM;
                requestContext.setVm(resource.getVM());
                break;
            case STATE_READYING_FOR_TRANSPORT:
                req = reqFactory.cancelReadyingForTransport();
                nextstate = STATE_CANCELLING_READYING_FOR_TRANSPORT;
                requestContext.setVm(resource.getVM());
                break;
            case STATE_READY_FOR_TRANSPORT:
                req = reqFactory.cancelReadyForTransport();
                nextstate = STATE_CANCELLING_READY_FOR_TRANSPORT;
                requestContext.setVm(resource.getVM());
                break;
            case STATE_STAGING_OUT: // now unused
            default:
        }

        if (current >= STATE_CORRUPTED) {

            // currently we will try to do something about a workspace
            // corrupted at times that may have left image files or state
            // at the backend node, we do not handle other corrupt-*
            // situations now.

            final int oldstate = current - STATE_CORRUPTED;

            if (oldstate >= STATE_PROPAGATING
                    && oldstate < STATE_READYING_FOR_TRANSPORT) {

                req = reqFactory.cancelAllAtVMM();
                nextstate = STATE_CANCELLING_AT_VMM;
                requestContext.setVm(resource.getVM());

            } else {

                // candidate for admin log/trigger of severe issues

                final String err = "Destroying a corrupted " +
                            "resource in state '" + curStr +
                            "'. That state does not indicate files or " +
                            "cruft may be on VMM node, not doing anything" +
                            " (but there may be stray staged files off-VMM).";

                if (this.event) {
                    logger.info(Lager.ev(id) + err);
                } else if (this.trace) {
                    logger.trace(idStr + err);
                }
            }
        }

        if (req != null) {

            resource.setStateUnderLock(nextstate, null);
            requestContext.setNotify(STATE_DESTROYING);
            req.setRequestContext(requestContext);

            if (this.trace) {
                logger.trace("\n\n   ***** ST--remove: " + idStr
                                    + ", executing " + req.toString() + "\n");
            }

            try {
                destroy_lock.lockInterruptibly();
            } catch (InterruptedException e) {
                throw new ManageException(e.getMessage(), e);
            }

            lock.unlock();

            // TODO: add a timeout
            try {
                req.execute(); // could block
            } catch (Throwable t) {
                // candidate for admin log/trigger of severe issues
                logger.error("",t);
            } finally {
              try {
                  lock.lockInterruptibly();
                  destroy_lock.unlock();
              } catch (InterruptedException e) {
                  throw new ManageException(e.getMessage(), e);
              }
            }

            if (this.trace) {
                logger.trace("\n\n   ***** ST--remove: " + idStr
                              + ", done executing " + req.toString() + "\n");
            }

        } else {
            resource.setStateUnderLock(STATE_DESTROY_SUCCEEDED, null);
            if (this.trace) {
                logger.trace("\n\n   ***** ST--remove: " + idStr
                        + ", nothing to do\n");
            }
        }

        return true;
    }


    /* ******* */
    /* stageIn */
    /* ******* */

    private boolean stageIn(final StatefulResourceImpl resource,
                            final int current,
                            final int target,
                            final int id,
                            final String idStr,
                            final String curStr,
                            final String tarStr)

            throws ManageException {

        if (current == STATE_STAGING_IN) {
            // can happen if user calls operations while staging is still
            // happening, just cut out and do nothing
            if (this.trace) {
                logger.debug("current is " + curStr + ", nothing to do");
            }
            return true;
        }

        // target not applicable
        if (target < STATE_UNPROPAGATED) {
            return false;
        }

        // nothing to do
        if (current >= STATE_STAGING_IN) {
            return false;
        }

        if (this.trace) {
            logger.trace("\n\n   ***** ST--stageIn (now a no-op): processing " +
                    idStr + ", current = " + curStr + ", target = " +
                    tarStr + "\n");
        }

        resource.setStateUnderLock(STATE_UNPROPAGATED, null);
        _runProp(resource, STATE_UNPROPAGATED, target, id,
                 idStr, this.dataConvert.stateName(STATE_UNPROPAGATED), tarStr);
        return true;
    }


    /* ********* */
    /* propagate */
    /* ********* */

    private boolean propagate(final StatefulResourceImpl resource,
                              final int current,
                              int target,
                              final int id,
                              final String idStr,
                              final String curStr,
                              final String tarStr)

            throws ManageException {

        if (current == STATE_PROPAGATING ||
            current == STATE_PROPAGATING_TO_PAUSE ||
            current == STATE_PROPAGATING_TO_START) {
            // can happen if user calls operations while propagation is still
            // happening, just cut out and do nothing
            if (this.trace) {
                logger.debug("current state is " + curStr + ", nothing to do");
            }
            return true;
        }

        if (target < STATE_PROPAGATED) {
            return false;
        }

        // nothing to do
        if (current >= STATE_PROPAGATING) {
            return false;
        }

        if (this.trace) {
            logger.trace("\n\n   ***** ST--propagate: processing " +
                    idStr + ", current = " + curStr + ", target = " +
                    tarStr + "\n");
        }

        if (!resource.isPropagateRequired()) {
            resource.setStateUnderLock(STATE_PROPAGATED, null);
            _runStart(resource, STATE_PROPAGATED, target, id,
                      idStr, this.dataConvert.stateName(STATE_PROPAGATED), tarStr);
            return true;
        }

        //propagate is needed

        if (!this.globals.isPropagateEnabled()) {

            logger.error("should be unreachable, Binding should have " +
                    "rejected this creation request");

            int next = STATE_INVALID;
            if (target == STATE_PROPAGATED) {
                next = STATE_PROPAGATING;
            } else if (target == STATE_STARTED) {
                next = STATE_PROPAGATING_TO_START;
            } else if (target == STATE_PAUSED) {
                next = STATE_PROPAGATING_TO_PAUSE;
            }

            final String errMsg = "propagate functionality " +
                            "needed but it has been disabled";
            final ManageException wexc = new ManageException(errMsg);

            final int stateToSet;
            if (next == STATE_INVALID) {
                stateToSet = STATE_CORRUPTED_GENERIC;
            } else {
                stateToSet = STATE_CORRUPTED + next;
            }
            resource.setStateUnderLock(stateToSet, wexc);
            throw wexc;
        }

        WorkspaceRequest req;
        boolean fallback = false;
        final boolean propstartOK = resource.isPropagateStartOK();

        if (target == STATE_STARTED) {

            if (propstartOK) {
                req = this.reqFactory.propagateAndStart();

                if (req == null) {
                    if (this.trace) {
                        logger.trace("\n\n   ***** ST--propagate " + idStr +
                                ": could use propagateToStart, " +
                                            "but not implemented\n");
                    }
                    fallback = true;
                }

            } else {

                req = reqFactory.propagate();

                if (req == null) {

                    final String errMsg = "propagate functionality " +
                            "needed but it is not implemented";
                    final ManageException wexc =
                            new ManageException(errMsg);

                    resource.setStateUnderLock(
                                STATE_CORRUPTED + STATE_PROPAGATING, wexc);
                   
                    throw wexc;
                }

                // just change local var, not real resource target
                target = STATE_PROPAGATED;

                if (this.trace) {
                    logger.trace("\n\n   ***** ST--propagate " + idStr +
                    ": propagateToStart not OK for this resource, doing" +
                        " propagate-only\n");
                }

            }

        } else if (target == STATE_PAUSED) {

            if (propstartOK) {

                req = this.reqFactory.propagateAndPause();

                if (req == null) {
                    if (this.trace) {
                        logger.trace("\n\n   ***** ST--propagate " + idStr +
                       ": could use propagateToPause, but not implemented\n");
                    }
                    fallback = true;
                }

            } else {

                req = reqFactory.propagate();

                if (req == null) {

                    final String errMsg = "propagate functionality " +
                            "needed but it is not implemented";
                    final ManageException wexc =
                            new ManageException(errMsg);

                    resource.setStateUnderLock(
                                STATE_CORRUPTED + STATE_PROPAGATING, wexc);
                   
                    throw wexc;
                }

                // just change local var, not real resource target
                // todo: comment why
                target = STATE_PROPAGATED;

                if (this.trace) {

                    logger.trace("\n\n   ***** ST--propagate " + idStr +
                    ": propagateToPause not OK for this resource, doing" +
                        " propagate-only\n");
                }
            }

        } else if (target == STATE_PROPAGATED) {

            req = this.reqFactory.propagate();

            if (req == null) {

                final String errMsg = "propagate functionality " +
                            "needed but it is not implemented";
                final ManageException wexc =
                        new ManageException(errMsg);

                resource.setStateUnderLock(
                                STATE_CORRUPTED + STATE_PROPAGATING, wexc);

                throw wexc;
            }

        } else {

            // handlers before propagate handler should not
            // let this happen

            throw new ManageException("Current state is " +
                    this.dataConvert.stateName(current) + ", " +
                    "propagate is needed but not" +
                    " achieved, but target state is not " +
                    this.dataConvert.stateName(STATE_STARTED) + ", " +
                    this.dataConvert.stateName(STATE_PAUSED) + ", or" +
                    this.dataConvert.stateName(STATE_PROPAGATED) + ", it is " +
                    this.dataConvert.stateName(target));
        }

        if (fallback) {

            if (this.trace) {
                logger.trace("\n\n   ***** ST--propagate " + idStr +
                                        ": falling back to propagate-only\n");
            }

            req = reqFactory.propagate();

            if (req == null) {

                final String errMsg = "propagate functionality " +
                            "needed but it is not implemented";
                final ManageException wexc =
                        new ManageException(errMsg);

                resource.setStateUnderLock(
                                STATE_CORRUPTED + STATE_PROPAGATING, wexc);
               
                throw wexc;
            }

            // just change local var, not real resource target
            target = STATE_PROPAGATED;
        }


        final WorkspaceRequestContext requestContext =
                new WorkspaceRequestContext(id, resource.getName(),
                                            this.locator, this.lager);

        requestContext.setVm(resource.getVM());
        requestContext.setGroupID(resource.getGroupId());
        requestContext.setGroupSize(resource.getGroupSize());
        if (resource.isLastInGroup()) {
            requestContext.setLastInGroup(true);
            resource.setLastInGroup(false);
        }
        requestContext.setPartOfGroupRequest(resource.isPartOfGroupRequest());

        // req cannot be null here

        if (target == STATE_PROPAGATED) {

            requestContext.setNotify(STATE_PROPAGATED);
            req.setRequestContext(requestContext);

            resource.setStateUnderLock(STATE_PROPAGATING, null);

            if (this.trace) {
                logger.trace("\n\n   ***** ST--propagate " + idStr +
                        ": adding propagate request: " + req + "\n");
            }

            RequestDispatch.addRequest(req, id);
            return true;

        } else if (target == STATE_STARTED) {

            requestContext.setNotify(STATE_STARTED);
            req.setRequestContext(requestContext);

            resource.setStateUnderLock(STATE_PROPAGATING_TO_START, null);

            if (this.trace) {
                logger.trace("\n\n   ***** ST--propagate " + idStr +
                      ": adding propagate-to-start request: " + req + "\n");
            }

            RequestDispatch.addRequest(req, id);
            return true;

        } else {

            requestContext.setNotify(STATE_PAUSED);
            req.setRequestContext(requestContext);

            resource.setStateUnderLock(STATE_PROPAGATING_TO_PAUSE, null);

            if (this.trace) {
                logger.trace("\n\n   ***** ST--propagate " + idStr +
                        ": adding propagate-to-pause request: " + req + "\n");
            }

            RequestDispatch.addRequest(req, id);
            return true;
        }
    }


    /* ***** */
    /* start */
    /* ***** */

    private boolean start(final StatefulResourceImpl resource,
                          final int current,
                          final int target,
                          final int id,
                          final String idStr,
                          final String curStr,
                          final String tarStr)

            throws ManageException {

        if (target != STATE_STARTED
             && target != STATE_REBOOT
             && target != STATE_PAUSED) {
            return false;
        }

        if (target == STATE_PAUSED && current == STATE_STARTED) {
            return false; //shutdown will get this
        }

        if (current >= STATE_READYING_FOR_TRANSPORT) {
            // restriction could go away in the future
            throw new ManageException("illegal to move to " +
                    this.dataConvert.stateName(STATE_STARTED) + " from " +
                    this.dataConvert.stateName(current));
        }

        if (this.trace) {
            logger.trace("\n\n   ***** ST--start: processing " + idStr +
                           ", current = " + curStr + ", target = " +
                           tarStr + "\n");
        }

        // no implementation of these does not cause resource to move to
        // corrupted which is why TaskNotImplementedException is thrown

        boolean notifyPaused = false;

        final WorkspaceRequest req;

        if (target == STATE_REBOOT) {

            req = this.reqFactory.reboot();

            if (req == null) {
                throw new TaskNotImplementedException("reboot not implemented");
            }

            // would be cool to implement a 'reboot -> start-paused' option,
            // (there may be a conceivable application use), but for now we
            // expect reboot results in STATE_STARTED
            resource.setTargetStateUnderLock(STATE_STARTED);

            if (this.trace) {
                logger.trace("\n\n   ***** ST--start " + idStr +
                        ": adding reboot request: " + req + "\n");
            }

        } else if (current == STATE_PAUSED) {

            if (target == STATE_PAUSED) {
                throw new ProgrammingError("current and target are " +
                        "both paused");
            }

            req = reqFactory.unpause();

            if (req == null) {
                throw new TaskNotImplementedException(
                                        "unpause not implemented");
            }

            if (this.trace) {
                logger.trace("\n\n   ***** ST--start " + idStr +
                        ": adding unpause request: " + req + "\n");
            }

        } else if (current == STATE_PROPAGATED) {

            if (target == STATE_PAUSED) {
                req = reqFactory.startPaused();
                notifyPaused = true;
            } else {
                req = reqFactory.start();
            }

            if (req == null) {
                throw new TaskNotImplementedException(
                                        "start (create) not implemented");
            }

            if (this.trace) {
                logger.trace("\n\n   ***** ST--start " + idStr +
                        ": adding start (create) request: " + req + "\n");
            }

        } else if (current == STATE_SERIALIZED) {

            req = reqFactory.unserialize();

            if (req == null) {
                throw new TaskNotImplementedException(
                                        "unserialize not implemented");
            }

            if (this.trace) {
                logger.trace("\n\n   ***** ST--start " + idStr +
                        ": adding unserialize request: " + req + "\n");
            }

        } else {
            // If current is another state between STATE_PROPAGATED and
            // STATE_SHUTTING_DOWN, there is nothing to do but wait until
            // the notification comes in that STATE_PROPAGATED,
            // STATE_PAUSED, or STATE_SERIALIZED has been achieved.

            // If target is still STATE_STARTED at that point, StateTransition
            // will run again via WorkspaceResourceImpl.setState() and will
            // cause one of the three commands to be launched

            return true;
        }

        final WorkspaceRequestContext requestContext =
                new WorkspaceRequestContext(id, resource.getName(),
                                            this.locator, this.lager);

        requestContext.setVm(resource.getVM());
        if (notifyPaused) {
            requestContext.setNotify(STATE_PAUSED);
        } else {
            requestContext.setNotify(STATE_STARTED);
        }
        requestContext.setGroupID(resource.getGroupId());
        requestContext.setGroupSize(resource.getGroupSize());
        if (resource.isLastInGroup()) {
            requestContext.setLastInGroup(true);
            resource.setLastInGroup(false);
        }
        requestContext.setPartOfGroupRequest(resource.isPartOfGroupRequest());
        req.setRequestContext(requestContext);

        resource.setStateUnderLock(STATE_STARTING, null);
        RequestDispatch.addRequest(req, id);

        return true;
    }


    /* ******** */
    /* shutdown */
    /* ******** */

    private boolean shutdown(final StatefulResourceImpl resource,
                             final int current,
                             final int target,
                             final int id,
                             final String idStr,
                             final String curStr,
                             final String tarStr)

            throws ManageException {

        if (current != STATE_STARTING &&
            current != STATE_STARTED  &&
            current != STATE_PAUSING  &&
            current != STATE_PAUSED) {

            return false;
        }

        if (target != STATE_PROPAGATED &&
            target != STATE_SERIALIZED &&
            target != STATE_PAUSED &&
            target != STATE_READY_FOR_TRANSPORT &&
            target != STATE_STAGED_OUT) {

            return false;
        }

        if (this.trace) {
            logger.trace("\n\n   ***** ST--shutdown: processing " +
                    idStr + ", current = " + curStr + ", target = " +
                    tarStr + "\n");
        }

        final WorkspaceRequest req;

        final WorkspaceRequestContext requestContext =
                new WorkspaceRequestContext(id, resource.getName(),
                                            this.locator, this.lager);

        if (target == STATE_PROPAGATED ||
            target == STATE_READY_FOR_TRANSPORT ||
            target == STATE_STAGED_OUT) {

            req = reqFactory.shutdownNormal();

            if (req == null) {
                throw new TaskNotImplementedException(
                                    "shutdown normal unimplemented");
            }

            requestContext.setNotify(STATE_PROPAGATED);
            resource.setStateUnderLock(STATE_SHUTTING_DOWN, null);

            if (this.trace) {
                logger.trace("\n\n   ***** ST--shutdown " + idStr +
                        ": target state is " + tarStr +
                        ", adding shutdown-normal request: " + req + "\n");
            }


        } else if (target == STATE_PAUSED) {

            if (current == STATE_PAUSING) {
                return true;
            }

            // current == STATE_PAUSED guaranteed to be false, so we are
            // just left with STATE_STARTING and STATE_STARTED

            req = reqFactory.pause();

            if (req == null) {
                throw new TaskNotImplementedException("pause not implemented");
            }

            requestContext.setNotify(STATE_PAUSED);
            resource.setStateUnderLock(STATE_PAUSING, null);

            if (this.trace) {
                logger.trace("\n\n   ***** ST--shutdown " + idStr +
                        ": target state is " + tarStr +
                        ", adding pause request: " + req + "\n");
            }

        } else {

            req = reqFactory.shutdownSerialize();

            if (req == null) {
                throw new TaskNotImplementedException(
                                    "shutdown-serialize not implemented");
            }

            requestContext.setNotify(STATE_PROPAGATED);
            resource.setStateUnderLock(STATE_SERIALIZING, null);

            if (this.trace) {
                logger.trace("\n\n   ***** ST--shutdown " + idStr +
                        ": target state is " + tarStr +
                        ", adding serialize request: " + req + "\n");
            }
        }

        requestContext.setVm(resource.getVM());
        requestContext.setGroupID(resource.getGroupId());
        requestContext.setGroupSize(resource.getGroupSize());
        if (resource.isLastInGroup()) {
            requestContext.setLastInGroup(true);
            resource.setLastInGroup(false);
        }
        requestContext.setPartOfGroupRequest(resource.isPartOfGroupRequest());
        req.setRequestContext(requestContext);
        RequestDispatch.addRequest(req, id);

        return true;
    }


    /* ***************** */
    /* readyForTransport */
    /* ***************** */

    private boolean readyForTransport(final StatefulResourceImpl resrc,
                                     final int current,
                                     final int target,
                                     final int id,
                                     final String idStr,
                                     final String curStr,
                                     final String tarStr)
            throws ManageException {


        if (target < STATE_READY_FOR_TRANSPORT) {
            return false;
        }

        if (current >= STATE_READYING_FOR_TRANSPORT) {
            return false;
        }

        // TODO: it makes sense to remove memory and IP address allocations
        // once we start readying for transport (since the workspace can not
        // turn back from that in this version).  But, we need to be careful
        // about hitting back-out-allocate in the cancellation and corruption
        // situations, so leaving it in the do_remove() method of
        // WorkspaceResourceImpl for now until there is more time to consider
        // the options.  Also, it would not make sense to back out every
        // allocation when we start reserving intra-site propagation network
        // bandwidth etc. (unless that is kept track of completely in the
        // scheduler).

        if (!resrc.isUnPropagateRequired()) {
            resrc.setStateUnderLock(STATE_READY_FOR_TRANSPORT, null);
            stageOut(resrc, STATE_READY_FOR_TRANSPORT, target, id,
                     idStr,
                     this.dataConvert.stateName(STATE_READY_FOR_TRANSPORT), tarStr);
            return true;
        }

        if (this.trace) {
            logger.trace("\n\n   ***** ST--rForT: processing " + idStr +
                         ", current = " + curStr + ", target = " +
                         tarStr + "\n");
        }


        int corrupTag = STATE_INVALID;
        if (target == STATE_READY_FOR_TRANSPORT) {
            corrupTag = STATE_READYING_FOR_TRANSPORT;
        } else if (target == STATE_STAGED_OUT) {
            corrupTag = STATE_STAGING_OUT;
        }

        // unpropagate is needed

        if (!this.globals.isUnpropagateEnabled()) {

            logger.error("should be unreachable, Binding should have " +
                    "rejected this creation request");

            final String errMsg = "unpropagate functionality " +
                            "needed but it has been disabled";
            final ManageException wexc =
                    new ManageException(errMsg);

            final int stateToSet;
            if (corrupTag == STATE_INVALID) {
                stateToSet = STATE_CORRUPTED_GENERIC;
            } else {
                stateToSet = STATE_CORRUPTED + corrupTag;
            }
            resrc.setStateUnderLock(stateToSet, wexc);
            throw wexc;
        }

        final WorkspaceRequest req = this.reqFactory.readyForTransport();
        if (req == null) {

            final String errMsg = "ready-for-transport " +
                    "functionality needed and enabled but no implementation " +
                        "class is configured";
            final ManageException wexc =
                    new ManageException(errMsg);

            final int stateToSet;
            if (corrupTag == STATE_INVALID) {
                stateToSet = STATE_CORRUPTED_GENERIC;
            } else {
                stateToSet = STATE_CORRUPTED + corrupTag;
            }
            resrc.setStateUnderLock(stateToSet, wexc);
            throw wexc;
        }

        final WorkspaceRequestContext requestContext =
                new WorkspaceRequestContext(id, resrc.getName(),
                                            this.locator, this.lager);

        requestContext.setVm(resrc.getVM());
        requestContext.setNotify(STATE_READY_FOR_TRANSPORT);
        req.setRequestContext(requestContext);

        resrc.setStateUnderLock(STATE_READYING_FOR_TRANSPORT, null);

        if (this.trace) {
            logger.trace("\n\n   ***** ST--rForT " + idStr +
                    ": adding readyForTransport request: " + req + "\n");
        }
        RequestDispatch.addRequest(req, id);

        return true;
    }


    /* ******** */
    /* stageOut */
    /* ******** */

    private boolean stageOut(final StatefulResourceImpl resource,
                             final int current,
                             final int target,
                             final int id,
                             final String idStr,
                             final String curStr,
                             final String tarStr)

            throws ManageException {

        if (current != STATE_READY_FOR_TRANSPORT) {
            return false;
        }

        if (target < STATE_READY_FOR_TRANSPORT) {
            return false;
        }

        if (this.trace) {
            logger.trace("\n\n   ***** ST--stageOut: " + idStr +
                         " is now a no-op, staging is not possible, current " +
                         "state = " + curStr + "\n");
        }

        // return value does not matter, end of chain, but return
        // false in case someone wants to add a handler after this

        return false;
    }
}
TOP

Related Classes of org.globus.workspace.service.impls.StateTransition

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.