Commons SCXML - Force jump to a given state

633 views Asked by At

I am using Apache Commons SCXML, and I would like to know if it is possible to tell the state machine (SCXMLExecutor) to jump to a given state.

I can not use the initialstate attribute, because I want the state machine to recover (i.e. from power failures), and the only thing I have is the last state. That is why I was thinking about telling the state machine to make a direct jump to it.

3

There are 3 answers

1
Jim Garrison On BEST ANSWER

In the general case it's a really bad idea to jump to a state without the state machine's being "aware" of it, because there may be preconditions for a particular state's execution that aren't satisfied (that would be if you reached the state the "normal") way. A better idea is to design the state machine with a "restart" capability, implemented as an input "restart" event and the states and transitions necessary to handle it.

0
Evan Reynolds On

This is an old question, but I just hit this and needed an answer to it as well and thought it might help others to answer it. I am using this as part of unit testing, where it IS extremely useful to just get to a particular state (I want to be sure that if at state A, if a sequence of events happens, it goes to state B - and still goes there after I tinker with the state machine XML!)

I finally found this code in SCXMLTestHelper and it worked. Just call it with the executor and the destination state.

public static void setCurrentState(SCXMLExecutor exec, final String id) throws IllegalArgumentException{
    try {
        exec.reset();
    } catch (ModelException me) {
        throw new IllegalArgumentException("Provided SCXMLExecutor "
                + "instance cannot be reset.");
    }
    TransitionTarget active = (TransitionTarget) exec.getStateMachine().
            getTargets().get(id);
    if (active == null) {
        throw new IllegalArgumentException("No target with id '" + id
                + "' present in state machine.");
    }
    Set current = exec.getCurrentStatus().getStates();
    current.clear();
    current.add(active);
}
0
milchreis On

It's an old question, but in common-scxml2 the given code doesn't work anymore. I did some research and found a solution for the current version that works fine.

import org.apache.commons.scxml2.SCInstance;
import org.apache.commons.scxml2.SCXMLExecutor;
import org.apache.commons.scxml2.model.EnterableState;
import org.apache.commons.scxml2.model.TransitionTarget;

public class AccessibleSCXMLExecutor extends SCXMLExecutor {

    public void setCurrentState(String targetId) {
        final EnterableState targetState = getStateMachine().getChildren().stream()
                .filter(s -> s.getId().equals(targetId))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("The state '" + targetId + "' is unknown"));

        final SCInstance scInstance = getSCInstance();
        scInstance.getStateConfiguration().clear();
        scInstance.getStateConfiguration().enterState(targetState);
    }
}

The main problem is the inaccessible SCInstance which contains the running statemachine. Fortunately this object is protected and can be called by derived classes.