diff --git a/saga/seata-saga-engine/src/main/java/io/seata/saga/engine/impl/DefaultStateMachineConfig.java b/saga/seata-saga-engine/src/main/java/io/seata/saga/engine/impl/DefaultStateMachineConfig.java index 5ec5299ee96bab00cc9f075e784c9f9c5e46e9a6..6e47cddc54516ea060b54416d41238ef89ea16fa 100644 --- a/saga/seata-saga-engine/src/main/java/io/seata/saga/engine/impl/DefaultStateMachineConfig.java +++ b/saga/seata-saga-engine/src/main/java/io/seata/saga/engine/impl/DefaultStateMachineConfig.java @@ -208,6 +208,7 @@ public class DefaultStateMachineConfig implements StateMachineConfig, Applicatio SpringBeanServiceInvoker springBeanServiceInvoker = new SpringBeanServiceInvoker(); springBeanServiceInvoker.setApplicationContext(getApplicationContext()); springBeanServiceInvoker.setThreadPoolExecutor(threadPoolExecutor); + springBeanServiceInvoker.setSagaJsonParser(getSagaJsonParser()); this.serviceInvokerManager.putServiceInvoker(DomainConstants.SERVICE_TYPE_SPRING_BEAN, springBeanServiceInvoker); } diff --git a/saga/seata-saga-engine/src/main/java/io/seata/saga/engine/invoker/impl/SpringBeanServiceInvoker.java b/saga/seata-saga-engine/src/main/java/io/seata/saga/engine/invoker/impl/SpringBeanServiceInvoker.java index af41a7ebcebbfde1d854fc7d25eb38beee206aa0..9f4908a8b94d6bf45cdcd878779343f421bc0dc9 100644 --- a/saga/seata-saga-engine/src/main/java/io/seata/saga/engine/invoker/impl/SpringBeanServiceInvoker.java +++ b/saga/seata-saga-engine/src/main/java/io/seata/saga/engine/invoker/impl/SpringBeanServiceInvoker.java @@ -27,9 +27,6 @@ import java.util.Map; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicInteger; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.parser.Feature; - import io.seata.common.exception.FrameworkErrorCode; import io.seata.common.util.CollectionUtils; import io.seata.saga.engine.exception.EngineExecutionException; @@ -39,6 +36,8 @@ import io.seata.saga.engine.utils.ExceptionUtils; import io.seata.saga.statelang.domain.ServiceTaskState; import io.seata.saga.statelang.domain.TaskState.Retry; import io.seata.saga.statelang.domain.impl.ServiceTaskStateImpl; +import io.seata.saga.statelang.parser.JsonParser; +import io.seata.saga.statelang.parser.JsonParserFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; @@ -56,6 +55,7 @@ public class SpringBeanServiceInvoker implements ServiceInvoker, ApplicationCont private ApplicationContext applicationContext; private ThreadPoolExecutor threadPoolExecutor; + private String sagaJsonParser; @Override public Object invoke(ServiceTaskState serviceTaskState, Object... input) throws Throwable { @@ -296,8 +296,12 @@ public class SpringBeanServiceInvoker implements ServiceInvoker, ApplicationCont } else if (isPrimitive(paramType)) { return value; } else { - String jsonValue = JSON.toJSONString(value); - return JSON.parseObject(jsonValue, paramType, Feature.SupportAutoType); + JsonParser jsonParser = JsonParserFactory.getJsonParser(getSagaJsonParser()); + if (jsonParser == null) { + throw new RuntimeException("Cannot get JsonParser by name : " + getSagaJsonParser()); + } + String jsonValue = jsonParser.toJsonString(value, true, false); + return jsonParser.parse(jsonValue, paramType, false); } } @@ -344,4 +348,12 @@ public class SpringBeanServiceInvoker implements ServiceInvoker, ApplicationCont return null; } } + + public String getSagaJsonParser() { + return sagaJsonParser; + } + + public void setSagaJsonParser(String sagaJsonParser) { + this.sagaJsonParser = sagaJsonParser; + } } \ No newline at end of file diff --git a/saga/seata-saga-statelang/src/main/java/io/seata/saga/statelang/parser/JsonParser.java b/saga/seata-saga-statelang/src/main/java/io/seata/saga/statelang/parser/JsonParser.java index 37fef5c258e41174d2b9944a19090781d7f74e7b..3d956d1314074be98802a913eaad397e74e2f09c 100644 --- a/saga/seata-saga-statelang/src/main/java/io/seata/saga/statelang/parser/JsonParser.java +++ b/saga/seata-saga-statelang/src/main/java/io/seata/saga/statelang/parser/JsonParser.java @@ -39,6 +39,15 @@ public interface JsonParser { */ String toJsonString(Object o, boolean prettyPrint); + /** + * Object to Json string + * @param o + * @param ignoreAutoType + * @param prettyPrint + * @return + */ + String toJsonString(Object o, boolean ignoreAutoType, boolean prettyPrint); + /** * parse json string to Object * diff --git a/saga/seata-saga-statelang/src/main/java/io/seata/saga/statelang/parser/impl/FastjsonParser.java b/saga/seata-saga-statelang/src/main/java/io/seata/saga/statelang/parser/impl/FastjsonParser.java index 04efa8eed97009166b9a1c758479587bfa6497ba..b7ccfb35a591c2241a81035d5dfb0fa8bdaba27d 100644 --- a/saga/seata-saga-statelang/src/main/java/io/seata/saga/statelang/parser/impl/FastjsonParser.java +++ b/saga/seata-saga-statelang/src/main/java/io/seata/saga/statelang/parser/impl/FastjsonParser.java @@ -40,6 +40,11 @@ public class FastjsonParser implements JsonParser { SerializerFeature.WriteClassName, SerializerFeature.PrettyFormat }; + private static final SerializerFeature[] FEATURES_PRETTY = new SerializerFeature[] { + SerializerFeature.DisableCircularReferenceDetect, + SerializerFeature.WriteDateUseDateFormat, + SerializerFeature.PrettyFormat }; + public static final String NAME = "fastjson"; @Override @@ -49,11 +54,26 @@ public class FastjsonParser implements JsonParser { @Override public String toJsonString(Object o, boolean prettyPrint) { + return toJsonString(o, false, prettyPrint); + } + + @Override + public String toJsonString(Object o, boolean ignoreAutoType, boolean prettyPrint) { if (prettyPrint) { - return JSON.toJSONString(o, SERIALIZER_FEATURES_PRETTY); + if (ignoreAutoType) { + return JSON.toJSONString(o, FEATURES_PRETTY); + } + else { + return JSON.toJSONString(o, SERIALIZER_FEATURES_PRETTY); + } } else { - return JSON.toJSONString(o, SERIALIZER_FEATURES); + if (ignoreAutoType) { + return JSON.toJSONString(o); + } + else { + return JSON.toJSONString(o, SERIALIZER_FEATURES); + } } } diff --git a/saga/seata-saga-statelang/src/main/java/io/seata/saga/statelang/parser/impl/JacksonJsonParser.java b/saga/seata-saga-statelang/src/main/java/io/seata/saga/statelang/parser/impl/JacksonJsonParser.java index 6d84558ddca050816def7b4835c28beb7df9d86c..3407df89ffd3fab154caa9a8f1c56ecc1ba35d3d 100644 --- a/saga/seata-saga-statelang/src/main/java/io/seata/saga/statelang/parser/impl/JacksonJsonParser.java +++ b/saga/seata-saga-statelang/src/main/java/io/seata/saga/statelang/parser/impl/JacksonJsonParser.java @@ -61,15 +61,31 @@ public class JacksonJsonParser implements JsonParser { @Override public String toJsonString(Object o, boolean prettyPrint) { + return toJsonString(o, false, prettyPrint); + } + + @Override + public String toJsonString(Object o, boolean ignoreAutoType, boolean prettyPrint) { try { if (o instanceof List && ((List) o).isEmpty()) { return "[]"; } if (prettyPrint) { - return objectMapperWithAutoType.writerWithDefaultPrettyPrinter().writeValueAsString(o); + if (ignoreAutoType) { + return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(o); + } + else { + return objectMapperWithAutoType.writerWithDefaultPrettyPrinter().writeValueAsString(o); + } + } else { - return objectMapperWithAutoType.writeValueAsString(o); + if (ignoreAutoType) { + return objectMapper.writeValueAsString(o); + } + else { + return objectMapperWithAutoType.writeValueAsString(o); + } } } catch (JsonProcessingException e) { throw new RuntimeException("Parse object to json error", e); diff --git a/test/src/test/java/io/seata/saga/engine/StateMachineAsyncTests.java b/test/src/test/java/io/seata/saga/engine/StateMachineAsyncTests.java index ea71c40d5979dc959a20776e462413b73a5237b2..29a3626f21e6967549776771e22554be1508be61 100644 --- a/test/src/test/java/io/seata/saga/engine/StateMachineAsyncTests.java +++ b/test/src/test/java/io/seata/saga/engine/StateMachineAsyncTests.java @@ -219,7 +219,7 @@ public class StateMachineAsyncTests { } @Test - public void testStateMachineWithComplextParams() { + public void testStateMachineWithComplexParams() { long start = System.currentTimeMillis(); diff --git a/test/src/test/java/io/seata/saga/engine/StateMachineTests.java b/test/src/test/java/io/seata/saga/engine/StateMachineTests.java index 95368d345fa5a430bcc5a185a4b1f11f154ded1f..a46ffedee92ff5e0fb02d01df4723d0443b46fff 100644 --- a/test/src/test/java/io/seata/saga/engine/StateMachineTests.java +++ b/test/src/test/java/io/seata/saga/engine/StateMachineTests.java @@ -20,12 +20,14 @@ import io.seata.saga.engine.mock.DemoService.People; import io.seata.saga.statelang.domain.DomainConstants; import io.seata.saga.statelang.domain.ExecutionStatus; import io.seata.saga.statelang.domain.StateMachineInstance; +import io.seata.saga.statelang.parser.JsonParserFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -279,7 +281,36 @@ public class StateMachineTests { } @Test - public void testStateMachineWithComplextParams() { + public void testStateComplexParams() { + + People people1 = new People(); + people1.setName("lilei"); + people1.setAge(18); + + People people2 = new People(); + people2.setName("lilei2"); + people2.setAge(19); + + People people3 = new People(); + people3.setName("lilei3"); + people3.setAge(20); + + People people4 = new People(); + people4.setName("lilei4"); + people4.setAge(21); + + people1.setChildrenArray(new People[] {people2}); + people1.setChildrenList(Arrays.asList(people3)); + Map<String, People> map1 = new HashMap<>(1); + map1.put("lilei4", people4); + people1.setChildrenMap(map1); + + String json = JsonParserFactory.getJsonParser("jackson").toJsonString(people1, false, true); + System.out.println(json); + } + + @Test + public void testStateMachineWithComplexParams() { long start = System.currentTimeMillis(); diff --git a/test/src/test/java/io/seata/saga/engine/db/StateMachineDBTests.java b/test/src/test/java/io/seata/saga/engine/db/StateMachineDBTests.java index ba74a08b40df89438a3748f03a5d955d6d6def15..29994e59b37454233b4b04d97a7580aab8177952 100644 --- a/test/src/test/java/io/seata/saga/engine/db/StateMachineDBTests.java +++ b/test/src/test/java/io/seata/saga/engine/db/StateMachineDBTests.java @@ -24,6 +24,8 @@ import io.seata.saga.engine.AsyncCallback; import io.seata.saga.engine.StateMachineEngine; import io.seata.saga.engine.exception.EngineExecutionException; import io.seata.saga.engine.impl.DefaultStateMachineConfig; +import io.seata.saga.engine.mock.DemoService.Engineer; +import io.seata.saga.engine.mock.DemoService.People; import io.seata.saga.proctrl.ProcessContext; import io.seata.saga.statelang.domain.DomainConstants; import io.seata.saga.statelang.domain.ExecutionStatus; @@ -248,6 +250,36 @@ public class StateMachineDBTests extends AbstractServerTest { Assertions.assertTrue(GlobalStatus.CommitRetrying.equals(globalTransaction.getStatus())); } + @Test + public void testStateMachineWithComplexParams() { + + long start = System.currentTimeMillis(); + + Map<String, Object> paramMap = new HashMap<>(1); + People people = new People(); + people.setName("lilei"); + people.setAge(18); + + Engineer engineer = new Engineer(); + engineer.setName("programmer"); + + paramMap.put("people", people); + paramMap.put("career", engineer); + + String stateMachineName = "simpleStateMachineWithComplexParamsJackson"; + + StateMachineInstance instance = stateMachineEngine.start(stateMachineName, null, paramMap); + + People peopleResult = (People) instance.getEndParams().get("complexParameterMethodResult"); + Assertions.assertNotNull(peopleResult); + Assertions.assertTrue(people.getName().equals(people.getName())); + + long cost = System.currentTimeMillis() - start; + System.out.println("====== XID: " + instance.getId() + " cost :" + cost); + + Assertions.assertTrue(ExecutionStatus.SU.equals(instance.getStatus())); + } + @Test public void testCompensationStateMachine() throws Exception { diff --git a/test/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineAsyncDBMockServerTests.java b/test/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineAsyncDBMockServerTests.java index 10087333686677d49886f30f4b14a742cae2ee43..9e60fe92cefb0e641d09eaeeb370dcd08a5e5d22 100644 --- a/test/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineAsyncDBMockServerTests.java +++ b/test/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineAsyncDBMockServerTests.java @@ -177,7 +177,7 @@ public class StateMachineAsyncDBMockServerTests { } @Test - public void testStateMachineWithComplextParams() { + public void testStateMachineWithComplexParams() { long start = System.currentTimeMillis(); @@ -187,7 +187,7 @@ public class StateMachineAsyncDBMockServerTests { people.setAge(18); paramMap.put("people", people); - String stateMachineName = "simpleStateMachineWithComplexParams"; + String stateMachineName = "simpleStateMachineWithComplexParamsJackson"; StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback); diff --git a/test/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineDBMockServerTests.java b/test/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineDBMockServerTests.java index cd6a458d4ef87b94bd6ddb46367185e540f46d02..5eab4716c129751aba7b7b0a4373766c593eb699 100644 --- a/test/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineDBMockServerTests.java +++ b/test/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineDBMockServerTests.java @@ -613,7 +613,7 @@ public class StateMachineDBMockServerTests { } @Test - public void testStateMachineWithComplextParams() { + public void testStateMachineWithComplexParams() { long start = System.currentTimeMillis(); @@ -628,7 +628,7 @@ public class StateMachineDBMockServerTests { paramMap.put("people", people); paramMap.put("career", engineer); - String stateMachineName = "simpleStateMachineWithComplexParams"; + String stateMachineName = "simpleStateMachineWithComplexParamsJackson"; StateMachineInstance instance = stateMachineEngine.start(stateMachineName, null, paramMap); diff --git a/test/src/test/resources/saga/statelang/simple_statelang_with_complex_params.json b/test/src/test/resources/saga/statelang/simple_statelang_with_complex_params.json index e5c5a41dc69d92e711084d087a319fabd21b9055..5c6f1cb47e1c774066c15a5f3e82f2fc2669f6df 100644 --- a/test/src/test/resources/saga/statelang/simple_statelang_with_complex_params.json +++ b/test/src/test/resources/saga/statelang/simple_statelang_with_complex_params.json @@ -1,6 +1,6 @@ { "Name": "simpleStateMachineWithComplexParams", - "Comment": "甯﹀鏉傚弬鏁扮殑娴嬭瘯鐘舵€佹満瀹氫箟", + "Comment": "甯﹀鏉傚弬鏁扮殑娴嬭瘯鐘舵€佹満瀹氫箟fastjson鏍煎紡", "StartState": "FirstState", "Version": "0.0.1", "States": { diff --git a/test/src/test/resources/saga/statelang/simple_statelang_with_complex_params_jackson.json b/test/src/test/resources/saga/statelang/simple_statelang_with_complex_params_jackson.json new file mode 100644 index 0000000000000000000000000000000000000000..1ebb0006634c31935c8e0093f7a81bd276321222 --- /dev/null +++ b/test/src/test/resources/saga/statelang/simple_statelang_with_complex_params_jackson.json @@ -0,0 +1,141 @@ +{ + "Name": "simpleStateMachineWithComplexParamsJackson", + "Comment": "甯﹀鏉傚弬鏁扮殑娴嬭瘯鐘舵€佹満瀹氫箟jackson鏍煎紡", + "StartState": "FirstState", + "Version": "0.0.1", + "States": { + "FirstState": { + "Type": "ServiceTask", + "ServiceName": "demoService", + "ServiceMethod": "complexParameterMethod", + "Next": "ChoiceState", + "ParameterTypes" : ["java.lang.String", "int", "io.seata.saga.engine.mock.DemoService$People", "[Lio.seata.saga.engine.mock.DemoService$People;", "java.util.List", "java.util.Map"], + "Input": [ + "$.[people].name", + "$.[people].age", + { + "@type": "io.seata.saga.engine.mock.DemoService$People", + "name": "lilei", + "age": 18, + "childrenArray": [ + "[Lio.seata.saga.engine.mock.DemoService$People;", + [ + { + "@type": "io.seata.saga.engine.mock.DemoService$People", + "name": "lilei", + "age": 18 + }, + { + "@type": "io.seata.saga.engine.mock.DemoService$People", + "name": "lilei", + "age": 18 + } + ] + ], + "childrenList": [ + "java.util.ArrayList", + [ + { + "@type": "io.seata.saga.engine.mock.DemoService$People", + "name": "lilei", + "age": 18 + }, + { + "@type": "io.seata.saga.engine.mock.DemoService$People", + "name": "lilei", + "age": 18 + } + ] + ], + "childrenMap": { + "@type": "java.util.LinkedHashMap", + "lilei": { + "@type": "io.seata.saga.engine.mock.DemoService$People", + "name": "lilei", + "age": 18 + } + } + }, + [ + "[Lio.seata.saga.engine.mock.DemoService$People;", + [ + { + "@type": "io.seata.saga.engine.mock.DemoService$People", + "name": "$.[people].name", + "age": "$.[people].age" + }, + { + "@type": "io.seata.saga.engine.mock.DemoService$People", + "name": "$.[people].name", + "age": "$.[people].age" + } + ] + ], + [ + "java.util.ArrayList", + [ + { + "@type": "io.seata.saga.engine.mock.DemoService$People", + "name": "$.[people].name", + "age": "$.[people].age" + } + ] + ], + { + "@type": "java.util.LinkedHashMap", + "lilei": { + "@type": "io.seata.saga.engine.mock.DemoService$People", + "name": "$.[people].name", + "age": "$.[people].age" + } + } + ], + "Output": { + "complexParameterMethodResult": "$.#root" + } + }, + "ChoiceState":{ + "Type": "Choice", + "Choices":[ + { + "Expression":"[complexParameterMethodResult].age > 0", + "Next":"SecondState" + }, + { + "Expression":"[complexParameterMethodResult].age <= 0", + "Next":"ThirdState" + } + ], + "Default":"Fail" + }, + "SecondState": { + "Type": "ServiceTask", + "ServiceName": "demoService", + "ServiceMethod": "interfaceParameterMethod", + "Input": [ + "$.[career]" + ], + "Output": { + "secondStateResult": "$.#root" + }, + "Next": "ThirdState" + }, + "ThirdState": { + "Type": "ServiceTask", + "ServiceName": "demoService", + "ServiceMethod": "interfaceParameterMethod", + "Input": [ + "$.[secondStateResult]" + ], + "Next": "Succeed" + }, + "Succeed": { + "Type":"Succeed" + }, + "Fail": { + "Type":"Fail", + "ErrorCode": "NOT_FOUND", + "Message": "not found" + } + } +} \ No newline at end of file