From e7c28c2353fe486777facc8a25189e5c2245d94a Mon Sep 17 00:00:00 2001 From: Janne Valkealahti Date: Sun, 1 May 2016 15:35:50 +0100 Subject: [PATCH] Add spel support for uml - Now Guards and Actions can be defined as expressions if 'spel' language is used. - Fixes #215 --- .../uml/support/UmlModelParser.java | 37 +++++- .../uml/UmlStateMachineModelFactoryTests.java | 45 +++++++ .../statemachine/uml/simple-spels.di | 2 + .../statemachine/uml/simple-spels.notation | 113 ++++++++++++++++++ .../statemachine/uml/simple-spels.uml | 32 +++++ 5 files changed, 226 insertions(+), 3 deletions(-) create mode 100644 spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-spels.di create mode 100644 spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-spels.notation create mode 100644 spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-spels.uml diff --git a/spring-statemachine-uml/src/main/java/org/springframework/statemachine/uml/support/UmlModelParser.java b/spring-statemachine-uml/src/main/java/org/springframework/statemachine/uml/support/UmlModelParser.java index 9952c08d..efe1674c 100644 --- a/spring-statemachine-uml/src/main/java/org/springframework/statemachine/uml/support/UmlModelParser.java +++ b/spring-statemachine-uml/src/main/java/org/springframework/statemachine/uml/support/UmlModelParser.java @@ -43,7 +43,11 @@ import org.eclipse.uml2.uml.Transition; import org.eclipse.uml2.uml.Trigger; import org.eclipse.uml2.uml.UMLPackage; import org.eclipse.uml2.uml.Vertex; +import org.springframework.expression.spel.SpelCompilerMode; +import org.springframework.expression.spel.SpelParserConfiguration; +import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.statemachine.action.Action; +import org.springframework.statemachine.action.SpelExpressionAction; import org.springframework.statemachine.config.model.ChoiceData; import org.springframework.statemachine.config.model.EntryData; import org.springframework.statemachine.config.model.ExitData; @@ -55,6 +59,7 @@ import org.springframework.statemachine.config.model.StatesData; import org.springframework.statemachine.config.model.TransitionData; import org.springframework.statemachine.config.model.TransitionsData; import org.springframework.statemachine.guard.Guard; +import org.springframework.statemachine.guard.SpelExpressionGuard; import org.springframework.statemachine.state.PseudoStateKind; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -68,6 +73,7 @@ import org.springframework.util.StringUtils; public class UmlModelParser { public final static String LANGUAGE_BEAN = "bean"; + public final static String LANGUAGE_SPEL = "spel"; private final Model model; private final StateMachineComponentResolver resolver; private final Collection> stateDatas = new ArrayList>(); @@ -311,6 +317,13 @@ public class UmlModelParser { String beanId = UmlUtils.resolveBodyByLanguage(LANGUAGE_BEAN, (OpaqueExpression)c.getSpecification()); if (StringUtils.hasText(beanId)) { guard = resolver.resolveGuard(beanId); + } else { + String expression = UmlUtils.resolveBodyByLanguage(LANGUAGE_SPEL, (OpaqueExpression)c.getSpecification()); + if (StringUtils.hasText(expression)) { + SpelExpressionParser parser = new SpelExpressionParser( + new SpelParserConfiguration(SpelCompilerMode.MIXED, null)); + guard = new SpelExpressionGuard(parser.parseExpression(expression)); + } } } } @@ -367,6 +380,15 @@ public class UmlModelParser { entrys.add(bean); stateData.setEntryActions(entrys); } + } else { + String expression = UmlUtils.resolveBodyByLanguage(LANGUAGE_SPEL, (OpaqueBehavior)state.getEntry()); + if (StringUtils.hasText(expression)) { + SpelExpressionParser parser = new SpelExpressionParser( + new SpelParserConfiguration(SpelCompilerMode.MIXED, null)); + ArrayList> entrys = new ArrayList>(); + entrys.add(new SpelExpressionAction(parser.parseExpression(expression))); + stateData.setEntryActions(entrys); + } } } if (state.getExit() instanceof OpaqueBehavior) { @@ -374,9 +396,18 @@ public class UmlModelParser { if (StringUtils.hasText(beanId)) { Action bean = resolver.resolveAction(beanId); if (bean != null) { - ArrayList> entrys = new ArrayList>(); - entrys.add(bean); - stateData.setExitActions(entrys); + ArrayList> exits = new ArrayList>(); + exits.add(bean); + stateData.setExitActions(exits); + } + } else { + String expression = UmlUtils.resolveBodyByLanguage(LANGUAGE_SPEL, (OpaqueBehavior)state.getExit()); + if (StringUtils.hasText(expression)) { + SpelExpressionParser parser = new SpelExpressionParser( + new SpelParserConfiguration(SpelCompilerMode.MIXED, null)); + ArrayList> exits = new ArrayList>(); + exits.add(new SpelExpressionAction(parser.parseExpression(expression))); + stateData.setExitActions(exits); } } } diff --git a/spring-statemachine-uml/src/test/java/org/springframework/statemachine/uml/UmlStateMachineModelFactoryTests.java b/spring-statemachine-uml/src/test/java/org/springframework/statemachine/uml/UmlStateMachineModelFactoryTests.java index 5946a95a..499766ec 100644 --- a/spring-statemachine-uml/src/test/java/org/springframework/statemachine/uml/UmlStateMachineModelFactoryTests.java +++ b/spring-statemachine-uml/src/test/java/org/springframework/statemachine/uml/UmlStateMachineModelFactoryTests.java @@ -538,6 +538,34 @@ public class UmlStateMachineModelFactoryTests extends AbstractUmlTests { assertThat(initialAction.latch.await(1, TimeUnit.SECONDS), is(true)); } + @Test + @SuppressWarnings("unchecked") + public void testSimpleSpelsAllow() throws Exception { + context.register(Config16.class); + context.refresh(); + StateMachine stateMachine = context.getBean(StateMachine.class); + + stateMachine.start(); + assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S1")); + stateMachine.sendEvent(MessageBuilder.withPayload("E1").setHeader("foo", "bar").build()); + assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S2")); + assertThat(stateMachine.getExtendedState().get("myvar1", String.class), is("myvalue1")); + assertThat(stateMachine.getExtendedState().get("myvar2", String.class), is("myvalue2")); + } + + @Test + @SuppressWarnings("unchecked") + public void testSimpleSpelsDeny() throws Exception { + context.register(Config16.class); + context.refresh(); + StateMachine stateMachine = context.getBean(StateMachine.class); + + stateMachine.start(); + assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S1")); + stateMachine.sendEvent("E1"); + assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S1")); + } + @Configuration @EnableStateMachine public static class Config2 extends StateMachineConfigurerAdapter { @@ -845,6 +873,23 @@ public class UmlStateMachineModelFactoryTests extends AbstractUmlTests { } } + @Configuration + @EnableStateMachine + public static class Config16 extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineModelConfigurer model) throws Exception { + model + .withModel() + .factory(modelFactory()); + } + + @Bean + public StateMachineModelFactory modelFactory() { + return new UmlStateMachineModelFactory("classpath:org/springframework/statemachine/uml/simple-spels.uml"); + } + } + public static class LatchAction implements Action { CountDownLatch latch = new CountDownLatch(1); @Override diff --git a/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-spels.di b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-spels.di new file mode 100644 index 00000000..bf9abab3 --- /dev/null +++ b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-spels.di @@ -0,0 +1,2 @@ + + diff --git a/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-spels.notation b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-spels.notation new file mode 100644 index 00000000..a10be693 --- /dev/null +++ b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-spels.notation @@ -0,0 +1,113 @@ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-spels.uml b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-spels.uml new file mode 100644 index 00000000..dd3c618b --- /dev/null +++ b/spring-statemachine-uml/src/test/resources/org/springframework/statemachine/uml/simple-spels.uml @@ -0,0 +1,32 @@ + + + + + + + + + spel + messageHeaders.get('foo')=='bar' + + + + + + + spel + extendedState.variables.put('myvar2','myvalue2') + + + + + spel + extendedState.variables.put('myvar1','myvalue1') + + + + + + + +