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

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

Go to download

Core functionality for Status Machina, a small, simple and pragmatic state machine for resilient microservices orchestration.

There is a newer version: 8.4.2
Show newest version
/*
 *
 * 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;

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

import java.util.*;

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;

    public static  MachineBuilder ofType(MachineDefinition definition) {
        return new MachineInstanceBuilderImpl().ofType(definition);
    }

    MachineInstanceImpl(MachineDefinition def, Map context) throws TransitionException {
        this(def, UUID.randomUUID().toString(), context);
    }

    MachineInstanceImpl(MachineDefinition def,String id,  Map context) throws TransitionException {
        this.def = def;
        final StateAndContext kickoff = kickoff(def.getInitialState(), ImmutableMap.builder().putAll(context).build());

        this.id = id;
        this.history = ImmutableList.>builder().build();
        this.currentState = kickoff.getState();
        this.context = kickoff.getContext();
        this.error = Optional.empty();
    }

    public MachineInstanceImpl(
            String id,
            MachineDefinition def,
            S currentState,
            Map context,
            List> history,
            Optional error
    ) throws TransitionException {
        this.def = def;
        final StateAndContext kickoff = currentState == def.getInitialState() ?
                kickoff(def.getInitialState(), ImmutableMap.builder().putAll(context).build())
                : new StateAndContext(currentState, ImmutableMap.builder().putAll(context).build());

        this.id = id;
        this.history = ImmutableList.>builder().addAll(history).build();
        this.error = error;
        this.currentState = kickoff.getState();
        this.context = kickoff.getContext();
    }

    private StateAndContext kickoff(S initialState, ImmutableMap initialContext) {
        S runningState = initialState;
        ImmutableMap runningContext = ImmutableMap.builder().putAll(initialContext).build();
        Optional> transition;
        while ((transition = def.findStpTransition(runningState)).isPresent()) {
            final Transition seTransition = transition.get();
            final Optional> action = seTransition.getAction();
            final ImmutableMap tmpContext = ImmutableMap.builder().putAll(runningContext).build();
            runningContext = action.map(mapConsumer -> mapConsumer.apply(tmpContext, null)).orElse(tmpContext);
            runningState = seTransition.getTo();
        }

        return new StateAndContext(runningState, runningContext);
    }

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

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

    @Override
    public Map getContext() {
        return new HashMap<>(context);
    }

    @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();
    }

    @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 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); } private Machine tryStp() throws TransitionException { 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 { 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(); return new MachineInstanceImpl<>(id, def, newState, newContext, history, newError).tryStp(); } catch (Throwable t) { def.getErrorHandler().accept(new DefaultErrorData<>(transition, param, t)); Optional newError = Optional.of(t.getMessage()); return new MachineInstanceImpl<>(id, def, currentState, context, history, newError); } } @Override public boolean isTerminalState() { return def.getTerminalStates().contains(currentState); } 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