Skip to content

Commit f7a93a7

Browse files
committed
StateMachineContextSerializer handles history and id
- Add missing serialisation for history map and machine id - Fix possible NPE with extended state - Fixes #331 - Fixes #333
1 parent e51c765 commit f7a93a7

File tree

4 files changed

+213
-4
lines changed

4 files changed

+213
-4
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ project('spring-statemachine-build-tests') {
313313
dependencies {
314314
testCompile project(":spring-statemachine-uml")
315315
testCompile project(":spring-statemachine-test")
316+
testCompile project(":spring-statemachine-redis")
316317
testCompile project(":spring-statemachine-data-common:spring-statemachine-data-jpa")
317318
testCompile project(":spring-statemachine-data-common:spring-statemachine-data-redis")
318319
testCompile project(":spring-statemachine-data-common:spring-statemachine-data-mongodb")

spring-statemachine-build-tests/src/test/java/org/springframework/statemachine/buildtests/AbstractBuildTests.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016 the original author or authors.
2+
* Copyright 2016-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -36,6 +36,18 @@ public void clean() {
3636
context = null;
3737
}
3838

39+
protected enum TestStates {
40+
SI,S1,S2,S3,S4,SF,SH,
41+
S10,S11,S101,S111,S112,S12,S121,S122,S13,
42+
S20,S21,S201,S211,S212,
43+
S1011,S1012,S2011,S2012,
44+
S30,S31,S32,S33
45+
}
46+
47+
protected enum TestEvents {
48+
E1,E2,E3,E4,EF,EH
49+
}
50+
3951
/**
4052
* Builds the context.
4153
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/*
2+
* Copyright 2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.statemachine.buildtests;
17+
18+
import static org.hamcrest.Matchers.containsInAnyOrder;
19+
import static org.hamcrest.Matchers.is;
20+
import static org.hamcrest.Matchers.notNullValue;
21+
import static org.hamcrest.Matchers.nullValue;
22+
import static org.junit.Assert.assertThat;
23+
24+
import org.junit.Rule;
25+
import org.junit.Test;
26+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
27+
import org.springframework.context.annotation.Bean;
28+
import org.springframework.context.annotation.Configuration;
29+
import org.springframework.data.redis.connection.RedisConnectionFactory;
30+
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
31+
import org.springframework.statemachine.StateMachine;
32+
import org.springframework.statemachine.StateMachinePersist;
33+
import org.springframework.statemachine.buildtests.tck.redis.RedisRule;
34+
import org.springframework.statemachine.config.EnableStateMachineFactory;
35+
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
36+
import org.springframework.statemachine.config.StateMachineFactory;
37+
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
38+
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
39+
import org.springframework.statemachine.persist.RepositoryStateMachinePersist;
40+
import org.springframework.statemachine.persist.StateMachinePersister;
41+
import org.springframework.statemachine.redis.RedisStateMachineContextRepository;
42+
import org.springframework.statemachine.redis.RedisStateMachinePersister;
43+
44+
public class RedisPersistTests extends AbstractBuildTests {
45+
46+
@Rule
47+
public RedisRule redisAvailableRule = new RedisRule();
48+
49+
@Override
50+
protected AnnotationConfigApplicationContext buildContext() {
51+
return new AnnotationConfigApplicationContext();
52+
}
53+
54+
@Test
55+
@SuppressWarnings("unchecked")
56+
public void testPersistRegions() throws Exception {
57+
context.register(RedisConfig.class, Config1.class);
58+
context.refresh();
59+
60+
StateMachineFactory<TestStates, TestEvents> stateMachineFactory = context.getBean(StateMachineFactory.class);
61+
StateMachinePersister<TestStates, TestEvents, String> persister = context.getBean(StateMachinePersister.class);
62+
63+
StateMachine<TestStates, TestEvents> stateMachine = stateMachineFactory.getStateMachine("testid");
64+
stateMachine.start();
65+
assertThat(stateMachine, notNullValue());
66+
assertThat(stateMachine.getId(), is("testid"));
67+
68+
stateMachine.sendEvent(TestEvents.E1);
69+
assertThat(stateMachine.getState().getIds(), containsInAnyOrder(TestStates.S2, TestStates.S20, TestStates.S30));
70+
persister.persist(stateMachine, "xxx1");
71+
72+
stateMachine.sendEvent(TestEvents.E2);
73+
assertThat(stateMachine.getState().getIds(), containsInAnyOrder(TestStates.S2, TestStates.S21, TestStates.S30));
74+
persister.persist(stateMachine, "xxx2");
75+
76+
stateMachine.sendEvent(TestEvents.E3);
77+
assertThat(stateMachine.getState().getIds(), containsInAnyOrder(TestStates.S4));
78+
persister.persist(stateMachine, "xxx3");
79+
80+
stateMachine = stateMachineFactory.getStateMachine();
81+
assertThat(stateMachine, notNullValue());
82+
assertThat(stateMachine.getId(), nullValue());
83+
stateMachine = persister.restore(stateMachine, "xxx1");
84+
assertThat(stateMachine.getId(), is("testid"));
85+
assertThat(stateMachine.getState().getIds(), containsInAnyOrder(TestStates.S2, TestStates.S20, TestStates.S30));
86+
stateMachine.sendEvent(TestEvents.E2);
87+
assertThat(stateMachine.getState().getIds(), containsInAnyOrder(TestStates.S2, TestStates.S21, TestStates.S30));
88+
89+
stateMachine = stateMachineFactory.getStateMachine();
90+
assertThat(stateMachine, notNullValue());
91+
assertThat(stateMachine.getId(), nullValue());
92+
stateMachine = persister.restore(stateMachine, "xxx2");
93+
assertThat(stateMachine.getId(), is("testid"));
94+
assertThat(stateMachine.getState().getIds(), containsInAnyOrder(TestStates.S2, TestStates.S21, TestStates.S30));
95+
stateMachine.sendEvent(TestEvents.E3);
96+
assertThat(stateMachine.getState().getIds(), containsInAnyOrder(TestStates.S4));
97+
98+
stateMachine = stateMachineFactory.getStateMachine();
99+
assertThat(stateMachine, notNullValue());
100+
assertThat(stateMachine.getId(), nullValue());
101+
stateMachine = persister.restore(stateMachine, "xxx3");
102+
assertThat(stateMachine.getId(), is("testid"));
103+
assertThat(stateMachine.getState().getIds(), containsInAnyOrder(TestStates.S4));
104+
}
105+
106+
@Configuration
107+
static class RedisConfig {
108+
109+
@Bean
110+
public RedisConnectionFactory redisConnectionFactory() {
111+
return new JedisConnectionFactory();
112+
}
113+
114+
@Bean
115+
public StateMachinePersist<TestStates, TestEvents, String> stateMachinePersist(RedisConnectionFactory connectionFactory) {
116+
RedisStateMachineContextRepository<TestStates, TestEvents> repository =
117+
new RedisStateMachineContextRepository<TestStates, TestEvents>(connectionFactory);
118+
return new RepositoryStateMachinePersist<TestStates, TestEvents>(repository);
119+
}
120+
121+
@Bean
122+
public StateMachinePersister<TestStates, TestEvents, String> stateMachinePersister(
123+
StateMachinePersist<TestStates, TestEvents, String> stateMachinePersist) {
124+
return new RedisStateMachinePersister<TestStates, TestEvents>(stateMachinePersist);
125+
}
126+
}
127+
128+
@Configuration
129+
@EnableStateMachineFactory
130+
static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
131+
132+
@Override
133+
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
134+
states
135+
.withStates()
136+
.initial(TestStates.SI)
137+
.state(TestStates.SI)
138+
.fork(TestStates.S1)
139+
.state(TestStates.S2)
140+
.end(TestStates.SF)
141+
.join(TestStates.S3)
142+
.state(TestStates.S4)
143+
.and()
144+
.withStates()
145+
.parent(TestStates.S2)
146+
.initial(TestStates.S20)
147+
.state(TestStates.S20)
148+
.state(TestStates.S21)
149+
.and()
150+
.withStates()
151+
.parent(TestStates.S2)
152+
.initial(TestStates.S30)
153+
.state(TestStates.S30)
154+
.state(TestStates.S31);
155+
}
156+
157+
@Override
158+
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
159+
transitions
160+
.withExternal()
161+
.source(TestStates.SI)
162+
.target(TestStates.S2)
163+
.event(TestEvents.E1)
164+
.and()
165+
.withExternal()
166+
.source(TestStates.S20)
167+
.target(TestStates.S21)
168+
.event(TestEvents.E2)
169+
.and()
170+
.withExternal()
171+
.source(TestStates.S30)
172+
.target(TestStates.S31)
173+
.event(TestEvents.E3)
174+
.and()
175+
.withFork()
176+
.source(TestStates.S1)
177+
.target(TestStates.S20)
178+
.target(TestStates.S30)
179+
.and()
180+
.withJoin()
181+
.source(TestStates.S21)
182+
.source(TestStates.S31)
183+
.target(TestStates.S3)
184+
.and()
185+
.withExternal()
186+
.source(TestStates.S3)
187+
.target(TestStates.S4);
188+
}
189+
190+
}
191+
192+
}

spring-statemachine-kryo/src/main/java/org/springframework/statemachine/kryo/StateMachineContextSerializer.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2015 the original author or authors.
2+
* Copyright 2015-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -42,8 +42,10 @@ public void write(Kryo kryo, Output output, StateMachineContext<S, E> context) {
4242
kryo.writeClassAndObject(output, context.getEvent());
4343
kryo.writeClassAndObject(output, context.getState());
4444
kryo.writeClassAndObject(output, context.getEventHeaders());
45-
kryo.writeClassAndObject(output, context.getExtendedState().getVariables());
45+
kryo.writeClassAndObject(output, context.getExtendedState() != null ? context.getExtendedState().getVariables() : null);
4646
kryo.writeClassAndObject(output, context.getChilds());
47+
kryo.writeClassAndObject(output, context.getHistoryStates());
48+
kryo.writeClassAndObject(output, context.getId());
4749
}
4850

4951
@SuppressWarnings("unchecked")
@@ -54,7 +56,9 @@ public StateMachineContext<S, E> read(Kryo kryo, Input input, Class<StateMachine
5456
Map<String, Object> eventHeaders = (Map<String, Object>) kryo.readClassAndObject(input);
5557
Map<Object, Object> variables = (Map<Object, Object>) kryo.readClassAndObject(input);
5658
List<StateMachineContext<S, E>> childs = (List<StateMachineContext<S, E>>) kryo.readClassAndObject(input);
57-
return new DefaultStateMachineContext<S, E>(childs, state, event, eventHeaders, new DefaultExtendedState(variables));
59+
Map<S, S> historyStates = (Map<S, S>) kryo.readClassAndObject(input);
60+
String id = (String) kryo.readClassAndObject(input);
61+
return new DefaultStateMachineContext<S, E>(childs, state, event, eventHeaders, new DefaultExtendedState(variables), historyStates, id);
5862
}
5963

6064
}

0 commit comments

Comments
 (0)