All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.statusmachina.core.stdimpl.MachineInstanceImpl Maven / Gradle / Ivy

/*
 *  Copyright 2019 <---> Present Status Machina Contributors (https://github.com/entzik/status-machina/graphs/contributors)
 *
 *  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
 *
 *  This software 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 io.statusmachina.core.stdimpl;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.statusmachina.core.api.*;
import io.statusmachina.core.spi.MachinePersistenceCallback;

import java.util.*;
import java.util.function.Consumer;

public class MachineInstanceImpl implements Machine {
    private final String id;

    private final MachineDefinition def;
    private final ImmutableMap context;
    private final ImmutableList> history;
    private final Optional error;

    private final S currentState;
    private final MachinePersistenceCallback persistenceCallback;

    public MachineInstanceImpl(
            MachineDefinition def,
            MachinePersistenceCallback persistenceCallback,
            Map context
    ) throws Exception {
        this(def, UUID.randomUUID().toString(), persistenceCallback, context);
    }

    public MachineInstanceImpl(
            MachineDefinition def,
            String id,
            MachinePersistenceCallback persistenceCallback,
            Map context
    ) throws Exception {
        this(def, id, context, persistenceCallback);
    }

    public MachineInstanceImpl(
            MachineDefinition def,
            String id,
            Map context, MachinePersistenceCallback persistenceCallback
    ) throws Exception {
        this.def = def;

        this.id = id;
        this.history = ImmutableList.>builder().build();
        this.currentState = def.getInitialState();
        this.context = ImmutableMap.builder().putAll(context).build();
        this.error = Optional.empty();
        this.persistenceCallback = persistenceCallback;

        persistenceCallback.runInTransaction(() -> persistenceCallback.saveNew(this));
    }

    public MachineInstanceImpl(
            String id,
            MachineDefinition def,
            S currentState,
            Map context,
            List> history,
            Optional error,
            MachinePersistenceCallback persistenceCallback
    ) throws TransitionException {
        this.def = def;

        this.id = id;
        this.history = ImmutableList.>builder().addAll(history).build();
        this.error = error;
        this.currentState = currentState;
        this.context = ImmutableMap.builder().putAll(context).build();
        this.persistenceCallback = persistenceCallback;
    }

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

    @Override
    public S getCurrentState() {
        return currentState;
    }

    @Override
    public ImmutableMap getContext() {
        return ImmutableMap.builder().putAll(context).build();
    }

    @Override
    public List> getHistory() {
        return new ArrayList<>(history);
    }

    @Override
    public Optional getError() {
        return error;
    }

    @Override
    public MachineDefinition getDefinition() {
        return def;
    }

    @Override
    public boolean isErrorState() {
        return error.isPresent();
    }

    public Machine start() {
        if (currentState.equals(def.getInitialState()))
            return tryStp();
        else
            throw new IllegalStateException("machine is already started");
    }

    @Override
    public Machine  sendEvent(E event) throws TransitionException {
        Transition transition = def.findEventTransion(currentState, event).orElseThrow(() -> new IllegalStateException("for machines of type " + def.getName() + " event " + event.toString() + " does not trigger any transition out of state " + currentState.toString()));
        return applyTransition(transition, null);
    }

    @Override
    public 

Machine sendEvent(E event, P param) throws TransitionException { final Transition transition = def.findEventTransion(currentState, event).orElseThrow(() -> new IllegalStateException("for machines of type " + def.getName() + " event " + event.toString() + " does not trigger any transition out of state " + currentState.toString())); return applyTransition(transition, param); } /* @Override public Machine sendEvent(E event) throws TransitionException { return sendEvent(event); } @Override public

Machine sendEvent(E event, P param) throws TransitionException { return sendEvent(event, param); } */ @Override public Machine recoverFromError(S state, Map context) { Optional newError = Optional.empty(); ImmutableMap newContext = ImmutableMap.builder().putAll(context).build(); return new MachineInstanceImpl(id, def, state, newContext, history, newError, persistenceCallback); } private Machine tryStp() throws TransitionException { if (this.isErrorState()) return this; else { final Optional> stpTransition = def.findStpTransition(currentState); if (stpTransition.isPresent()) { return applyTransition(stpTransition.get(), null); } else return this; } } private

Machine applyTransition(Transition transition, P param) throws TransitionException { try { final Machine machine = persistenceCallback.runInTransaction(() -> { final Optional> action = transition.getAction(); try { ImmutableMap newContext = action.map(mapConsumer -> ((TransitionAction

) mapConsumer).apply(context, param)).orElse(context); Optional newError = Optional.empty(); S newState = transition.getTo(); final MachineInstanceImpl newMachine = new MachineInstanceImpl<>(id, def, newState, newContext, history, newError, persistenceCallback); return persistenceCallback.update(newMachine); } catch (Throwable t) { def.getErrorHandler().accept(new DefaultErrorData<>(transition, param, t)); Optional newError = Optional.of(t.getMessage()); final MachineInstanceImpl newMachine = new MachineInstanceImpl<>(id, def, currentState, context, history, newError, persistenceCallback); return persistenceCallback.update(newMachine); } }); if (!machine.isErrorState()) transition.getPostAction().ifPresent(transitionAction -> ((TransitionAction

) transitionAction).apply(machine.getContext(), param)); return ((MachineInstanceImpl) machine).tryStp(); } catch (Exception e) { throw new TransitionException(this, transition, e); } } @Override public boolean isTerminalState() { return def.getTerminalStates().contains(currentState); } private static class VoidMachineConsumer implements Consumer> { @Override public void accept(Machine seMachine) { } } private class StateAndContext { private final S state; private ImmutableMap context; public StateAndContext(S state, ImmutableMap context) { this.state = state; this.context = context; } public S getState() { return state; } public ImmutableMap getContext() { return context; } } private class DefaultErrorData implements ErrorData { private final Transition transition; private final P param; private final Throwable t; public DefaultErrorData(Transition transition, P param, Throwable t) { this.transition = transition; this.param = param; this.t = t; } @Override public S getFrom() { return transition.getFrom(); } @Override public S getTo() { return transition.getTo(); } @Override public Optional getEvent() { return transition.getEvent(); } @Override public Map getContext() { return new HashMap<>(context); } @Override public P getEventParameter() { return param; } @Override public String getErrorMessage() { return t.getMessage(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy