org.springframework.statemachine.state.AbstractState.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.statemachine.state.AbstractState.java

Source

/*
 * Copyright 2015 the original author or authors.
 *
 * 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.springframework.statemachine.state;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ScheduledFuture;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.messaging.Message;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.action.ActionListener;
import org.springframework.statemachine.action.CompositeActionListener;
import org.springframework.statemachine.region.Region;
import org.springframework.statemachine.support.LifecycleObjectSupport;
import org.springframework.statemachine.trigger.Trigger;

/**
 * Base implementation of a {@link State}.
 *
 * @author Janne Valkealahti
 *
 * @param <S> the type of state
 * @param <E> the type of event
 */
public abstract class AbstractState<S, E> extends LifecycleObjectSupport implements State<S, E> {

    private static final Log log = LogFactory.getLog(AbstractState.class);

    private final S id;
    private final PseudoState<S, E> pseudoState;
    private final Collection<E> deferred;
    private final Collection<? extends Action<S, E>> entryActions;
    private final Collection<? extends Action<S, E>> exitActions;
    private final Collection<? extends Action<S, E>> stateActions;
    private final Collection<Region<S, E>> regions = new ArrayList<Region<S, E>>();
    private final StateMachine<S, E> submachine;
    private List<Trigger<S, E>> triggers = new ArrayList<Trigger<S, E>>();
    private final CompositeStateListener<S, E> stateListener = new CompositeStateListener<S, E>();
    private final List<ScheduledFuture<?>> cancellableActions = new ArrayList<>();
    private CompositeActionListener<S, E> actionListener;

    /**
     * Instantiates a new abstract state.
     *
     * @param id the state identifier
     * @param pseudoState the pseudo state
     */
    public AbstractState(S id, PseudoState<S, E> pseudoState) {
        this(id, null, null, null, pseudoState);
    }

    /**
     * Instantiates a new abstract state.
     *
     * @param id the state identifier
     * @param deferred the deferred
     */
    public AbstractState(S id, Collection<E> deferred) {
        this(id, deferred, null, null);
    }

    /**
     * Instantiates a new abstract state.
     *
     * @param id the state identifier
     * @param deferred the deferred
     * @param entryActions the entry actions
     * @param exitActions the exit actions
     */
    public AbstractState(S id, Collection<E> deferred, Collection<? extends Action<S, E>> entryActions,
            Collection<? extends Action<S, E>> exitActions) {
        this(id, deferred, entryActions, exitActions, null);
    }

    /**
     * Instantiates a new abstract state.
     *
     * @param id the state identifier
     * @param deferred the deferred
     * @param entryActions the entry actions
     * @param exitActions the exit actions
     * @param pseudoState the pseudo state
     */
    public AbstractState(S id, Collection<E> deferred, Collection<? extends Action<S, E>> entryActions,
            Collection<? extends Action<S, E>> exitActions, PseudoState<S, E> pseudoState) {
        this(id, deferred, entryActions, exitActions, pseudoState, null, null);
    }

    /**
     * Instantiates a new abstract state.
     *
     * @param id the state identifier
     * @param deferred the deferred
     * @param entryActions the entry actions
     * @param exitActions the exit actions
     * @param pseudoState the pseudo state
     * @param submachine the submachine
     */
    public AbstractState(S id, Collection<E> deferred, Collection<? extends Action<S, E>> entryActions,
            Collection<? extends Action<S, E>> exitActions, PseudoState<S, E> pseudoState,
            StateMachine<S, E> submachine) {
        this(id, deferred, entryActions, exitActions, pseudoState, null, submachine);
    }

    /**
     * Instantiates a new abstract state.
     *
     * @param id the state identifier
     * @param deferred the deferred
     * @param entryActions the entry actions
     * @param exitActions the exit actions
     * @param pseudoState the pseudo state
     * @param regions the regions
     */
    public AbstractState(S id, Collection<E> deferred, Collection<? extends Action<S, E>> entryActions,
            Collection<? extends Action<S, E>> exitActions, PseudoState<S, E> pseudoState,
            Collection<Region<S, E>> regions) {
        this(id, deferred, entryActions, exitActions, pseudoState, regions, null);
    }

    /**
     * Instantiates a new abstract state.
     *
     * @param id the state identifier
     * @param deferred the deferred
     * @param entryActions the entry actions
     * @param exitActions the exit actions
     * @param pseudoState the pseudo state
     * @param regions the regions
     * @param submachine the submachine
     */
    public AbstractState(S id, Collection<E> deferred, Collection<? extends Action<S, E>> entryActions,
            Collection<? extends Action<S, E>> exitActions, PseudoState<S, E> pseudoState,
            Collection<Region<S, E>> regions, StateMachine<S, E> submachine) {
        this(id, deferred, entryActions, exitActions, null, pseudoState, regions, submachine);
    }

    /**
     * Instantiates a new abstract state.
     *
     * @param id the state identifier
     * @param deferred the deferred
     * @param entryActions the entry actions
     * @param exitActions the exit actions
     * @param stateActions the state actions
     * @param pseudoState the pseudo state
     * @param regions the regions
     * @param submachine the submachine
     */
    public AbstractState(S id, Collection<E> deferred, Collection<? extends Action<S, E>> entryActions,
            Collection<? extends Action<S, E>> exitActions, Collection<? extends Action<S, E>> stateActions,
            PseudoState<S, E> pseudoState, Collection<Region<S, E>> regions, StateMachine<S, E> submachine) {
        this.id = id;
        this.deferred = deferred != null ? deferred : Collections.<E>emptySet();
        this.entryActions = entryActions != null ? entryActions : Collections.<Action<S, E>>emptySet();
        this.exitActions = exitActions != null ? exitActions : Collections.<Action<S, E>>emptySet();
        this.stateActions = stateActions != null ? stateActions : Collections.<Action<S, E>>emptySet();
        this.pseudoState = pseudoState;

        // use of private ctor should prevent user to
        // add regions and a submachine which is not allowed.
        if (regions != null) {
            this.regions.addAll(regions);
        }
        this.submachine = submachine;
    }

    @Override
    public boolean sendEvent(Message<E> event) {
        return false;
    }

    @Override
    public boolean shouldDefer(Message<E> event) {
        return deferred.contains(event.getPayload());
    }

    @Override
    public void exit(StateContext<S, E> context) {
        cancelStateActions();
        stateListener.onExit(context);
        for (Trigger<S, E> trigger : triggers) {
            trigger.disarm();
        }
    }

    @Override
    public void entry(StateContext<S, E> context) {
        stateListener.onEntry(context);
        for (Trigger<S, E> trigger : triggers) {
            trigger.arm();
        }
        scheduleStateActions(context);
    }

    @Override
    public S getId() {
        return id;
    }

    @Override
    public abstract Collection<S> getIds();

    @Override
    public abstract Collection<State<S, E>> getStates();

    @Override
    public PseudoState<S, E> getPseudoState() {
        return pseudoState;
    }

    @Override
    public Collection<E> getDeferredEvents() {
        return deferred;
    }

    @Override
    public Collection<? extends Action<S, E>> getEntryActions() {
        return entryActions;
    }

    @Override
    public Collection<? extends Action<S, E>> getExitActions() {
        return exitActions;
    }

    @Override
    public boolean isComposite() {
        return !regions.isEmpty();
    }

    @Override
    public boolean isOrthogonal() {
        return regions.size() > 1;
    }

    @Override
    public boolean isSimple() {
        return !isSubmachineState() && !isComposite();
    }

    @Override
    public boolean isSubmachineState() {
        return submachine != null;
    }

    @Override
    public void addStateListener(StateListener<S, E> listener) {
        stateListener.register(listener);
    }

    @Override
    public void removeStateListener(StateListener<S, E> listener) {
        stateListener.unregister(listener);
    }

    @Override
    public void addActionListener(ActionListener<S, E> listener) {
        synchronized (this) {
            if (this.actionListener == null) {
                this.actionListener = new CompositeActionListener<>();
            }
            this.actionListener.register(listener);
        }
    }

    @Override
    public void removeActionListener(ActionListener<S, E> listener) {
        synchronized (this) {
            if (this.actionListener != null) {
                this.actionListener.unregister(listener);
            }
        }
    }

    /**
     * Gets the submachine.
     *
     * @return the submachine or null if not set
     */
    public StateMachine<S, E> getSubmachine() {
        return submachine;
    }

    /**
     * Gets the regions.
     *
     * @return the regions or empty collection if no regions
     */
    public Collection<Region<S, E>> getRegions() {
        return regions;
    }

    /**
     * Sets the triggers.
     *
     * @param triggers the triggers
     */
    public void setTriggers(List<Trigger<S, E>> triggers) {
        if (triggers != null) {
            this.triggers = triggers;
        } else {
            this.triggers.clear();
        }
    }

    /**
     * Gets the triggers.
     *
     * @return the triggers
     */
    public List<Trigger<S, E>> getTriggers() {
        return triggers;
    }

    /**
     * Cancel existing state actions and clear list.
     */
    protected void cancelStateActions() {
        for (ScheduledFuture<?> future : cancellableActions) {
            future.cancel(true);
        }
        cancellableActions.clear();
    }

    /**
     * Schedule state actions and store futures into list to
     * be cancelled.
     *
     * @param context the context
     */
    protected void scheduleStateActions(StateContext<S, E> context) {
        for (Action<S, E> action : stateActions) {
            ScheduledFuture<?> future = scheduleAction(action, context);
            if (future != null) {
                cancellableActions.add(future);
            }
        }
    }

    /**
     * Execute action and notify action listener if set.
     *
     * @param action the action
     * @param context the context
     */
    protected void executeAction(Action<S, E> action, StateContext<S, E> context) {
        long now = System.currentTimeMillis();
        action.execute(context);
        if (this.actionListener != null) {
            try {
                this.actionListener.onExecute(context.getStateMachine(), action, System.currentTimeMillis() - now);
            } catch (Exception e) {
                log.warn("Error with actionListener", e);
            }
        }
    }

    /**
     * Schedule action and return future which can be used to cancel it.
     *
     * @param action the action
     * @param context the context
     * @return the scheduled future
     */
    protected ScheduledFuture<?> scheduleAction(final Action<S, E> action, final StateContext<S, E> context) {
        TaskScheduler taskScheduler = getTaskScheduler();
        if (taskScheduler == null) {
            log.error("Unable to schedule action as taskSchedule is not set, action=[" + action + "]");
            return null;
        }
        ScheduledFuture<?> future = taskScheduler.schedule(new Runnable() {

            @Override
            public void run() {
                executeAction(action, context);
            }
        }, new Date());
        return future;
    }

    @Override
    public String toString() {
        return "AbstractState [id=" + id + ", pseudoState=" + pseudoState + ", deferred=" + deferred
                + ", entryActions=" + entryActions + ", exitActions=" + exitActions + ", stateActions="
                + stateActions + ", regions=" + regions + ", submachine=" + submachine + "]";
    }
}