/*
* 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.recipes.persist;
import java.util.Iterator;
import java.util.List;
import org.springframework.messaging.Message;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.access.StateMachineAccess;
import org.springframework.statemachine.access.StateMachineFunction;
import org.springframework.statemachine.listener.AbstractCompositeListener;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.support.DefaultStateMachineContext;
import org.springframework.statemachine.support.LifecycleObjectSupport;
import org.springframework.statemachine.support.StateMachineInterceptorAdapter;
import org.springframework.statemachine.transition.Transition;
import org.springframework.util.Assert;
/**
* {@code PersistStateMachineHandler} is a recipe which can be used to
* handle a state change of an arbitrary entity in a persistent storage.
*
* @author Janne Valkealahti
*
*/
public class PersistStateMachineHandler extends LifecycleObjectSupport {
private final StateMachine<String, String> stateMachine;
private final PersistingStateChangeInterceptor interceptor = new PersistingStateChangeInterceptor();
private final CompositePersistStateChangeListener listeners = new CompositePersistStateChangeListener();
/**
* Instantiates a new persist state machine handler.
*
* @param stateMachine the state machine
*/
public PersistStateMachineHandler(StateMachine<String, String> stateMachine) {
Assert.notNull(stateMachine, "State machine must be set");
this.stateMachine = stateMachine;
}
@Override
protected void onInit() throws Exception {
stateMachine.getStateMachineAccessor().doWithAllRegions(new StateMachineFunction<StateMachineAccess<String,String>>() {
@Override
public void apply(StateMachineAccess<String, String> function) {
function.addStateMachineInterceptor(interceptor);
}
});
}
/**
* Handle event with entity.
*
* @param event the event
* @param state the state
* @return true if event was accepted
*/
public boolean handleEventWithState(Message<String> event, String state) {
stateMachine.stop();
List<StateMachineAccess<String, String>> withAllRegions = stateMachine.getStateMachineAccessor().withAllRegions();
for (StateMachineAccess<String, String> a : withAllRegions) {
a.resetStateMachine(new DefaultStateMachineContext<String, String>(state, null, null, null));
}
stateMachine.start();
return stateMachine.sendEvent(event);
}
/**
* Adds the persist state change listener.
*
* @param listener the listener
*/
public void addPersistStateChangeListener(PersistStateChangeListener listener) {
listeners.register(listener);
}
/**
* The listener interface for receiving persistStateChange events.
* The class that is interested in processing a persistStateChange
* event implements this interface, and the object created
* with that class is registered with a component using the
* component's <code>addPersistStateChangeListener</code> method. When
* the persistStateChange event occurs, that object's appropriate
* method is invoked.
*/
public interface PersistStateChangeListener {
/**
* Called when state needs to be persisted.
*
* @param state the state
* @param message the message
* @param transition the transition
* @param stateMachine the state machine
*/
void onPersist(State<String, String> state, Message<String> message, Transition<String, String> transition,
StateMachine<String, String> stateMachine);
}
private class PersistingStateChangeInterceptor extends StateMachineInterceptorAdapter<String, String> {
@Override
public void preStateChange(State<String, String> state, Message<String> message,
Transition<String, String> transition, StateMachine<String, String> stateMachine) {
listeners.onPersist(state, message, transition, stateMachine);
}
}
private class CompositePersistStateChangeListener extends AbstractCompositeListener<PersistStateChangeListener> implements
PersistStateChangeListener {
@Override
public void onPersist(State<String, String> state, Message<String> message,
Transition<String, String> transition, StateMachine<String, String> stateMachine) {
for (Iterator<PersistStateChangeListener> iterator = getListeners().reverse(); iterator.hasNext();) {
PersistStateChangeListener listener = iterator.next();
listener.onPersist(state, message, transition, stateMachine);
}
}
}
}