![JAR search and dependency download from the Maven repository](/logo.png)
com.tangosol.util.fsm.AnnotationDrivenModel Maven / Gradle / Ivy
/*
* Copyright (c) 2000, 2020, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
package com.tangosol.util.fsm;
import com.tangosol.util.fsm.annotations.OnEnterState;
import com.tangosol.util.fsm.annotations.OnExitState;
import com.tangosol.util.fsm.annotations.OnTransition;
import com.tangosol.util.fsm.annotations.Transitions;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.EnumMap;
import java.util.Map;
/**
* An {@link AnnotationDrivenModel} is a {@link Model} created through extracting
* information defined by {@link Transitions} annotation.
*
* @param the type of the state for the {@link AnnotationDrivenModel}
*
* @author Brian Oliver
* @since Coherence 12.2.1
*/
public class AnnotationDrivenModel>
implements Model
{
// ----- AnnotationDrivenModel constructor ------------------------------
/**
* Constructs an {@link AnnotationDrivenModel} based on the specified
* annotated class.
*
* @param clzState the {@link Class} of the state of the {@link Transitions}
* @param oInstance the instance from which the {@link Model} will be
* reflected
*/
public AnnotationDrivenModel(Class clzState, Object oInstance)
{
if (oInstance == null)
{
throw new IllegalArgumentException(String.format("Can't create an %s from a null instance",
this.getClass().getName()));
}
else
{
// construct the model based on the annotations
Class> clzInstance = oInstance.getClass();
m_model = new SimpleModel(clzState);
// define maps to hold the reflected transition information
EnumMap> mapTransitionNames = new EnumMap>(clzState);
EnumMap>> mapTransitionActions = new EnumMap>>(clzState);
for (S state : m_model.getStates())
{
mapTransitionNames.put(state, new EnumMap(clzState));
mapTransitionActions.put(state, new EnumMap>(clzState));
}
// reflect out the defined transitions
Transitions annTransitions = clzInstance.getAnnotation(Transitions.class);
if (annTransitions != null)
{
// determine the valid state transitions based on the
// @Transitions annotation in the specified class
for (com.tangosol.util.fsm.annotations.Transition annTransition :
annTransitions.value())
{
String sProvidedTransitionName = annTransition.name();
// ensure the transition name is null if it wasn't specified
sProvidedTransitionName = sProvidedTransitionName == null
|| sProvidedTransitionName.trim().isEmpty() ? null
: sProvidedTransitionName;
// determine the ending state for the transition
String sStateToName = annTransition.toState();
S stateTo = m_model.getState(sStateToName);
if (stateTo == null)
{
throw new IllegalArgumentException(String.format(
"The %s defined on %s declares a to state %s that is not defined by %s.",
annTransition, clzInstance, sStateToName, clzState));
}
// determine the starting states for the transition
for (String sStateFromName : annTransition.fromStates())
{
// determine the starting states for the transition
S stateFrom = m_model.getState(sStateFromName);
if (stateFrom == null)
{
throw new IllegalArgumentException(String.format(
"The %s defined on %s declares a from state %s that is not defined by %s.",
annTransition, clzInstance, sStateFromName, clzState));
}
else
{
// ensure we have a transition name
String sTransitionName = sProvidedTransitionName == null
? String.format("%s to %s", stateFrom.name(), stateTo.name())
: sProvidedTransitionName;
// define the transition
mapTransitionNames.get(stateFrom).put(stateTo, sTransitionName);
}
}
}
}
// reflect and add state entry, exit and transition actions from
// annotated methods into the model
for (Method method : clzInstance.getMethods())
{
// add the StateEntryAction for the method (if annotated)
OnEnterState annOnEnterState = method.getAnnotation(OnEnterState.class);
if (annOnEnterState != null)
{
S state = m_model.getState(annOnEnterState.value());
if (state == null)
{
throw new IllegalArgumentException(String.format(
"The %s annotation on method %s defined in %s declares the state %s that " +
"is not defined in %s.",
annOnEnterState, method, clzInstance, annOnEnterState.value(), clzState));
}
// ensure the method has the correct signature for the
if (ReflectionHelper.isCompatibleMethod(method, Modifier.PUBLIC, Instruction.class, clzState,
clzState, Event.class, ExecutionContext.class))
{
// register the StateEntryAction
m_model.addStateEntryAction(state, new StateEntryActionMethod(oInstance, method));
}
else
{
throw new IllegalArgumentException(String.format(
"The method %s defined in class %s annotated with %s is not compatible with the " +
"required method signature 'Instruction method(State, State, Context);'.",
method, clzInstance, annOnEnterState));
}
}
// add the StateEntryAction for the method (if annotated)
OnExitState annOnExitState = method.getAnnotation(OnExitState.class);
if (annOnExitState != null)
{
// determine the state
S state = m_model.getState(annOnExitState.value());
if (state == null)
{
throw new IllegalArgumentException(String.format(
"The %s annotation on method %s defined in %s declares the state %s " +
"that is not defined in %s.",
annOnExitState, method, clzInstance, annOnExitState.value(), clzState));
}
// ensure the method has the correct signature for the
if (ReflectionHelper.isCompatibleMethod(method, Modifier.PUBLIC, Void.TYPE, clzState, Event.class,
ExecutionContext.class))
{
// register the StateExitAction
m_model.addStateExitAction(state, new StateExitActionMethod(oInstance, method));
}
else
{
throw new IllegalArgumentException(String.format(
"The method %s defined in class %s annotated with %s is not compatible with the " +
"required method signature 'void method(State, Context);'.",
method, clzInstance, annOnExitState));
}
}
// add the TransitionAction for the method (if annotated)
OnTransition annOnTransition = method.getAnnotation(OnTransition.class);
if (annOnTransition != null)
{
// ensure that the method has the correct signature
if (ReflectionHelper.isCompatibleMethod(method, Modifier.PUBLIC, Void.TYPE,
String.class, clzState, clzState, Event.class, ExecutionContext.class))
{
TransitionAction action = new TransitionActionMethod(oInstance, method);
// add the action for each of the transitions
for (String sStateFromName : annOnTransition.fromStates())
{
// determine the starting state for the transition
S stateFrom = m_model.getState(sStateFromName);
if (stateFrom == null)
{
throw new IllegalArgumentException(String.format(
"The %s defined on method %s in %s declares a from state %s that " +
"is not defined by %s.",
annOnTransition, method, clzInstance, sStateFromName, clzState));
}
else
{
for (String sStateToName : annOnTransition.toStates())
{
// determine the ending state for the transition
S stateTo = m_model.getState(sStateToName);
if (stateTo == null)
{
throw new IllegalArgumentException(String.format(
"The %s defined on method %s in %s declares a from state %s that " +
"is not defined by %s.",
annOnTransition, method, clzInstance, sStateToName, clzState));
}
else if (mapTransitionNames.get(stateFrom).containsKey(stateTo))
{
// add the transition action to the transition
mapTransitionActions.get(stateFrom).put(stateTo, action);
}
else
{
throw new IllegalArgumentException(String.format(
"The %s defined on method %s in %s specifies a transition from " +
"%s to %s that is not defined by the Finite State Machine.",
annOnTransition, method, clzInstance, stateFrom, stateTo));
}
}
}
}
}
else
{
throw new IllegalArgumentException(String.format(
"The method %s defined in class %s annotated with %s is not compatible with the " +
"required method signature 'void method(String, State, State, " +
" Event, Context);'.",
method, clzInstance, annOnTransition));
}
}
}
// define the transitions on the model
for (S stateFrom : m_model.getStates())
{
for (S stateTo : mapTransitionNames.keySet())
{
String sTransitionName = mapTransitionNames.get(stateFrom).get(stateTo);
if (sTransitionName != null)
{
TransitionAction action = mapTransitionActions.get(stateFrom).get(stateTo);
m_model.addTransition(new Transition(sTransitionName, stateFrom, stateTo, action));
}
}
}
}
}
// ----- Model interface ------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public Class getStateClass()
{
return m_model.getStateClass();
}
/**
* {@inheritDoc}
*/
@Override
public Map> getStateEntryActions()
{
return m_model.getStateEntryActions();
}
/**
* {@inheritDoc}
*/
@Override
public Map> getStateExitActions()
{
return m_model.getStateExitActions();
}
/**
* {@inheritDoc}
*/
@Override
public S[] getStates()
{
return m_model.getStates();
}
/**
* {@inheritDoc}
*/
@Override
public Iterable> getTransitions()
{
return m_model.getTransitions();
}
// ----- inner class StateEntryActionMethod -----------------------------
/**
* A {@link StateEntryActionMethod} is an {@link StateEntryAction}
* implementation based on a specified {@link Method} and instance.
*/
private static class StateEntryActionMethod>
implements StateEntryAction
{
/**
* Constructs an {@link StateEntryActionMethod}.
*
* @param oInstance the instance on which to invoke the {@link StateEntryAction}
* @param method the method to use for performing the {@link StateEntryAction}
*/
public StateEntryActionMethod(Object oInstance, Method method)
{
m_oInstance = oInstance;
m_method = method;
}
/**
* {@inheritDoc}
*/
public Instruction onEnterState(S previousState, S newState, Event event, ExecutionContext context)
{
try
{
return (Instruction) m_method.invoke(m_oInstance, previousState, newState, event, context);
}
catch (RuntimeException e)
{
throw e;
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
/**
* The instance on which to invoke the methods.
*/
private Object m_oInstance;
/**
* The {@link Method} to execute for the {@link StateEntryAction}.
*/
private Method m_method;
}
// ----- inner class StateExitActionMethod ------------------------------
/**
* A {@link StateExitActionMethod} is an {@link StateExitAction}
* implementation based on a specified {@link Method} and instance.
*/
private static class StateExitActionMethod>
implements StateExitAction
{
/**
* Constructs an {@link StateExitActionMethod}.
*
* @param oInstance the instance on which to invoke the {@link StateEntryAction}
* @param method the method to use for performing the {@link StateEntryAction}
*/
public StateExitActionMethod(Object oInstance, Method method)
{
m_oInstance = oInstance;
m_method = method;
}
/**
* {@inheritDoc}
*/
public void onExitState(S state, Event event, ExecutionContext context)
{
try
{
m_method.invoke(m_oInstance, state, event, context);
}
catch (RuntimeException e)
{
throw e;
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
/**
* The instance on which to invoke the methods.
*/
private Object m_oInstance;
/**
* The {@link Method} to execute for the {@link StateEntryAction}.
*/
private Method m_method;
}
// ----- inner class TransitionActionMethod -----------------------------
/**
* An {@link TransitionActionMethod} is an {@link TransitionAction}
* implementation based on a specified {@link Method} and instance.
*/
private static class TransitionActionMethod>
implements TransitionAction
{
/**
* Constructs an {@link TransitionActionMethod}.
*
* @param oInstance the instance on which to invoke the {@link TransitionAction}
* @param method the method to use for performing the {@link TransitionAction}
*/
public TransitionActionMethod(Object oInstance, Method method)
{
m_oInstance = oInstance;
m_method = method;
}
/**
* {@inheritDoc}
*/
public void onTransition(String sTransitionName, S stateFrom, S stateTo, Event event,
ExecutionContext context)
throws RollbackTransitionException
{
try
{
m_method.invoke(m_oInstance, sTransitionName, stateFrom, stateTo, event, context);
}
catch (Exception e)
{
throw new RollbackTransitionException(stateFrom, stateTo, e);
}
}
// ----- data members -----------------------------------------------
/**
* The instance on which to invoke the methods.
*/
private Object m_oInstance;
/**
* The {@link Method} to execute for the {@link TransitionAction}.
*/
private Method m_method;
}
// ----- data members ---------------------------------------------------
/**
* The actual {@link Model}.
*/
private SimpleModel m_model;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy