Add event if event is not accepted
- OnStateNotAcceptedEvent is fired via context if event is not accepted. - eventNotAccepted called via StateMachineListener if event is not accepted. - Test in StateMachineEventTests.testEventNotAccepted() - Fixes #43
This commit is contained in:
@@ -17,6 +17,7 @@ package org.springframework.statemachine.event;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
@@ -72,6 +73,13 @@ public class DefaultStateMachineEventPublisher implements StateMachineEventPubli
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishEventNotAccepted(Object source, Message<?> event) {
|
||||
if (applicationEventPublisher != null) {
|
||||
applicationEventPublisher.publishEvent(new OnEventNotAcceptedEvent(source, event));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishTransitionStart(Object source, Transition<?, ?> transition) {
|
||||
if (applicationEventPublisher != null) {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.event;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* Generic event representing that a state machine did not
|
||||
* accept an event.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class OnEventNotAcceptedEvent extends StateMachineEvent {
|
||||
|
||||
private final Message<?> event;
|
||||
|
||||
/**
|
||||
* Instantiates a new on state not accepted event.
|
||||
*
|
||||
* @param source the source
|
||||
* @param event the event
|
||||
*/
|
||||
public OnEventNotAcceptedEvent(Object source, Message<?> event) {
|
||||
super(source);
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the event.
|
||||
*
|
||||
* @return the event
|
||||
*/
|
||||
public Message<?> getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OnStateNotAcceptedEvent [event=" + event + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.statemachine.event;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
@@ -52,6 +53,14 @@ public interface StateMachineEventPublisher {
|
||||
*/
|
||||
void publishStateExited(Object source, State<?, ?> state);
|
||||
|
||||
/**
|
||||
* Publish event not accepted event.
|
||||
*
|
||||
* @param source the source
|
||||
* @param event the event
|
||||
*/
|
||||
void publishEventNotAccepted(Object source, Message<?> event);
|
||||
|
||||
/**
|
||||
* Publish a transition start event.
|
||||
*
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.statemachine.listener;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
@@ -56,6 +57,14 @@ public class CompositeStateMachineListener<S,E> extends AbstractCompositeListene
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventNotAccepted(Message<E> event) {
|
||||
for (Iterator<StateMachineListener<S, E>> iterator = getListeners().reverse(); iterator.hasNext();) {
|
||||
StateMachineListener<S, E> listener = iterator.next();
|
||||
listener.eventNotAccepted(event);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transition(Transition<S, E> transition) {
|
||||
for (Iterator<StateMachineListener<S, E>> iterator = getListeners().reverse(); iterator.hasNext();) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.statemachine.listener;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
@@ -51,6 +52,13 @@ public interface StateMachineListener<S,E> {
|
||||
*/
|
||||
void stateExited(State<S,E> state);
|
||||
|
||||
/**
|
||||
* Notified when event was not accepted.
|
||||
*
|
||||
* @param event the event
|
||||
*/
|
||||
void eventNotAccepted(Message<E> event);
|
||||
|
||||
/**
|
||||
* Notified when transition happened.
|
||||
*
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.statemachine.listener;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
@@ -42,6 +43,10 @@ public class StateMachineListenerAdapter<S, E> implements StateMachineListener<S
|
||||
public void stateExited(State<S, E> state) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventNotAccepted(Message<E> event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transition(Transition<S, E> transition) {
|
||||
}
|
||||
|
||||
@@ -182,10 +182,14 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
@Override
|
||||
public boolean sendEvent(Message<E> event) {
|
||||
if (isComplete() || !isRunning()) {
|
||||
notifyEventNotAccepted(event);
|
||||
return false;
|
||||
}
|
||||
boolean accepted = acceptEvent(event);
|
||||
stateMachineExecutor.execute();
|
||||
if (!accepted) {
|
||||
notifyEventNotAccepted(event);
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
|
||||
|
||||
@@ -125,6 +125,16 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyEventNotAccepted(Message<E> event) {
|
||||
stateListener.eventNotAccepted(event);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishEventNotAccepted(this, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyTransitionStart(Transition<S,E> transition) {
|
||||
stateListener.transitionStarted(transition);
|
||||
if (contextEventsEnabled) {
|
||||
@@ -217,6 +227,11 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
stateListener.stateExited(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventNotAccepted(Message<E> event) {
|
||||
stateListener.eventNotAccepted(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transition(Transition<S, E> transition) {
|
||||
stateListener.transition(transition);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.statemachine.event;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
@@ -31,6 +32,7 @@ import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.AbstractStateMachineTests;
|
||||
import org.springframework.statemachine.ObjectStateMachine;
|
||||
import org.springframework.statemachine.StateMachineSystemConstants;
|
||||
@@ -38,6 +40,7 @@ import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
|
||||
|
||||
/**
|
||||
* Tests from state machine app context events.
|
||||
@@ -72,7 +75,37 @@ public class StateMachineEventTests extends AbstractStateMachineTests {
|
||||
// 6 events instead of 5, first one is initial transition
|
||||
// to SI where source state is null
|
||||
assertThat(listener.onEventLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||
assertThat(listener.events.size(), is(6));
|
||||
assertThat(listener.stateChangedEvents.size(), is(6));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEventNotAccepted() throws Exception {
|
||||
context.register(BaseConfig.class, StateMachineEventPublisherConfiguration.class, Config1.class);
|
||||
context.refresh();
|
||||
assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
|
||||
TestEventListener eventListener = context.getBean(TestEventListener.class);
|
||||
TestListener listener = new TestListener();
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
eventListener.reset(2, 0);
|
||||
machine.addStateListener(listener);
|
||||
machine.start();
|
||||
assertThat(machine, notNullValue());
|
||||
machine.sendEvent(TestEvents.E1);
|
||||
|
||||
assertThat(eventListener.onEventLatch.await(2, TimeUnit.SECONDS), is(true));
|
||||
assertThat(eventListener.stateChangedEvents.size(), is(2));
|
||||
|
||||
eventListener.reset(0, 1);
|
||||
machine.sendEvent(TestEvents.E3);
|
||||
assertThat(eventListener.onEventLatch.await(2, TimeUnit.SECONDS), is(true));
|
||||
assertThat(eventListener.eventNotAcceptedEvents.size(), is(1));
|
||||
assertThat(eventListener.eventNotAcceptedEvents.get(0), instanceOf(OnEventNotAcceptedEvent.class));
|
||||
assertThat(((OnEventNotAcceptedEvent)eventListener.eventNotAcceptedEvents.get(0)).getEvent().getPayload(), is(TestEvents.E3));
|
||||
|
||||
assertThat(listener.eventNotAcceptedLatch.await(2, TimeUnit.SECONDS), is(true));
|
||||
assertThat(listener.eventNotAccepted.size(), is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -185,18 +218,42 @@ public class StateMachineEventTests extends AbstractStateMachineTests {
|
||||
|
||||
static class TestEventListener implements ApplicationListener<StateMachineEvent> {
|
||||
|
||||
CountDownLatch onEventLatch = new CountDownLatch(6);
|
||||
volatile CountDownLatch onEventLatch = new CountDownLatch(6);
|
||||
|
||||
ArrayList<StateMachineEvent> events = new ArrayList<StateMachineEvent>();
|
||||
volatile ArrayList<StateMachineEvent> stateChangedEvents = new ArrayList<StateMachineEvent>();
|
||||
volatile ArrayList<StateMachineEvent> eventNotAcceptedEvents = new ArrayList<StateMachineEvent>();
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(StateMachineEvent event) {
|
||||
if (event instanceof OnStateChangedEvent) {
|
||||
events.add(event);
|
||||
stateChangedEvents.add(event);
|
||||
onEventLatch.countDown();
|
||||
} else if (event instanceof OnEventNotAcceptedEvent) {
|
||||
eventNotAcceptedEvents.add(event);
|
||||
onEventLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
public void reset(int c1,int c2) {
|
||||
onEventLatch = new CountDownLatch(c1);
|
||||
eventNotAcceptedEvents = new ArrayList<StateMachineEvent>();
|
||||
stateChangedEvents.clear();
|
||||
eventNotAcceptedEvents.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestListener extends StateMachineListenerAdapter<TestStates, TestEvents> {
|
||||
|
||||
volatile CountDownLatch eventNotAcceptedLatch = new CountDownLatch(1);
|
||||
volatile ArrayList<Message<TestEvents>> eventNotAccepted = new ArrayList<Message<TestEvents>>();
|
||||
|
||||
@Override
|
||||
public void eventNotAccepted(Message<TestEvents> event) {
|
||||
eventNotAccepted.add(event);
|
||||
eventNotAcceptedLatch.countDown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.statemachine.AbstractStateMachineTests;
|
||||
import org.springframework.statemachine.ObjectStateMachine;
|
||||
@@ -148,6 +149,10 @@ public class ListenerTests extends AbstractStateMachineTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventNotAccepted(Message<TestEvents> event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transition(Transition<TestStates, TestEvents> transition) {
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user