From de848cda2073c9b6fc6ebf4290261b1f34a2be59 Mon Sep 17 00:00:00 2001 From: Janne Valkealahti Date: Sun, 1 Mar 2015 17:52:54 +0000 Subject: [PATCH] Event publish and listener changes --- .../statemachine/StateMachine.java | 2 +- .../DefaultStateMachineEventPublisher.java | 36 +++++++++--- .../statemachine/event/LoggingListener.java | 6 +- .../event/OnStateChangedEvent.java | 11 +++- .../event/OnTransitionEndEvent.java | 44 +++++++++++++++ .../statemachine/event/OnTransitionEvent.java | 44 +++++++++++++++ .../event/OnTransitionStartEvent.java | 44 +++++++++++++++ ...chineEvent.java => StateMachineEvent.java} | 6 +- .../event/StateMachineEventPublisher.java | 27 ++++++++- .../statemachine/event/TransitionEvent.java | 56 +++++++++++++++++++ .../CompositeStateMachineListener.java | 41 ++++++++++++-- .../listener/StateMachineListener.java | 40 ++++++++++++- .../support/AbstractStateMachine.java | 31 +++++++++- .../event/StateMachineEventTests.java | 12 ++-- .../statemachine/listener/ListenerTests.java | 15 ++++- 15 files changed, 384 insertions(+), 31 deletions(-) create mode 100644 spring-statemachine-core/src/main/java/org/springframework/statemachine/event/OnTransitionEndEvent.java create mode 100644 spring-statemachine-core/src/main/java/org/springframework/statemachine/event/OnTransitionEvent.java create mode 100644 spring-statemachine-core/src/main/java/org/springframework/statemachine/event/OnTransitionStartEvent.java rename spring-statemachine-core/src/main/java/org/springframework/statemachine/event/{AbstractStateMachineEvent.java => StateMachineEvent.java} (85%) create mode 100644 spring-statemachine-core/src/main/java/org/springframework/statemachine/event/TransitionEvent.java diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/StateMachine.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/StateMachine.java index 98386856..19b4238c 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/StateMachine.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/StateMachine.java @@ -42,6 +42,6 @@ public interface StateMachine extends Region { * * @param listener the listener */ - void addStateListener(StateMachineListener, E> listener); + void addStateListener(StateMachineListener listener); } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/DefaultStateMachineEventPublisher.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/DefaultStateMachineEventPublisher.java index b20ac526..81220d06 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/DefaultStateMachineEventPublisher.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/DefaultStateMachineEventPublisher.java @@ -18,10 +18,11 @@ package org.springframework.statemachine.event; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.statemachine.state.State; +import org.springframework.statemachine.transition.Transition; /** * Default implementation of {@link StateMachineEventPublisher}. - * + * * @author Janne Valkealahti * */ @@ -30,20 +31,25 @@ public class DefaultStateMachineEventPublisher implements StateMachineEventPubli private ApplicationEventPublisher applicationEventPublisher; /** - * Instantiates a new leader event publisher. + * Instantiates a new state machine event publisher. */ public DefaultStateMachineEventPublisher() { } /** - * Instantiates a new leader event publisher. - * + * Instantiates a new state machine event publisher. + * * @param applicationEventPublisher the application event publisher */ public DefaultStateMachineEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + this.applicationEventPublisher = applicationEventPublisher; + } + @Override public void publishStateChanged(Object source, State sourceState, State targetState) { if (applicationEventPublisher != null) { @@ -52,8 +58,24 @@ public class DefaultStateMachineEventPublisher implements StateMachineEventPubli } @Override - public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { - this.applicationEventPublisher = applicationEventPublisher; + public void publishTransitionStart(Object source, Transition transition) { + if (applicationEventPublisher != null) { + applicationEventPublisher.publishEvent(new OnTransitionStartEvent(source, transition)); + } } - + + @Override + public void publishTransitionEnd(Object source, Transition transition) { + if (applicationEventPublisher != null) { + applicationEventPublisher.publishEvent(new OnTransitionEndEvent(source, transition)); + } + } + + @Override + public void publishTransition(Object source, Transition transition) { + if (applicationEventPublisher != null) { + applicationEventPublisher.publishEvent(new OnTransitionEvent(source, transition)); + } + } + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/LoggingListener.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/LoggingListener.java index da630f01..946c0a2d 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/LoggingListener.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/LoggingListener.java @@ -22,13 +22,13 @@ import org.springframework.util.StringUtils; /** * Simple {@link ApplicationListener} which logs all events - * based on {@link AbstractStateMachineEvent} using a log level + * based on {@link StateMachineEvent} using a log level * set during the construction. * * @author Janne Valkealahti * */ -public class LoggingListener implements ApplicationListener { +public class LoggingListener implements ApplicationListener { private static final Log log = LogFactory.getLog(LoggingListener.class); @@ -64,7 +64,7 @@ public class LoggingListener implements ApplicationListener sourceState; private final State targetState; - + /** * Instantiates a new granted event. - * + * * @param source the component that published the event (never {@code null}) */ public OnStateChangedEvent(Object source, State sourceState, State targetState) { @@ -58,4 +58,9 @@ public class OnStateChangedEvent extends AbstractStateMachineEvent { return targetState; } + @Override + public String toString() { + return "OnStateChangedEvent [sourceState=" + sourceState + ", targetState=" + targetState + "]"; + } + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/OnTransitionEndEvent.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/OnTransitionEndEvent.java new file mode 100644 index 00000000..c1a5745f --- /dev/null +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/OnTransitionEndEvent.java @@ -0,0 +1,44 @@ +/* + * 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.statemachine.transition.Transition; + +/** + * Event representing that a {@link Transition} has ended. + * + * @author Janne Valkealahti + * + */ +@SuppressWarnings("serial") +public class OnTransitionEndEvent extends TransitionEvent { + + /** + * Instantiates a new on transition end event. + * + * @param source the source + * @param transition the transition + */ + public OnTransitionEndEvent(Object source, Transition transition) { + super(source, transition); + } + + @Override + public String toString() { + return "OnTransitionEndEvent [transition=" + getTransition() + "]"; + } + +} diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/OnTransitionEvent.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/OnTransitionEvent.java new file mode 100644 index 00000000..d0ae36f6 --- /dev/null +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/OnTransitionEvent.java @@ -0,0 +1,44 @@ +/* + * 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.statemachine.transition.Transition; + +/** + * Event representing that a {@link Transition} has happened. + * + * @author Janne Valkealahti + * + */ +@SuppressWarnings("serial") +public class OnTransitionEvent extends TransitionEvent { + + /** + * Instantiates a new on transition event. + * + * @param source the source + * @param transition the transition + */ + public OnTransitionEvent(Object source, Transition transition) { + super(source, transition); + } + + @Override + public String toString() { + return "OnTransitionEvent [transition=" + getTransition() + "]"; + } + +} diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/OnTransitionStartEvent.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/OnTransitionStartEvent.java new file mode 100644 index 00000000..3a0ee11d --- /dev/null +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/OnTransitionStartEvent.java @@ -0,0 +1,44 @@ +/* + * 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.statemachine.transition.Transition; + +/** + * Event representing that a {@link Transition} has started. + * + * @author Janne Valkealahti + * + */ +@SuppressWarnings("serial") +public class OnTransitionStartEvent extends TransitionEvent { + + /** + * Instantiates a new on transition start event. + * + * @param source the source + * @param transition the transition + */ + public OnTransitionStartEvent(Object source, Transition transition) { + super(source, transition); + } + + @Override + public String toString() { + return "OnTransitionStartEvent [transition=" + getTransition() + "]"; + } + +} diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/AbstractStateMachineEvent.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/StateMachineEvent.java similarity index 85% rename from spring-statemachine-core/src/main/java/org/springframework/statemachine/event/AbstractStateMachineEvent.java rename to spring-statemachine-core/src/main/java/org/springframework/statemachine/event/StateMachineEvent.java index 418fea45..703344fa 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/AbstractStateMachineEvent.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/StateMachineEvent.java @@ -25,20 +25,20 @@ import org.springframework.context.ApplicationEvent; * */ @SuppressWarnings("serial") -public abstract class AbstractStateMachineEvent extends ApplicationEvent { +public abstract class StateMachineEvent extends ApplicationEvent { /** * Create a new ApplicationEvent. * * @param source the component that published the event (never {@code null}) */ - public AbstractStateMachineEvent(Object source) { + public StateMachineEvent(Object source) { super(source); } @Override public String toString() { - return "AbstractLeaderEvent [source=" + source + "]"; + return "AbstractStateMachineEvent [source=" + source + "]"; } } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/StateMachineEventPublisher.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/StateMachineEventPublisher.java index 22814270..9f935033 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/StateMachineEventPublisher.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/StateMachineEventPublisher.java @@ -16,6 +16,7 @@ package org.springframework.statemachine.event; import org.springframework.statemachine.state.State; +import org.springframework.statemachine.transition.Transition; /** * Interface for publishing state machine based application events. @@ -33,5 +34,29 @@ public interface StateMachineEventPublisher { * @param targetState the target state */ void publishStateChanged(Object source, State sourceState, State targetState); - + + /** + * Publish a transition start event. + * + * @param source the source + * @param transition the transition + */ + void publishTransitionStart(Object source, Transition transition); + + /** + * Publish a transition end event. + * + * @param source the source + * @param transition the transition + */ + void publishTransitionEnd(Object source, Transition transition); + + /** + * Publish a transition event. + * + * @param source the source + * @param transition the transition + */ + void publishTransition(Object source, Transition transition); + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/TransitionEvent.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/TransitionEvent.java new file mode 100644 index 00000000..b85f235b --- /dev/null +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/event/TransitionEvent.java @@ -0,0 +1,56 @@ +/* + * 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.statemachine.transition.Transition; + +/** + * Generic base event representing something with a {@link Transition}. + * + * @author Janne Valkealahti + * + */ +@SuppressWarnings("serial") +public abstract class TransitionEvent extends StateMachineEvent { + + private final Transition transition; + + /** + * Instantiates a new transition event. + * + * @param source the source + * @param transition the transition + */ + public TransitionEvent(Object source, Transition transition) { + super(source); + this.transition = transition; + } + + /** + * Gets the transition. + * + * @return the transition + */ + public Transition getTransition() { + return transition; + } + + @Override + public String toString() { + return "OnTransitionStartEvent [transition=" + transition + "]"; + } + +} diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/CompositeStateMachineListener.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/CompositeStateMachineListener.java index 263da314..9906ff7b 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/CompositeStateMachineListener.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/CompositeStateMachineListener.java @@ -18,16 +18,49 @@ package org.springframework.statemachine.listener; import java.util.Iterator; import org.springframework.statemachine.state.State; +import org.springframework.statemachine.transition.Transition; -public class CompositeStateMachineListener extends AbstractCompositeListener, E>> implements - StateMachineListener, E> { +/** + * Default {@link StateMachineListener} dispatcher. + * + * @author Janne Valkealahti + * + * @param the type of state + * @param the type of event + */ +public class CompositeStateMachineListener extends AbstractCompositeListener> implements + StateMachineListener { @Override public void stateChanged(State from, State to) { - for (Iterator, E>> iterator = getListeners().reverse(); iterator.hasNext();) { - StateMachineListener, E> listener = iterator.next(); + for (Iterator> iterator = getListeners().reverse(); iterator.hasNext();) { + StateMachineListener listener = iterator.next(); listener.stateChanged(from, to); } } + @Override + public void transition(Transition transition) { + for (Iterator> iterator = getListeners().reverse(); iterator.hasNext();) { + StateMachineListener listener = iterator.next(); + listener.transition(transition); + } + } + + @Override + public void transitionStarted(Transition transition) { + for (Iterator> iterator = getListeners().reverse(); iterator.hasNext();) { + StateMachineListener listener = iterator.next(); + listener.transitionStarted(transition); + } + } + + @Override + public void transitionEnded(Transition transition) { + for (Iterator> iterator = getListeners().reverse(); iterator.hasNext();) { + StateMachineListener listener = iterator.next(); + listener.transitionEnded(transition); + } + } + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/StateMachineListener.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/StateMachineListener.java index eec8ffe6..a0e97acb 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/StateMachineListener.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/listener/StateMachineListener.java @@ -15,8 +15,46 @@ */ package org.springframework.statemachine.listener; +import org.springframework.statemachine.state.State; +import org.springframework.statemachine.transition.Transition; + +/** + * {@code StateMachineListener} for various state machine events. + * + * @author Janne Valkealahti + * + * @param the type of state + * @param the type of event + */ public interface StateMachineListener { - void stateChanged(S from, S to); + /** + * Notified when state is changed. + * + * @param from the source state + * @param to the target state + */ + void stateChanged(State from, State to); + + /** + * Notified when transition happened. + * + * @param transition the transition + */ + void transition(Transition transition); + + /** + * Notified when transition started. + * + * @param transition the transition + */ + void transitionStarted(Transition transition); + + /** + * Notified when transition ended. + * + * @param transition the transition + */ + void transitionEnded(Transition transition); } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java index 5cafcdb4..8659284d 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java @@ -158,7 +158,7 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport @Override public boolean sendEvent(Message event) { - if (isComplete()) { + if (isComplete() || !isRunning()) { return false; } // TODO: machine header looks weird! @@ -199,7 +199,7 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport } @Override - public void addStateListener(StateMachineListener, E> listener) { + public void addStateListener(StateMachineListener listener) { stateListener.register(listener); } @@ -308,10 +308,13 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport if (StateMachineUtils.containsAtleastOne(source.getIds(), currentState.getIds())) { if (trigger != null && trigger.evaluate(queuedEvent.getPayload())) { StateContext stateContext = new DefaultStateContext(queuedEvent.getHeaders(), extendedState, transition); + notifyTransitionStart(transition); boolean transit = transition.transit(stateContext); if (transit && transition.getKind() != TransitionKind.INTERNAL) { switchToState(target, queuedEvent, transition); + notifyTransition(transition); } + notifyTransitionEnd(transition); break; } else if (source.getDeferredEvents() != null && source.getDeferredEvents().contains(queuedEvent.getPayload())) { defer = queuedEvent; @@ -423,4 +426,28 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport } } + private void notifyTransitionStart(Transition transition) { + stateListener.transitionStarted(transition); + StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); + if (eventPublisher != null) { + eventPublisher.publishTransitionStart(this, transition); + } + } + + private void notifyTransition(Transition transition) { + stateListener.transition(transition); + StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); + if (eventPublisher != null) { + eventPublisher.publishTransitionEnd(this, transition); + } + } + + private void notifyTransitionEnd(Transition transition) { + stateListener.transitionEnded(transition); + StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); + if (eventPublisher != null) { + eventPublisher.publishTransition(this, transition); + } + } + } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/event/StateMachineEventTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/event/StateMachineEventTests.java index fec8b8d3..04f440b4 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/event/StateMachineEventTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/event/StateMachineEventTests.java @@ -183,16 +183,18 @@ public class StateMachineEventTests extends AbstractStateMachineTests { } - static class TestEventListener implements ApplicationListener { + static class TestEventListener implements ApplicationListener { CountDownLatch onEventLatch = new CountDownLatch(6); - ArrayList events = new ArrayList(); + ArrayList events = new ArrayList(); @Override - public void onApplicationEvent(AbstractStateMachineEvent event) { - events.add(event); - onEventLatch.countDown(); + public void onApplicationEvent(StateMachineEvent event) { + if (event instanceof OnStateChangedEvent) { + events.add(event); + onEventLatch.countDown(); + } } } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/listener/ListenerTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/listener/ListenerTests.java index 0902c56e..c039aba1 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/listener/ListenerTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/listener/ListenerTests.java @@ -42,6 +42,7 @@ import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; import org.springframework.statemachine.state.State; +import org.springframework.statemachine.transition.Transition; /** * Tests for state machine listener functionality. @@ -95,7 +96,7 @@ public class ListenerTests extends AbstractStateMachineTests { } - private static class TestStateMachineListener implements StateMachineListener, TestEvents> { + private static class TestStateMachineListener implements StateMachineListener { ArrayList states = new ArrayList(); @@ -113,6 +114,18 @@ public class ListenerTests extends AbstractStateMachineTests { } } + @Override + public void transition(Transition transition) { + } + + @Override + public void transitionStarted(Transition transition) { + } + + @Override + public void transitionEnded(Transition transition) { + } + } @Configuration