Refactor region handling and persistence

- Make kryo in AbstractKryoStateMachineSerialisationService aware of same classloader
  most likely use in an app. This takes away some of those weird kryo
  errors you see with a web apps.
- Add context references concept to StateMachineContext which can be used
  to store reference id and then individual running machines with regions
  can independently store their states. Whole machine state can then get
  restored more accurately.
- Add new `region(String id)` to StateConfigurer which can be used to set region id.
  This is equivalent as setting region id with json based machine structure where
  you need to define region id's with orthogonal regions are in use.
- Add new datajpamultipersist sample showing running regions and how those are
  persisted to a database.
- Fixes #617
- Fixes #605
- Fixes #615
This commit is contained in:
Janne Valkealahti
2019-01-12 08:36:51 +00:00
parent c700301767
commit 84ca0aec3e
34 changed files with 1297 additions and 51 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 the original author or authors.
* Copyright 2017-2018 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.
@@ -23,6 +23,7 @@ import java.io.OutputStream;
import org.springframework.statemachine.StateMachineContext;
import org.springframework.statemachine.service.StateMachineSerialisationService;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
@@ -49,6 +50,10 @@ public abstract class AbstractKryoStateMachineSerialisationService<S, E> impleme
@Override
public Kryo create() {
Kryo kryo = new Kryo();
// kryo is really getting trouble checking things if class loaders
// doesn't match. for now just use below trick before we try
// to go fully on beans and get a bean class loader.
kryo.setClassLoader(ClassUtils.getDefaultClassLoader());
configureKryoInstance(kryo);
return kryo;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2017 the original author or authors.
* Copyright 2015-2019 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.
@@ -46,6 +46,10 @@ public class StateMachineContextSerializer<S, E> extends Serializer<StateMachine
kryo.writeClassAndObject(output, context.getChilds());
kryo.writeClassAndObject(output, context.getHistoryStates());
kryo.writeClassAndObject(output, context.getId());
// child refs is added after initial implementation, leaving this not here
// in case it's starting to cause issues with any existing serialized contexts
// which doesn't have this field
kryo.writeClassAndObject(output, context.getChildReferences());
}
@SuppressWarnings("unchecked")
@@ -58,7 +62,8 @@ public class StateMachineContextSerializer<S, E> extends Serializer<StateMachine
List<StateMachineContext<S, E>> childs = (List<StateMachineContext<S, E>>) kryo.readClassAndObject(input);
Map<S, S> historyStates = (Map<S, S>) kryo.readClassAndObject(input);
String id = (String) kryo.readClassAndObject(input);
return new DefaultStateMachineContext<S, E>(childs, state, event, eventHeaders, new DefaultExtendedState(variables), historyStates, id);
List<String> childRefs = (List<String>) kryo.readClassAndObject(input);
return new DefaultStateMachineContext<S, E>(childRefs, childs, state, event, eventHeaders,
new DefaultExtendedState(variables), historyStates, id);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2018 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.kryo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.springframework.statemachine.StateMachineContext;
import org.springframework.statemachine.support.DefaultExtendedState;
import org.springframework.statemachine.support.DefaultStateMachineContext;
/**
* Tests for {@link KryoStateMachineSerialisationService}.
*
* @author Janne Valkealahti
*
*/
public class KryoStateMachineSerialisationServiceTests {
@Test
public void testContextWithChilds() throws Exception {
StateMachineContext<String, String> child1 = new DefaultStateMachineContext<String, String>("child1", null, null,
new DefaultExtendedState());
StateMachineContext<String, String> child2 = new DefaultStateMachineContext<String, String>("child2", null, null,
new DefaultExtendedState());
List<StateMachineContext<String, String>> childs = new ArrayList<>();
childs.add(child1);
childs.add(child2);
StateMachineContext<String, String> root = new DefaultStateMachineContext<String, String>(childs, "root", null,
null, new DefaultExtendedState());
KryoStateMachineSerialisationService<String, String> service = new KryoStateMachineSerialisationService<>();
byte[] bytes = service.serialiseStateMachineContext(root);
StateMachineContext<String, String> context = service.deserialiseStateMachineContext(bytes);
assertThat(context.getChilds().size(), is(2));
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2018 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.kryo;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.statemachine.StateMachineContext;
import org.springframework.statemachine.support.DefaultExtendedState;
import org.springframework.statemachine.support.DefaultStateMachineContext;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
/**
* Tests for {@link StateMachineContextSerializer}.
*
* @author Janne Valkealahti
*
*/
public class StateMachineContextSerializerTests {
private Kryo kryo;
private Output output;
private Input input;
@Before
public void setUp() throws Exception {
kryo = new Kryo();
}
@Test
public void testContextWithChilds() {
StateMachineContextSerializer<String, String> serializer = new StateMachineContextSerializer<>();
kryo.addDefaultSerializer(StateMachineContext.class, serializer);
StateMachineContext<String, String> child = new DefaultStateMachineContext<String, String>(new ArrayList<>(), "child", "event1",
new HashMap<String, Object>(), new DefaultExtendedState());
List<StateMachineContext<String, String>> childs = new ArrayList<>();
childs.add(child);
StateMachineContext<String, String> root = new DefaultStateMachineContext<String, String>(childs, "root", "event2",
new HashMap<String, Object>(), new DefaultExtendedState());
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
output = new Output(outStream);
kryo.writeClassAndObject(output, root);
output.flush();
input = new Input(new ByteArrayInputStream(outStream.toByteArray()));
kryo.readClassAndObject(input);
}
}