Handle annotation method call errors

- Instead of halting calling annotated methods when
  one user level method throws, catch and log those
  so that all methods are called.
- Fixes #180
This commit is contained in:
Janne Valkealahti
2016-03-05 12:30:17 +00:00
parent 28a23cbf15
commit c6f56df79d
4 changed files with 123 additions and 7 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 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.
@@ -32,14 +32,32 @@ public class MethodInvokingStateMachineRuntimeProcessor<T, S, E> implements Stat
private final StateMachineMethodInvokerHelper<T, S, E> delegate;
/**
* Instantiates a new method invoking state machine runtime processor.
*
* @param targetObject the target object
* @param method the method
*/
public MethodInvokingStateMachineRuntimeProcessor(Object targetObject, Method method) {
delegate = new StateMachineMethodInvokerHelper<T, S, E>(targetObject, method);
}
/**
* Instantiates a new method invoking state machine runtime processor.
*
* @param targetObject the target object
* @param methodName the method name
*/
public MethodInvokingStateMachineRuntimeProcessor(Object targetObject, String methodName) {
delegate = new StateMachineMethodInvokerHelper<T, S, E>(targetObject, methodName);
}
/**
* Instantiates a new method invoking state machine runtime processor.
*
* @param targetObject the target object
* @param annotationType the annotation type
*/
public MethodInvokingStateMachineRuntimeProcessor(Object targetObject, Class<? extends Annotation> annotationType) {
delegate = new StateMachineMethodInvokerHelper<T, S, E>(targetObject, annotationType);
}
@@ -48,9 +66,8 @@ public class MethodInvokingStateMachineRuntimeProcessor<T, S, E> implements Stat
public T process(StateMachineRuntime<S, E> stateMachineRuntime) {
try {
return delegate.process(stateMachineRuntime);
} catch (Exception e) {
} catch (Throwable e) {
throw new RuntimeException("Error processing bean", e);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 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.
@@ -135,4 +135,8 @@ public class StateMachineHandler<T extends Annotation, S, E> implements Ordered
return processor.process(stateMachineRuntime);
}
@Override
public String toString() {
return "StateMachineHandler [beanClass=" + beanClass + "]";
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 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.
@@ -356,7 +356,11 @@ public class StateMachineHandlerCallHelper<S, E> implements InitializingBean, Be
};
List<Object> results = new ArrayList<Object>();
for (StateMachineHandler<? extends Annotation, S, E> handler : stateMachineHandlers) {
results.add(handler.handle(runtime));
try {
results.add(handler.handle(runtime));
} catch (Throwable e) {
log.error("Error processing handler " + handler, e);
}
}
return results;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 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.
@@ -15,6 +15,7 @@
*/
package org.springframework.statemachine.processor;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
@@ -155,6 +156,25 @@ public class AnnotatedMethodTests extends AbstractStateMachineTests {
assertThat(bean2.onMethod0Latch.await(2, TimeUnit.SECONDS), is(true));
}
@Test
@SuppressWarnings("unchecked")
public void testMethodsThrowDoesNotBreakMachine() throws Exception {
context.register(Config6.class, BeanConfig3.class);
context.refresh();
ObjectStateMachine<TestStates,TestEvents> machine =
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
Bean3 bean3 = context.getBean(Bean3.class);
machine.start();
assertThat(bean3.onMethod0Latch.await(2, TimeUnit.SECONDS), is(true));
machine.sendEvent(TestEvents.E1);
assertThat(bean3.onMethod1Latch.await(2, TimeUnit.SECONDS), is(true));
assertThat(bean3.onMethod11Latch.await(2, TimeUnit.SECONDS), is(true));
assertThat(machine.getState().getIds(), containsInAnyOrder(TestStates.S2));
machine.sendEvent(TestEvents.E2);
assertThat(bean3.onMethod2Latch.await(2, TimeUnit.SECONDS), is(true));
assertThat(machine.getState().getIds(), containsInAnyOrder(TestStates.S3));
}
@WithStateMachine
static class Bean1 {
@@ -249,6 +269,39 @@ public class AnnotatedMethodTests extends AbstractStateMachineTests {
}
}
@WithStateMachine
static class Bean3 {
CountDownLatch onMethod0Latch = new CountDownLatch(1);
CountDownLatch onMethod1Latch = new CountDownLatch(1);
CountDownLatch onMethod11Latch = new CountDownLatch(1);
CountDownLatch onMethod2Latch = new CountDownLatch(1);
@OnTransition(target = "S1")
public void method0() {
onMethod0Latch.countDown();
throw new RuntimeException();
}
@OnTransition(source = "S1", target = "S2")
public void method1() {
onMethod1Latch.countDown();
throw new Error();
}
@OnTransition(source = "S1", target = "S2")
public void method11() {
onMethod11Latch.countDown();
throw new Error();
}
@OnTransition(source = "S2", target = "S3")
public void method2() {
onMethod2Latch.countDown();
throw new RuntimeException();
}
}
@Configuration
static class BeanConfig1 {
@@ -269,6 +322,16 @@ public class AnnotatedMethodTests extends AbstractStateMachineTests {
}
@Configuration
static class BeanConfig3 {
@Bean
public Bean3 bean3() {
return new Bean3();
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@OnTransition
@@ -493,4 +556,32 @@ public class AnnotatedMethodTests extends AbstractStateMachineTests {
}
@Configuration
@EnableStateMachine
static class Config6 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
@Override
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
states
.withStates()
.initial(TestStates.S1)
.states(EnumSet.allOf(TestStates.class));
}
@Override
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
transitions
.withExternal()
.source(TestStates.S1)
.target(TestStates.S2)
.event(TestEvents.E1)
.and()
.withExternal()
.source(TestStates.S2)
.target(TestStates.S3)
.event(TestEvents.E2);
}
}
}