Event publish and listener changes

This commit is contained in:
Janne Valkealahti
2015-03-01 17:52:54 +00:00
parent 2ceca88e6a
commit de848cda20
15 changed files with 384 additions and 31 deletions

View File

@@ -42,6 +42,6 @@ public interface StateMachine<S, E> extends Region<S, E> {
*
* @param listener the listener
*/
void addStateListener(StateMachineListener<State<S,E>, E> listener);
void addStateListener(StateMachineListener<S, E> listener);
}

View File

@@ -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));
}
}
}

View File

@@ -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<AbstractStateMachineEvent> {
public class LoggingListener implements ApplicationListener<StateMachineEvent> {
private static final Log log = LogFactory.getLog(LoggingListener.class);
@@ -64,7 +64,7 @@ public class LoggingListener implements ApplicationListener<AbstractStateMachine
}
@Override
public void onApplicationEvent(AbstractStateMachineEvent event) {
public void onApplicationEvent(StateMachineEvent event) {
switch (this.level) {
case FATAL:
if (log.isFatalEnabled()) {

View File

@@ -24,14 +24,14 @@ import org.springframework.statemachine.state.State;
*
*/
@SuppressWarnings("serial")
public class OnStateChangedEvent extends AbstractStateMachineEvent {
public class OnStateChangedEvent extends StateMachineEvent {
private final State<?, ?> 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 + "]";
}
}

View File

@@ -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() + "]";
}
}

View File

@@ -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() + "]";
}
}

View File

@@ -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() + "]";
}
}

View File

@@ -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 + "]";
}
}

View File

@@ -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);
}

View File

@@ -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 + "]";
}
}

View File

@@ -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<S,E> extends AbstractCompositeListener<StateMachineListener<State<S,E>, E>> implements
StateMachineListener<State<S,E>, E> {
/**
* Default {@link StateMachineListener} dispatcher.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public class CompositeStateMachineListener<S,E> extends AbstractCompositeListener<StateMachineListener<S,E>> implements
StateMachineListener<S, E> {
@Override
public void stateChanged(State<S, E> from, State<S, E> to) {
for (Iterator<StateMachineListener<State<S,E>, E>> iterator = getListeners().reverse(); iterator.hasNext();) {
StateMachineListener<State<S,E>, E> listener = iterator.next();
for (Iterator<StateMachineListener<S, E>> iterator = getListeners().reverse(); iterator.hasNext();) {
StateMachineListener<S, E> listener = iterator.next();
listener.stateChanged(from, to);
}
}
@Override
public void transition(Transition<S, E> transition) {
for (Iterator<StateMachineListener<S, E>> iterator = getListeners().reverse(); iterator.hasNext();) {
StateMachineListener<S, E> listener = iterator.next();
listener.transition(transition);
}
}
@Override
public void transitionStarted(Transition<S, E> transition) {
for (Iterator<StateMachineListener<S, E>> iterator = getListeners().reverse(); iterator.hasNext();) {
StateMachineListener<S, E> listener = iterator.next();
listener.transitionStarted(transition);
}
}
@Override
public void transitionEnded(Transition<S, E> transition) {
for (Iterator<StateMachineListener<S, E>> iterator = getListeners().reverse(); iterator.hasNext();) {
StateMachineListener<S, E> listener = iterator.next();
listener.transitionEnded(transition);
}
}
}

View File

@@ -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 <S> the type of state
* @param <E> the type of event
*/
public interface StateMachineListener<S,E> {
void stateChanged(S from, S to);
/**
* Notified when state is changed.
*
* @param from the source state
* @param to the target state
*/
void stateChanged(State<S,E> from, State<S,E> to);
/**
* Notified when transition happened.
*
* @param transition the transition
*/
void transition(Transition<S, E> transition);
/**
* Notified when transition started.
*
* @param transition the transition
*/
void transitionStarted(Transition<S, E> transition);
/**
* Notified when transition ended.
*
* @param transition the transition
*/
void transitionEnded(Transition<S, E> transition);
}

View File

@@ -158,7 +158,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
@Override
public boolean sendEvent(Message<E> event) {
if (isComplete()) {
if (isComplete() || !isRunning()) {
return false;
}
// TODO: machine header looks weird!
@@ -199,7 +199,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
}
@Override
public void addStateListener(StateMachineListener<State<S, E>, E> listener) {
public void addStateListener(StateMachineListener<S, E> listener) {
stateListener.register(listener);
}
@@ -308,10 +308,13 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
if (StateMachineUtils.containsAtleastOne(source.getIds(), currentState.getIds())) {
if (trigger != null && trigger.evaluate(queuedEvent.getPayload())) {
StateContext<S, E> stateContext = new DefaultStateContext<S, E>(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<S, E> extends LifecycleObjectSupport
}
}
private void notifyTransitionStart(Transition<S,E> transition) {
stateListener.transitionStarted(transition);
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
if (eventPublisher != null) {
eventPublisher.publishTransitionStart(this, transition);
}
}
private void notifyTransition(Transition<S,E> transition) {
stateListener.transition(transition);
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
if (eventPublisher != null) {
eventPublisher.publishTransitionEnd(this, transition);
}
}
private void notifyTransitionEnd(Transition<S,E> transition) {
stateListener.transitionEnded(transition);
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
if (eventPublisher != null) {
eventPublisher.publishTransition(this, transition);
}
}
}

View File

@@ -183,16 +183,18 @@ public class StateMachineEventTests extends AbstractStateMachineTests {
}
static class TestEventListener implements ApplicationListener<AbstractStateMachineEvent> {
static class TestEventListener implements ApplicationListener<StateMachineEvent> {
CountDownLatch onEventLatch = new CountDownLatch(6);
ArrayList<AbstractStateMachineEvent> events = new ArrayList<AbstractStateMachineEvent>();
ArrayList<StateMachineEvent> events = new ArrayList<StateMachineEvent>();
@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();
}
}
}

View File

@@ -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<State<TestStates, TestEvents>, TestEvents> {
private static class TestStateMachineListener implements StateMachineListener<TestStates, TestEvents> {
ArrayList<Holder> states = new ArrayList<Holder>();
@@ -113,6 +114,18 @@ public class ListenerTests extends AbstractStateMachineTests {
}
}
@Override
public void transition(Transition<TestStates, TestEvents> transition) {
}
@Override
public void transitionStarted(Transition<TestStates, TestEvents> transition) {
}
@Override
public void transitionEnded(Transition<TestStates, TestEvents> transition) {
}
}
@Configuration