Add base DefaultStateMachineService implementation
- First take on new interface StateMachineService with its default impl. - Modify datajpapersist sample to use it. - Relates to #432
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2017 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.service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.StateMachineContext;
|
||||
import org.springframework.statemachine.StateMachineException;
|
||||
import org.springframework.statemachine.StateMachinePersist;
|
||||
import org.springframework.statemachine.access.StateMachineAccess;
|
||||
import org.springframework.statemachine.access.StateMachineFunction;
|
||||
import org.springframework.statemachine.config.StateMachineFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of a {@link StateMachineService}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public class DefaultStateMachineService<S, E> implements StateMachineService<S, E> {
|
||||
|
||||
private final StateMachineFactory<S, E> stateMachineFactory;
|
||||
private final Map<String, StateMachine<S, E>> machines = new HashMap<String, StateMachine<S, E>>();
|
||||
private StateMachinePersist<S, E, String> stateMachinePersist;
|
||||
|
||||
/**
|
||||
* Instantiates a new default state machine service.
|
||||
*
|
||||
* @param stateMachineFactory the state machine factory
|
||||
*/
|
||||
public DefaultStateMachineService(StateMachineFactory<S, E> stateMachineFactory) {
|
||||
this(stateMachineFactory, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new default state machine service.
|
||||
*
|
||||
* @param stateMachineFactory the state machine factory
|
||||
* @param stateMachinePersist the state machine persist
|
||||
*/
|
||||
public DefaultStateMachineService(StateMachineFactory<S, E> stateMachineFactory,
|
||||
StateMachinePersist<S, E, String> stateMachinePersist) {
|
||||
Assert.notNull(stateMachineFactory, "'stateMachineFactory' must be set");
|
||||
this.stateMachineFactory = stateMachineFactory;
|
||||
this.stateMachinePersist = stateMachinePersist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateMachine<S, E> acquireStateMachine(String machineId) {
|
||||
synchronized (machines) {
|
||||
StateMachine<S,E> stateMachine = machines.get(machineId);
|
||||
if (stateMachine == null) {
|
||||
stateMachine = stateMachineFactory.getStateMachine(machineId);
|
||||
machines.put(machineId, stateMachine);
|
||||
}
|
||||
if (stateMachinePersist != null) {
|
||||
try {
|
||||
StateMachineContext<S, E> stateMachineContext = stateMachinePersist.read(machineId);
|
||||
return restoreStateMachine(stateMachine, stateMachineContext);
|
||||
} catch (Exception e) {
|
||||
throw new StateMachineException("Unable to read context from store", e);
|
||||
}
|
||||
} else {
|
||||
return stateMachine;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseStateMachine(String machineId) {
|
||||
synchronized (machines) {
|
||||
StateMachine<S, E> stateMachine = machines.remove(machineId);
|
||||
if (stateMachine != null) {
|
||||
stateMachine.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state machine persist.
|
||||
*
|
||||
* @param stateMachinePersist the state machine persist
|
||||
*/
|
||||
public void setStateMachinePersist(StateMachinePersist<S, E, String> stateMachinePersist) {
|
||||
this.stateMachinePersist = stateMachinePersist;
|
||||
}
|
||||
|
||||
protected StateMachine<S, E> restoreStateMachine(StateMachine<S, E> stateMachine, final StateMachineContext<S, E> stateMachineContext) {
|
||||
if (stateMachineContext == null) {
|
||||
return stateMachine;
|
||||
}
|
||||
stateMachine.stop();
|
||||
stateMachine.getStateMachineAccessor().doWithAllRegions(new StateMachineFunction<StateMachineAccess<S, E>>() {
|
||||
|
||||
@Override
|
||||
public void apply(StateMachineAccess<S, E> function) {
|
||||
function.resetStateMachine(stateMachineContext);
|
||||
}
|
||||
});
|
||||
return stateMachine;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2017 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.service;
|
||||
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
|
||||
/**
|
||||
* Service class helping to persist and restore {@link StateMachine}s
|
||||
* in a runtime environment.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public interface StateMachineService<S, E> {
|
||||
|
||||
/**
|
||||
* Acquires the state machine.
|
||||
*
|
||||
* @param machineId the machine id
|
||||
* @return the state machine
|
||||
*/
|
||||
StateMachine<S, E> acquireStateMachine(String machineId);
|
||||
|
||||
/**
|
||||
* Release the state machine.
|
||||
*
|
||||
* @param machineId the machine id
|
||||
*/
|
||||
void releaseStateMachine(String machineId);
|
||||
}
|
||||
@@ -20,14 +20,18 @@ import java.util.EnumSet;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.statemachine.StateMachinePersist;
|
||||
import org.springframework.statemachine.config.EnableStateMachineFactory;
|
||||
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.StateMachineFactory;
|
||||
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
import org.springframework.statemachine.data.jpa.JpaPersistingStateMachineInterceptor;
|
||||
import org.springframework.statemachine.data.jpa.JpaStateMachineRepository;
|
||||
import org.springframework.statemachine.persist.StateMachineRuntimePersister;
|
||||
import org.springframework.statemachine.service.DefaultStateMachineService;
|
||||
import org.springframework.statemachine.service.StateMachineService;
|
||||
|
||||
@Configuration
|
||||
public class StateMachineConfig {
|
||||
@@ -35,7 +39,7 @@ public class StateMachineConfig {
|
||||
//tag::snippetA[]
|
||||
@Configuration
|
||||
@EnableStateMachineFactory
|
||||
public static class Config extends StateMachineConfigurerAdapter<States, Events> {
|
||||
public static class MachineConfig extends StateMachineConfigurerAdapter<States, Events> {
|
||||
|
||||
@Autowired
|
||||
private JpaStateMachineRepository jpaStateMachineRepository;
|
||||
@@ -93,6 +97,16 @@ public class StateMachineConfig {
|
||||
}
|
||||
//end::snippetA[]
|
||||
|
||||
@Configuration
|
||||
public static class ServiceConfig {
|
||||
|
||||
@Bean
|
||||
public StateMachineService<States, Events> stateMachineService(StateMachineFactory<States, Events> stateMachineFactory,
|
||||
StateMachinePersist<States, Events, String> stateMachinePersist) {
|
||||
return new DefaultStateMachineService<>(stateMachineFactory, stateMachinePersist);
|
||||
}
|
||||
}
|
||||
|
||||
//tag::snippetB[]
|
||||
public enum States {
|
||||
S1, S2, S3, S4, S5, S6;
|
||||
|
||||
@@ -22,9 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.StateMachineContext;
|
||||
import org.springframework.statemachine.StateMachinePersist;
|
||||
import org.springframework.statemachine.access.StateMachineAccess;
|
||||
import org.springframework.statemachine.access.StateMachineFunction;
|
||||
import org.springframework.statemachine.config.StateMachineFactory;
|
||||
import org.springframework.statemachine.service.StateMachineService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -41,15 +39,15 @@ public class StateMachineController {
|
||||
public final static String MACHINE_ID_2 = "datajpapersist2";
|
||||
private final static String[] MACHINES = new String[] { MACHINE_ID_1, MACHINE_ID_2 };
|
||||
|
||||
private final StateMachineLogListener listener = new StateMachineLogListener();
|
||||
private StateMachine<States, Events> currentStateMachine;
|
||||
|
||||
@Autowired
|
||||
private StateMachineFactory<States, Events> stateMachineFactory;
|
||||
private StateMachineService<States, Events> stateMachineService;
|
||||
|
||||
@Autowired
|
||||
private StateMachinePersist<States, Events, String> stateMachinePersist;
|
||||
|
||||
private StateMachine<States, Events> cachedStateMachine;
|
||||
private final StateMachineLogListener listener = new StateMachineLogListener();
|
||||
|
||||
@RequestMapping("/")
|
||||
public String home() {
|
||||
return "redirect:/state";
|
||||
@@ -60,7 +58,6 @@ public class StateMachineController {
|
||||
@RequestParam(value = "events", required = false) List<Events> events,
|
||||
@RequestParam(value = "machine", required = false, defaultValue = MACHINE_ID_1) String machine,
|
||||
Model model) throws Exception {
|
||||
|
||||
StateMachine<States, Events> stateMachine = getStateMachine(machine);
|
||||
if (events != null) {
|
||||
for (Events event : events) {
|
||||
@@ -77,40 +74,18 @@ public class StateMachineController {
|
||||
}
|
||||
|
||||
private synchronized StateMachine<States, Events> getStateMachine(String machineId) throws Exception {
|
||||
if (cachedStateMachine == null) {
|
||||
cachedStateMachine = buildStateMachine(machineId);
|
||||
cachedStateMachine.start();
|
||||
} else {
|
||||
if (!ObjectUtils.nullSafeEquals(cachedStateMachine.getId(), machineId)) {
|
||||
cachedStateMachine.stop();
|
||||
cachedStateMachine = buildStateMachine(machineId);
|
||||
cachedStateMachine.start();
|
||||
}
|
||||
}
|
||||
return cachedStateMachine;
|
||||
}
|
||||
|
||||
private StateMachine<States, Events> buildStateMachine(String machineId) throws Exception {
|
||||
StateMachine<States, Events> stateMachine = stateMachineFactory.getStateMachine(machineId);
|
||||
stateMachine.addStateListener(listener);
|
||||
listener.resetMessages();
|
||||
return restoreStateMachine(stateMachine, stateMachinePersist.read(machineId));
|
||||
}
|
||||
|
||||
private StateMachine<States, Events> restoreStateMachine(StateMachine<States, Events> stateMachine,
|
||||
StateMachineContext<States, Events> stateMachineContext) {
|
||||
if (stateMachineContext == null) {
|
||||
return stateMachine;
|
||||
if (currentStateMachine == null) {
|
||||
currentStateMachine = stateMachineService.acquireStateMachine(machineId);
|
||||
currentStateMachine.addStateListener(listener);
|
||||
currentStateMachine.start();
|
||||
} else if (!ObjectUtils.nullSafeEquals(currentStateMachine.getId(), machineId)) {
|
||||
currentStateMachine.stop();
|
||||
currentStateMachine = stateMachineService.acquireStateMachine(machineId);
|
||||
currentStateMachine.addStateListener(listener);
|
||||
currentStateMachine.start();
|
||||
}
|
||||
stateMachine.stop();
|
||||
stateMachine.getStateMachineAccessor().doWithAllRegions(new StateMachineFunction<StateMachineAccess<States, Events>>() {
|
||||
|
||||
@Override
|
||||
public void apply(StateMachineAccess<States, Events> function) {
|
||||
function.resetStateMachine(stateMachineContext);
|
||||
}
|
||||
});
|
||||
return stateMachine;
|
||||
return currentStateMachine;
|
||||
}
|
||||
|
||||
private Events[] getEvents() {
|
||||
|
||||
Reference in New Issue
Block a user