Fixes for annotation method handling
- adding generics - tidy up annotation handling for methods - add EventHeaders annotation
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface EventHeaders {
|
||||
}
|
||||
@@ -25,25 +25,27 @@ import java.lang.reflect.Method;
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <T> the return type
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public class MethodInvokingStateMachineRuntimeProcessor<T> implements StateMachineRuntimeProcessor<T> {
|
||||
public class MethodInvokingStateMachineRuntimeProcessor<T, S, E> implements StateMachineRuntimeProcessor<T, S, E> {
|
||||
|
||||
private final StateMachineMethodInvokerHelper<T> delegate;
|
||||
private final StateMachineMethodInvokerHelper<T, S, E> delegate;
|
||||
|
||||
public MethodInvokingStateMachineRuntimeProcessor(Object targetObject, Method method) {
|
||||
delegate = new StateMachineMethodInvokerHelper<T>(targetObject, method);
|
||||
delegate = new StateMachineMethodInvokerHelper<T, S, E>(targetObject, method);
|
||||
}
|
||||
|
||||
public MethodInvokingStateMachineRuntimeProcessor(Object targetObject, String methodName) {
|
||||
delegate = new StateMachineMethodInvokerHelper<T>(targetObject, methodName);
|
||||
delegate = new StateMachineMethodInvokerHelper<T, S, E>(targetObject, methodName);
|
||||
}
|
||||
|
||||
public MethodInvokingStateMachineRuntimeProcessor(Object targetObject, Class<? extends Annotation> annotationType) {
|
||||
delegate = new StateMachineMethodInvokerHelper<T>(targetObject, annotationType);
|
||||
delegate = new StateMachineMethodInvokerHelper<T, S, E>(targetObject, annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T process(StateMachineRuntime stateMachineRuntime) {
|
||||
public T process(StateMachineRuntime<S, E> stateMachineRuntime) {
|
||||
try {
|
||||
return delegate.process(stateMachineRuntime);
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -40,8 +40,8 @@ public class StateMachineActivatorAnnotationPostProcessor implements MethodAnnot
|
||||
|
||||
@Override
|
||||
public Object postProcess(Object bean, String beanName, Method method, OnTransition annotation) {
|
||||
StateMachineHandler handler = new StateMachineOnTransitionHandler(bean, method, annotation);
|
||||
|
||||
StateMachineHandler<Object, Object> handler = new StateMachineOnTransitionHandler<Object, Object>(bean, method, annotation);
|
||||
|
||||
Integer order = findOrder(bean, method);
|
||||
if (order != null) {
|
||||
handler.setOrder(order);
|
||||
|
||||
@@ -28,22 +28,16 @@ import org.springframework.statemachine.annotation.WithStateMachine;
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <T> the return type
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public class StateMachineHandler implements Ordered {
|
||||
public class StateMachineHandler<S, E> implements Ordered {
|
||||
|
||||
private final StateMachineRuntimeProcessor<?> processor;
|
||||
private final StateMachineRuntimeProcessor<?, S, E> processor;
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
/**
|
||||
* Instantiates a new container handler.
|
||||
*
|
||||
* @param target the target bean
|
||||
*/
|
||||
// public StateMachineHandler(Object target) {
|
||||
// this(new MethodInvokingStateMachineRuntimeProcessor<Object>(target, OnTransition.class));
|
||||
// }
|
||||
|
||||
/**
|
||||
* Instantiates a new container handler.
|
||||
*
|
||||
@@ -51,7 +45,7 @@ public class StateMachineHandler implements Ordered {
|
||||
* @param method the method
|
||||
*/
|
||||
public StateMachineHandler(Object target, Method method) {
|
||||
this(new MethodInvokingStateMachineRuntimeProcessor<Object>(target, method));
|
||||
this(new MethodInvokingStateMachineRuntimeProcessor<Object, S, E>(target, method));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,7 +55,7 @@ public class StateMachineHandler implements Ordered {
|
||||
* @param methodName the method name
|
||||
*/
|
||||
public StateMachineHandler(Object target, String methodName) {
|
||||
this(new MethodInvokingStateMachineRuntimeProcessor<Object>(target, methodName));
|
||||
this(new MethodInvokingStateMachineRuntimeProcessor<Object, S, E>(target, methodName));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,7 +64,7 @@ public class StateMachineHandler implements Ordered {
|
||||
* @param <T> the generic type
|
||||
* @param processor the processor
|
||||
*/
|
||||
public <T> StateMachineHandler(MethodInvokingStateMachineRuntimeProcessor<T> processor) {
|
||||
public <T> StateMachineHandler(MethodInvokingStateMachineRuntimeProcessor<T, S, E> processor) {
|
||||
this.processor = processor;
|
||||
}
|
||||
|
||||
@@ -95,7 +89,7 @@ public class StateMachineHandler implements Ordered {
|
||||
* @param stateMachineRuntime the state machine runtime
|
||||
* @return the result value
|
||||
*/
|
||||
public Object handle(StateMachineRuntime stateMachineRuntime) {
|
||||
public Object handle(StateMachineRuntime<S, E> stateMachineRuntime) {
|
||||
return processor.process(stateMachineRuntime);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
@@ -39,7 +37,9 @@ import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.annotation.EventHeaders;
|
||||
import org.springframework.statemachine.support.AbstractExpressionEvaluator;
|
||||
import org.springframework.statemachine.support.AnnotatedMethodFilter;
|
||||
import org.springframework.statemachine.support.FixedMethodFilter;
|
||||
@@ -56,7 +56,7 @@ import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
*
|
||||
* @param <T> the return type
|
||||
*/
|
||||
public class StateMachineMethodInvokerHelper<T> extends AbstractExpressionEvaluator {
|
||||
public class StateMachineMethodInvokerHelper<T, S, E> extends AbstractExpressionEvaluator {
|
||||
|
||||
private static final String CANDIDATE_METHODS = "CANDIDATE_METHODS";
|
||||
|
||||
@@ -105,8 +105,8 @@ public class StateMachineMethodInvokerHelper<T> extends AbstractExpressionEvalua
|
||||
this(targetObject, annotationType, (String) null, expectedType);
|
||||
}
|
||||
|
||||
public T process(StateMachineRuntime stateMachineRuntime) throws Exception {
|
||||
ParametersWrapper wrapper = new ParametersWrapper(stateMachineRuntime.getStateContext());
|
||||
public T process(StateMachineRuntime<S, E> stateMachineRuntime) throws Exception {
|
||||
ParametersWrapper<S, E> wrapper = new ParametersWrapper<S, E>(stateMachineRuntime.getStateContext());
|
||||
return processInternal(wrapper);
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ public class StateMachineMethodInvokerHelper<T> extends AbstractExpressionEvalua
|
||||
return false;
|
||||
}
|
||||
|
||||
private T processInternal(ParametersWrapper parameters) throws Exception {
|
||||
private T processInternal(ParametersWrapper<S, E> parameters) throws Exception {
|
||||
HandlerMethod candidate = this.findHandlerMethodForParameters(parameters);
|
||||
Assert.notNull(candidate, "No candidate methods found for messages.");
|
||||
Expression expression = candidate.getExpression();
|
||||
@@ -347,7 +347,7 @@ public class StateMachineMethodInvokerHelper<T> extends AbstractExpressionEvalua
|
||||
return targetClass;
|
||||
}
|
||||
|
||||
private HandlerMethod findHandlerMethodForParameters(ParametersWrapper parameters) {
|
||||
private HandlerMethod findHandlerMethodForParameters(ParametersWrapper<S, E> parameters) {
|
||||
if (this.handlerMethod != null) {
|
||||
return this.handlerMethod;
|
||||
} else {
|
||||
@@ -378,7 +378,7 @@ public class StateMachineMethodInvokerHelper<T> extends AbstractExpressionEvalua
|
||||
|
||||
private static final SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
|
||||
|
||||
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new LocalVariableTableParameterNameDiscoverer();
|
||||
// private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
private final Method method;
|
||||
|
||||
@@ -429,19 +429,12 @@ public class StateMachineMethodInvokerHelper<T> extends AbstractExpressionEvalua
|
||||
if (mappingAnnotation != null) {
|
||||
Class<? extends Annotation> annotationType = mappingAnnotation.annotationType();
|
||||
|
||||
// if (annotationType.equals(YarnEnvironments.class)) {
|
||||
// sb.append("environment");
|
||||
// } else if (annotationType.equals(YarnEnvironment.class)) {
|
||||
// YarnEnvironment headerAnnotation = (YarnEnvironment) mappingAnnotation;
|
||||
// sb.append(this.determineEnvironmentExpression(headerAnnotation, methodParameter));
|
||||
// } else if (annotationType.equals(YarnParameters.class)) {
|
||||
// Assert.isTrue(Map.class.isAssignableFrom(parameterType),
|
||||
// "The @YarnParameters annotation can only be applied to a Map-typed parameter.");
|
||||
// sb.append("parameters");
|
||||
// } else if (annotationType.equals(YarnParameter.class)) {
|
||||
// YarnParameter headerAnnotation = (YarnParameter) mappingAnnotation;
|
||||
// sb.append(this.determineParameterExpression(headerAnnotation, methodParameter));
|
||||
// }
|
||||
if (annotationType.equals(EventHeaders.class)) {
|
||||
sb.append("headers");
|
||||
}
|
||||
|
||||
} else if (ExtendedState.class.isAssignableFrom(parameterType)) {
|
||||
sb.append("extendedState");
|
||||
}
|
||||
}
|
||||
if (hasUnqualifiedMapParameter) {
|
||||
@@ -465,89 +458,44 @@ public class StateMachineMethodInvokerHelper<T> extends AbstractExpressionEvalua
|
||||
Annotation match = null;
|
||||
for (Annotation annotation : annotations) {
|
||||
Class<? extends Annotation> type = annotation.annotationType();
|
||||
// if (type.equals(YarnParameters.class) || type.equals(YarnParameter.class)
|
||||
// || type.equals(YarnEnvironments.class) || type.equals(YarnEnvironment.class)) {
|
||||
// if (match != null) {
|
||||
// throw new IllegalArgumentException(
|
||||
// "At most one parameter annotation can be provided for message mapping, "
|
||||
// + "but found two: [" + match.annotationType().getName() + "] and ["
|
||||
// + annotation.annotationType().getName() + "]");
|
||||
// }
|
||||
// match = annotation;
|
||||
// }
|
||||
if (type.equals(EventHeaders.class)) {
|
||||
if (match != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"At most one parameter annotation can be provided for message mapping, "
|
||||
+ "but found two: [" + match.annotationType().getName() + "] and ["
|
||||
+ annotation.annotationType().getName() + "]");
|
||||
}
|
||||
match = annotation;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
// private String determineParameterExpression(YarnParameter parameterAnnotation, MethodParameter methodParameter) {
|
||||
// methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
|
||||
// String headerName = null;
|
||||
// String relativeExpression = "";
|
||||
// String valueAttribute = parameterAnnotation.value();
|
||||
// if (!StringUtils.hasText(valueAttribute)) {
|
||||
// headerName = methodParameter.getParameterName();
|
||||
// } else if (valueAttribute.indexOf('.') != -1) {
|
||||
// String tokens[] = valueAttribute.split("\\.", 2);
|
||||
// headerName = tokens[0];
|
||||
// if (StringUtils.hasText(tokens[1])) {
|
||||
// relativeExpression = "." + tokens[1];
|
||||
// }
|
||||
// } else {
|
||||
// headerName = valueAttribute;
|
||||
// }
|
||||
// Assert.notNull(headerName, "Cannot determine parameter name. Possible reasons: -debug is "
|
||||
// + "disabled or header name is not explicitly provided via @YarnParameter annotation.");
|
||||
// String headerRetrievalExpression = "parameters['" + headerName + "']";
|
||||
// String fullHeaderExpression = headerRetrievalExpression + relativeExpression;
|
||||
// String fallbackExpression = (parameterAnnotation.required()) ? "T(org.springframework.util.Assert).isTrue(false, 'required parameter not available: "
|
||||
// + headerName + "')"
|
||||
// : "null";
|
||||
// return headerRetrievalExpression + " != null ? " + fullHeaderExpression + " : " + fallbackExpression;
|
||||
// }
|
||||
|
||||
// private String determineEnvironmentExpression(YarnEnvironment environmentAnnotation, MethodParameter methodParameter) {
|
||||
// methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
|
||||
// String headerName = null;
|
||||
// String relativeExpression = "";
|
||||
// String valueAttribute = environmentAnnotation.value();
|
||||
// if (!StringUtils.hasText(valueAttribute)) {
|
||||
// headerName = methodParameter.getParameterName();
|
||||
// } else if (valueAttribute.indexOf('.') != -1) {
|
||||
// String tokens[] = valueAttribute.split("\\.", 2);
|
||||
// headerName = tokens[0];
|
||||
// if (StringUtils.hasText(tokens[1])) {
|
||||
// relativeExpression = "." + tokens[1];
|
||||
// }
|
||||
// } else {
|
||||
// headerName = valueAttribute;
|
||||
// }
|
||||
// Assert.notNull(headerName, "Cannot determine parameter name. Possible reasons: -debug is "
|
||||
// + "disabled or header name is not explicitly provided via @YarnEnvironment annotation.");
|
||||
// String headerRetrievalExpression = "environment['" + headerName + "']";
|
||||
// String fullHeaderExpression = headerRetrievalExpression + relativeExpression;
|
||||
// String fallbackExpression = (environmentAnnotation.required()) ? "T(org.springframework.util.Assert).isTrue(false, 'required parameter not available: "
|
||||
// + headerName + "')"
|
||||
// : "null";
|
||||
// return headerRetrievalExpression + " != null ? " + fullHeaderExpression + " : " + fallbackExpression;
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping everything we need to work with spel.
|
||||
*/
|
||||
public class ParametersWrapper {
|
||||
public class ParametersWrapper<SS, EE> {
|
||||
|
||||
private final StateContext stateContext;
|
||||
|
||||
public ParametersWrapper(StateContext stateContext) {
|
||||
private final StateContext<SS, EE> stateContext;
|
||||
|
||||
public ParametersWrapper(StateContext<SS, EE> stateContext) {
|
||||
this.stateContext = stateContext;
|
||||
}
|
||||
|
||||
public StateContext getStateContext() {
|
||||
|
||||
public StateContext<SS, EE> getStateContext() {
|
||||
return stateContext;
|
||||
}
|
||||
|
||||
|
||||
public Map<String, ?> getHeaders() {
|
||||
return stateContext.getMessageHeaders();
|
||||
}
|
||||
|
||||
public ExtendedState getExtendedState() {
|
||||
return stateContext.getExtendedState();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,15 +19,35 @@ import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.statemachine.annotation.OnTransition;
|
||||
|
||||
public class StateMachineOnTransitionHandler extends StateMachineHandler {
|
||||
/**
|
||||
* Transition specific {@link StateMachineHandler}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public class StateMachineOnTransitionHandler<S, E> extends StateMachineHandler<S, E> {
|
||||
|
||||
private OnTransition annotation;
|
||||
|
||||
private final OnTransition annotation;
|
||||
|
||||
/**
|
||||
* Instantiates a new state machine on transition handler.
|
||||
*
|
||||
* @param target the target
|
||||
* @param method the method
|
||||
* @param annotation the annotation
|
||||
*/
|
||||
public StateMachineOnTransitionHandler(Object target, Method method, OnTransition annotation) {
|
||||
super(target, method);
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the annotation.
|
||||
*
|
||||
* @return the annotation
|
||||
*/
|
||||
public OnTransition getAnnotation() {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
@@ -22,9 +22,16 @@ import org.springframework.statemachine.StateContext;
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public interface StateMachineRuntime {
|
||||
public interface StateMachineRuntime<S, E> {
|
||||
|
||||
StateContext getStateContext();
|
||||
/**
|
||||
* Gets the state context.
|
||||
*
|
||||
* @return the state context
|
||||
*/
|
||||
StateContext<S, E> getStateContext();
|
||||
|
||||
}
|
||||
|
||||
@@ -21,9 +21,11 @@ package org.springframework.statemachine.processor;
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <T> type
|
||||
* @param <T> the return type
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public interface StateMachineRuntimeProcessor<T> {
|
||||
public interface StateMachineRuntimeProcessor<T, S, E> {
|
||||
|
||||
/**
|
||||
* Process the container based on information available
|
||||
@@ -32,6 +34,6 @@ public interface StateMachineRuntimeProcessor<T> {
|
||||
* @param stateMachineRuntime the yarn container runtime
|
||||
* @return the result
|
||||
*/
|
||||
T process(StateMachineRuntime stateMachineRuntime);
|
||||
T process(StateMachineRuntime<S, E> stateMachineRuntime);
|
||||
|
||||
}
|
||||
|
||||
@@ -87,6 +87,10 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
|
||||
private volatile Runnable task;
|
||||
|
||||
private final Map<String, StateMachineOnTransitionHandler<S, E>> handlers = new HashMap<String, StateMachineOnTransitionHandler<S,E>>();
|
||||
|
||||
private volatile boolean handlersInitialized;
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract state machine.
|
||||
*
|
||||
@@ -109,7 +113,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
*/
|
||||
public AbstractStateMachine(Collection<State<S, E>> states, Collection<Transition<S, E>> transitions,
|
||||
State<S, E> initialState, State<S, E> endState) {
|
||||
this(states, transitions, initialState, endState, null, null);
|
||||
this(states, transitions, initialState, endState, null, new DefaultExtendedState());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -366,6 +370,9 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
}
|
||||
|
||||
private void callHandlers(State<S,E> sourceState, State<S,E> targetState, Message<E> event) {
|
||||
|
||||
|
||||
|
||||
if (sourceState != null && targetState != null) {
|
||||
MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders(
|
||||
new HashMap<String, Object>());
|
||||
@@ -375,21 +382,21 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
|
||||
}
|
||||
|
||||
private List<Object> getStateMachineHandlerResults(List<StateMachineHandler> stateMachineHandlers, final StateContext<S, E> stateContext) {
|
||||
StateMachineRuntime runtime = new StateMachineRuntime() {
|
||||
private List<Object> getStateMachineHandlerResults(List<StateMachineHandler<S, E>> stateMachineHandlers, final StateContext<S, E> stateContext) {
|
||||
StateMachineRuntime<S, E> runtime = new StateMachineRuntime<S, E>() {
|
||||
@Override
|
||||
public StateContext<S, E> getStateContext() {
|
||||
return stateContext;
|
||||
}
|
||||
};
|
||||
List<Object> results = new ArrayList<Object>();
|
||||
for (StateMachineHandler handler : stateMachineHandlers) {
|
||||
for (StateMachineHandler<S, E> handler : stateMachineHandlers) {
|
||||
results.add(handler.handle(runtime));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<StateMachineHandler> getStateMachineHandlers(State<S, E> sourceState, State<S, E> targetState) {
|
||||
private synchronized List<StateMachineHandler<S, E>> getStateMachineHandlers(State<S, E> sourceState, State<S, E> targetState) {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
|
||||
// TODO think how to handle null bf
|
||||
@@ -397,11 +404,19 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Assert.state(beanFactory instanceof ListableBeanFactory, "Bean factory must be instance of ListableBeanFactory");
|
||||
Map<String, StateMachineOnTransitionHandler> handlers = ((ListableBeanFactory) beanFactory)
|
||||
.getBeansOfType(StateMachineOnTransitionHandler.class);
|
||||
List<StateMachineHandler> handlersList = new ArrayList<StateMachineHandler>();
|
||||
|
||||
for (Entry<String, StateMachineOnTransitionHandler> entry : handlers.entrySet()) {
|
||||
if (!handlersInitialized) {
|
||||
Map<String, StateMachineOnTransitionHandler> handlersx = ((ListableBeanFactory) beanFactory)
|
||||
.getBeansOfType(StateMachineOnTransitionHandler.class);
|
||||
for (Entry<String, StateMachineOnTransitionHandler> entry : handlersx.entrySet()) {
|
||||
handlers.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
handlersInitialized = true;
|
||||
}
|
||||
|
||||
List<StateMachineHandler<S, E>> handlersList = new ArrayList<StateMachineHandler<S, E>>();
|
||||
|
||||
for (Entry<String, StateMachineOnTransitionHandler<S, E>> entry : handlers.entrySet()) {
|
||||
OnTransition annotation = entry.getValue().getAnnotation();
|
||||
String source = annotation.source();
|
||||
String target = annotation.target();
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
package org.springframework.statemachine.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -29,6 +31,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.statemachine.AbstractStateMachineTests;
|
||||
import org.springframework.statemachine.EnumStateMachine;
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
import org.springframework.statemachine.StateMachineSystemConstants;
|
||||
import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
|
||||
@@ -59,6 +62,34 @@ public class MethodAnnotationTests extends AbstractStateMachineTests {
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testMethodAnnotations2() throws Exception {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BaseConfig.class, AnnoConfig.class, BeanConfig2.class, Config1.class);
|
||||
|
||||
EnumStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
|
||||
assertThat(context.containsBean("fooMachine"), is(true));
|
||||
machine.start();
|
||||
|
||||
Bean2 bean2 = context.getBean(Bean2.class);
|
||||
|
||||
// this event should cause 'method1' to get called
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).setHeader("foo", "jee").build());
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E2).build());
|
||||
|
||||
assertThat(bean2.onMethod1Latch.await(2, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean2.headers, notNullValue());
|
||||
assertThat((String)bean2.headers.get("foo"), is("jee"));
|
||||
assertThat(bean2.extendedState, notNullValue());
|
||||
|
||||
assertThat(bean2.onMethod2Latch.await(2, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean2.variable, notNullValue());
|
||||
assertThat((String)bean2.variable, is("jee"));
|
||||
|
||||
context.close();
|
||||
}
|
||||
|
||||
@WithStateMachine(name = StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE)
|
||||
static class Bean1 {
|
||||
|
||||
@@ -75,6 +106,30 @@ public class MethodAnnotationTests extends AbstractStateMachineTests {
|
||||
onOnTransitionFromS2ToS3Latch.countDown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@WithStateMachine(name = StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE)
|
||||
static class Bean2 {
|
||||
|
||||
CountDownLatch onMethod1Latch = new CountDownLatch(1);
|
||||
CountDownLatch onMethod2Latch = new CountDownLatch(1);
|
||||
Map<String, Object> headers;
|
||||
ExtendedState extendedState;
|
||||
Object variable;
|
||||
|
||||
@OnTransition(source = "S1", target = "S2")
|
||||
public void method1(@EventHeaders Map<String, Object> headers, ExtendedState extendedState) {
|
||||
this.headers = headers;
|
||||
extendedState.getVariables().put("foo", "jee");
|
||||
this.extendedState = extendedState;
|
||||
onMethod1Latch.countDown();
|
||||
}
|
||||
|
||||
@OnTransition(source = "S2", target = "S3")
|
||||
public void method2(@EventHeaders Map<String, Object> headers, ExtendedState extendedState) {
|
||||
variable = extendedState.getVariables().get("foo");
|
||||
onMethod2Latch.countDown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -88,6 +143,15 @@ public class MethodAnnotationTests extends AbstractStateMachineTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class BeanConfig2 {
|
||||
|
||||
@Bean
|
||||
public Bean2 bean2() {
|
||||
return new Bean2();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AnnoConfig {
|
||||
@@ -124,7 +188,12 @@ public class MethodAnnotationTests extends AbstractStateMachineTests {
|
||||
.withExternal()
|
||||
.source(TestStates.S2)
|
||||
.target(TestStates.S3)
|
||||
.event(TestEvents.E2);
|
||||
.event(TestEvents.E2)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S3)
|
||||
.target(TestStates.S4)
|
||||
.event(TestEvents.E3);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
Reference in New Issue
Block a user