Fail closed on ambiguous polymorphic widening to prevent over-linking transitions.

Tighten linker, matcher, and canonicalizer guards for null sourceState inference, dynamic getter wildcards, predicate ceiling, and interface accessor ENUM_SET widen; update goldens for ambiguous internal triggers.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-13 06:40:28 +02:00
parent 2f853fb971
commit 1bf75c8a34
23 changed files with 414 additions and 252 deletions

View File

@@ -121,10 +121,34 @@ class TransitionLinkerEnricherTest {
enricher.enrich(result, null, null);
CallChain updatedChain = result.getMetadata().getCallChains().get(0);
assertThat(updatedChain.getMatchedTransitions()).hasSize(2);
assertThat(updatedChain.getMatchedTransitions())
.extracting("sourceState")
.containsExactlyInAnyOrder("NEW", "FAILED");
assertThat(updatedChain.getMatchedTransitions()).isNullOrEmpty();
assertThat(updatedChain.getTriggerPoint().isAmbiguous()).isTrue();
}
@Test
void shouldLinkTransitionByEventWhenSourceStateIsUniquelyDeterminedByTransitionTable() {
Transition t1 = new Transition();
t1.setSourceStates(List.of(State.of("NEW", "NEW")));
t1.setTargetStates(List.of(State.of("PAID", "PAID")));
t1.setEvent(Event.of("PAY", "PAY"));
CallChain chain = CallChain.builder()
.triggerPoint(TriggerPoint.builder().event("PAY").build())
.build();
AnalysisResult result = AnalysisResult.builder()
.transitions(List.of(t1))
.metadata(CodebaseMetadata.builder().callChains(List.of(chain)).build())
.build();
enricher.enrich(result, null, null);
CallChain updatedChain = result.getMetadata().getCallChains().get(0);
assertThat(updatedChain.getMatchedTransitions()).hasSize(1);
assertThat(updatedChain.getMatchedTransitions().get(0).getSourceState()).isEqualTo("NEW");
assertThat(updatedChain.getMatchedTransitions().get(0).getTargetState()).isEqualTo("PAID");
assertThat(updatedChain.getTriggerPoint().getSourceState()).isEqualTo("NEW");
assertThat(updatedChain.getTriggerPoint().isAmbiguous()).isFalse();
}
@Test
@@ -307,7 +331,7 @@ class TransitionLinkerEnricherTest {
enricher.enrich(result, null, null);
CallChain updatedChain = result.getMetadata().getCallChains().get(0);
assertThat(updatedChain.getMatchedTransitions()).hasSize(1);
assertThat(updatedChain.getMatchedTransitions()).isNullOrEmpty();
}
@Test

View File

@@ -284,4 +284,35 @@ class StrictFqnMatchingEngineTest {
assertThat(engine.matches(smEvent, triggerPoint)).isFalse();
}
@Test
void shouldNotMatchGetterChainWithoutPolymorphicEventsAgainstBareStringEvent() {
Event smEvent = Event.of("PAY", "PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("richEvent.getId()")
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isFalse();
}
@Test
void shouldNotMatchGetterChainWithoutPolymorphicEventsAgainstEnumEvent() {
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("payload.getType()")
.eventTypeFqn("com.example.OrderEvents")
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isFalse();
}
@Test
void shouldNotMatchMethodCallWithoutPolymorphicEventsAgainstBareStringEvent() {
Event smEvent = Event.of("PAY", "PAY");
TriggerPoint triggerPoint = TriggerPoint.builder()
.event("getType()")
.build();
assertThat(engine.matches(smEvent, triggerPoint)).isFalse();
}
}

View File

@@ -282,7 +282,7 @@ class EnumMemberPredicatePolymorphicInferenceTest {
}
@Test
void shouldFallBackToTransitionEventsWhenPredicateEvaluationIsInconclusive(@TempDir Path tempDir) throws Exception {
void shouldFailClosedWhenPredicateMethodCannotBeResolved(@TempDir Path tempDir) throws Exception {
CodebaseContext context = scanEnumProject(tempDir, """
package com.example.order;
public enum OrderCommand {
@@ -304,10 +304,7 @@ class EnumMemberPredicatePolymorphicInferenceTest {
TriggerPoint enriched = MachineEnumCanonicalizer.ensureCallChainPolymorphicEvents(
trigger, types, context, transitions, true);
assertThat(enriched.getPolymorphicEvents())
.containsExactlyInAnyOrder(
"com.example.order.OrderCommand.PAY",
"com.example.order.OrderCommand.SHIP");
assertThat(enriched.getPolymorphicEvents()).isNullOrEmpty();
}
@Test

View File

@@ -293,7 +293,7 @@ class MachineEnumCanonicalizerTest {
}
@Test
void shouldInferPolymorphicEventsFromTransitionsWhenContextIsUnavailable() {
void shouldNotInferMultipleTransitionEventsForUnresolvedValueOf() {
Transition pay = new Transition();
pay.setEvent(Event.of("OrderEvent.PAY", "com.example.order.OrderEvent.PAY"));
Transition ship = new Transition();
@@ -309,11 +309,44 @@ class MachineEnumCanonicalizerTest {
TriggerPoint linked = MachineEnumCanonicalizer.ensureCallChainPolymorphicEvents(
trigger, types, null, List.of(pay, ship));
assertThat(linked.getPolymorphicEvents()).isNullOrEmpty();
assertThat(linked.isAmbiguous()).isFalse();
}
@Test
void shouldInferSingleTransitionEventForValueOfWhenOnlyOneConfigured() {
Transition pay = new Transition();
pay.setEvent(Event.of("OrderEvent.PAY", "com.example.order.OrderEvent.PAY"));
TriggerPoint trigger = TriggerPoint.builder()
.event("OrderEvent.valueOf(eventString)")
.build();
StateMachineTypeResolver.MachineTypes types = new StateMachineTypeResolver.MachineTypes(
"com.example.order.OrderState", "com.example.order.OrderEvent");
TriggerPoint linked = MachineEnumCanonicalizer.ensureCallChainPolymorphicEvents(
trigger, types, null, List.of(pay));
assertThat(linked.getPolymorphicEvents())
.containsExactlyInAnyOrder(
"com.example.order.OrderEvent.PAY",
"com.example.order.OrderEvent.SHIP");
assertThat(linked.isAmbiguous()).isTrue();
.containsExactly("com.example.order.OrderEvent.PAY");
assertThat(linked.isAmbiguous()).isFalse();
}
@Test
void shouldNotExpandSymbolicToMultipleConfiguredTransitionEvents() {
Transition pay = new Transition();
pay.setEvent(Event.of("OrderEvent.PAY", "com.example.order.OrderEvent.PAY"));
Transition ship = new Transition();
ship.setEvent(Event.of("OrderEvent.SHIP", "com.example.order.OrderEvent.SHIP"));
List<String> expanded = MachineEnumCanonicalizer.expandSymbolicPolymorphicEvents(
List.of("<SYMBOLIC: com.example.order.OrderEvent.*>"),
"com.example.order.OrderEvent",
null,
List.of(pay, ship));
assertThat(expanded).isEmpty();
}
@Test

View File

@@ -77,6 +77,47 @@ class DispatcherPolyCeilingPipelineTest {
.containsExactlyInAnyOrder(EVENT_TYPE + ".PAY", EVENT_TYPE + ".SHIP");
}
@Test
void linkerShouldNotInferAllTransitionEventsForUnresolvedValueOf(@TempDir Path tempDir) throws Exception {
String source = """
package com.example;
public class ApiController {
CentralDispatcher dispatcher;
public void handle(String action) { dispatcher.route(action); }
}
class CentralDispatcher {
StateMachine machine;
public void route(String action) { machine.sendEvent(OrderEvent.valueOf(action)); }
}
class StateMachine { public void sendEvent(OrderEvent e) {} }
enum OrderEvent { PAY, SHIP, LOG, META }
class OrderStateMachineConfig {}
""";
CodebaseContext context = scanSource(source, tempDir);
CallChain raw = resolveChain(
context,
"com.example.ApiController",
"handle",
"com.example.StateMachine",
"sendEvent",
EngineKind.JDT);
TriggerPoint stripped = raw.getTriggerPoint().toBuilder()
.polymorphicEvents(null)
.build();
CallChain chain = raw.toBuilder().triggerPoint(stripped).build();
Transition pay = transition("NEW", "PAID", EVENT_TYPE + ".PAY");
Transition ship = transition("PAID", "SHIPPED", EVENT_TYPE + ".SHIP");
CallChain linked = linkChain(
context, chain, MACHINE_CONFIG, EVENT_TYPE, STATE_TYPE, pay, ship);
assertThat(linked.getTriggerPoint().getPolymorphicEvents()).isNullOrEmpty();
assertThat(linked.getMatchedTransitions()).isNullOrEmpty();
}
@Test
void linkerShouldCapCallGraphPolyForDeepDispatcherToTransitionCeiling(@TempDir Path tempDir) throws Exception {
String source = """

View File

@@ -1,12 +1,7 @@
package click.kamil.springstatemachineexporter.analysis.service;
import click.kamil.springstatemachineexporter.analysis.model.AnalysisResult;
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
import click.kamil.springstatemachineexporter.analysis.model.CodebaseMetadata;
import click.kamil.springstatemachineexporter.exporter.ExportOptions;
import click.kamil.springstatemachineexporter.exporter.JsonExporter;
import click.kamil.springstatemachineexporter.service.ExportService;
import click.kamil.springstatemachineexporter.service.JsonImportService;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
@@ -19,92 +14,15 @@ import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Regression for REST dispatcher chains that lost {@code polymorphicEvents} during JSON round-trip.
* Regression for REST dispatcher chains: JSON round-trip must not re-infer all transition events
* for generic {@code {event}} endpoints.
*/
class EnterpriseDispatcherMatchedTransitionsRegressionTest {
@Test
void jsonReExportWithSourceShouldRestoreDispatcherMatchedTransitions(@TempDir Path tempDir) throws Exception {
Path enterprise = findProjectRoot().resolve("state_machines/state_machine_enterprise");
Path astOut = tempDir.resolve("ast");
ExportService exportService = new ExportService(List.of(new JsonExporter()));
exportService.runExporter(enterprise, astOut, List.of("json"), true, List.of(), null, null,
click.kamil.springstatemachineexporter.exporter.EnumFormat.fn,
click.kamil.springstatemachineexporter.exporter.EnumFormat.fn);
Path astJson = astOut.resolve(
"click.kamil.enterprise.machines.order.OrderStateMachineConfiguration/click.kamil.enterprise.machines.order.OrderStateMachineConfiguration.json");
AnalysisResult imported = new JsonImportService().importAnalysisResult(astJson);
List<CallChain> corruptedChains = imported.getMetadata().getCallChains().stream()
.map(chain -> {
if (chain.getTriggerPoint() == null
|| chain.getMethodChain() == null
|| !chain.getMethodChain().stream()
.anyMatch(m -> m.contains("StateMachineDispatcher.dispatch"))) {
return chain;
}
return chain.toBuilder()
.triggerPoint(chain.getTriggerPoint().toBuilder()
.event("event")
.external(false)
.polymorphicEvents(null)
.build())
.matchedTransitions(null)
.build();
})
.toList();
imported.setMetadata(CodebaseMetadata.builder()
.triggers(imported.getMetadata().getTriggers())
.entryPoints(imported.getMetadata().getEntryPoints())
.callChains(corruptedChains)
.properties(imported.getMetadata().getProperties())
.build());
Path brokenJson = tempDir.resolve("broken-order-machine.json");
Files.writeString(brokenJson, new JsonExporter().export(imported, ExportOptions.builder().build()));
Path outputDir = tempDir.resolve("out");
exportService.runJsonExporter(
brokenJson, outputDir, List.of("json"), List.of(),
click.kamil.springstatemachineexporter.exporter.EnumFormat.fn,
click.kamil.springstatemachineexporter.exporter.EnumFormat.fn,
enterprise);
Path finalized = outputDir.resolve(
"click.kamil.enterprise.machines.order.OrderStateMachineConfiguration/click.kamil.enterprise.machines.order.OrderStateMachineConfiguration.json");
AnalysisResult roundTripped = new JsonImportService().importAnalysisResult(finalized);
assertThat(roundTripped.getMetadata().getCallChains())
.as("call chains after re-export")
.isNotEmpty();
CallChain chain = roundTripped.getMetadata().getCallChains().stream()
.filter(c -> c.getMethodChain() != null && c.getMethodChain().stream()
.anyMatch(m -> m.contains("StateMachineDispatcher.dispatch")))
.findFirst()
.orElseThrow(() -> new IllegalStateException(
"no dispatcher chain among: "
+ roundTripped.getMetadata().getCallChains().stream()
.map(c -> c.getEntryPoint() != null ? c.getEntryPoint().getName() : "null")
.toList()));
assertThat(chain.getTriggerPoint().getPolymorphicEvents())
.as("polymorphicEvents on %s", chain.getEntryPoint().getName())
.isNotNull()
.isNotEmpty();
assertThat(chain.getTriggerPoint().isExternal())
.as("external flag on %s", chain.getEntryPoint().getName())
.isTrue();
assertThat(chain.getMatchedTransitions())
.as("matchedTransitions on %s", chain.getEntryPoint().getName())
.isNotNull()
.isNotEmpty();
}
private static final String TRANSITION_ENDPOINT = "POST /api/machine/{machineType}/transition/{event}";
@Test
void astExportTransitionEndpointShouldLinkMatchedTransitions(@TempDir Path tempDir) throws Exception {
void astExportGenericTransitionEndpointShouldRemainUnlinked(@TempDir Path tempDir) throws Exception {
Path enterprise = findProjectRoot().resolve("state_machines/state_machine_enterprise");
ExportService exportService = new ExportService(List.of(new JsonExporter()));
exportService.runExporter(enterprise, tempDir, List.of("json"), true, List.of(), null, null,
@@ -116,15 +34,14 @@ class EnterpriseDispatcherMatchedTransitionsRegressionTest {
assertThat(chain.path("triggerPoint").path("external").asBoolean()).isTrue();
assertThat(chain.path("triggerPoint").path("polymorphicEvents").isArray()).isTrue();
assertThat(chain.path("triggerPoint").path("polymorphicEvents")).isNotEmpty();
assertThat(chain.path("matchedTransitions").isArray()).isTrue();
assertThat(chain.path("matchedTransitions")).isNotEmpty();
assertThat(chain.path("triggerPoint").path("polymorphicEvents")).isEmpty();
JsonNode matched = chain.path("matchedTransitions");
assertThat(matched.isNull() || (matched.isArray() && matched.isEmpty())).isTrue();
}
private static JsonNode findTransitionEndpointChain(JsonNode machineJson) {
for (JsonNode chain : machineJson.path("metadata").path("callChains")) {
if ("POST /api/machine/{machineType}/transition/{event}".equals(
chain.path("entryPoint").path("name").asText())) {
if (TRANSITION_ENDPOINT.equals(chain.path("entryPoint").path("name").asText())) {
return chain;
}
}

View File

@@ -0,0 +1,112 @@
package click.kamil.springstatemachineexporter.analysis.service;
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
import click.kamil.springstatemachineexporter.model.Transition;
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.Path;
import static click.kamil.springstatemachineexporter.analysis.service.CentralDispatcherTestSupport.*;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Interface accessor widen must not union all implementation return values at ambiguous call sites.
*/
class InterfaceAccessorPolyPipelineTest {
private static final String MACHINE_CONFIG = "com.example.OrderStateMachineConfig";
private static final String EVENT_TYPE = "com.example.OrderEvent";
private static final String STATE_TYPE = "com.example.OrderState";
@Test
void linkerShouldNotMatchAllTransitionsForInterfaceTypedEventSource(@TempDir Path tempDir) throws Exception {
String source = """
package com.example;
public class ApiController {
EventHandler handler;
public void dispatch() { handler.fire(); }
}
class EventHandler {
StateMachine machine;
EventSource source;
void fire() { machine.sendEvent(source.getEvent()); }
}
interface EventSource { OrderEvent getEvent(); }
class PaySource implements EventSource {
public OrderEvent getEvent() { return OrderEvent.PAY; }
}
class ShipSource implements EventSource {
public OrderEvent getEvent() { return OrderEvent.SHIP; }
}
class StateMachine { public void sendEvent(OrderEvent e) {} }
enum OrderEvent { PAY, SHIP, LOG, META }
class OrderStateMachineConfig {}
""";
CodebaseContext context = scanSource(source, tempDir);
Transition pay = transition("NEW", "PAID", EVENT_TYPE + ".PAY");
Transition ship = transition("PAID", "SHIPPED", EVENT_TYPE + ".SHIP");
CallChain raw = resolveChain(context, "com.example.ApiController", "dispatch",
"com.example.StateMachine", "sendEvent", EngineKind.JDT);
CallChain linked = linkChain(
context,
raw,
MACHINE_CONFIG,
EVENT_TYPE,
STATE_TYPE,
pay,
ship);
assertThat(linked.getTriggerPoint().isAmbiguous()).isTrue();
assertThat(linked.getMatchedTransitions()).isNullOrEmpty();
}
@Test
void linkerShouldResolveConcretePaySourceImplementation(@TempDir Path tempDir) throws Exception {
String source = """
package com.example;
public class ApiController {
EventHandler handler;
public void pay() { handler.fire(new PaySource()); }
}
class EventHandler {
StateMachine machine;
void fire(EventSource source) { machine.sendEvent(source.getEvent()); }
}
interface EventSource { OrderEvent getEvent(); }
class PaySource implements EventSource {
public OrderEvent getEvent() { return OrderEvent.PAY; }
}
class ShipSource implements EventSource {
public OrderEvent getEvent() { return OrderEvent.SHIP; }
}
class StateMachine { public void sendEvent(OrderEvent e) {} }
enum OrderEvent { PAY, SHIP, LOG, META }
class OrderStateMachineConfig {}
""";
CodebaseContext context = scanSource(source, tempDir);
Transition pay = transition("NEW", "PAID", EVENT_TYPE + ".PAY");
Transition ship = transition("PAID", "SHIPPED", EVENT_TYPE + ".SHIP");
CallChain linked = resolveLinkAndAssertSingleMatch(
context,
"com.example.ApiController",
"pay",
"com.example.StateMachine",
"sendEvent",
MACHINE_CONFIG,
EVENT_TYPE,
STATE_TYPE,
EVENT_TYPE + ".PAY",
EngineKind.JDT,
pay,
ship);
assertPolyCappedToTransitionEvents(linked, EVENT_TYPE, pay, ship);
}
}

View File

@@ -181,25 +181,13 @@
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 87,
"polymorphicEvents" : [ "click.kamil.enterprise.machines.document.DocumentEvent.SUBMIT", "click.kamil.enterprise.machines.document.DocumentEvent.APPROVE", "click.kamil.enterprise.machines.document.DocumentEvent.REJECT" ],
"polymorphicEvents" : [ ],
"external" : true,
"constraint" : "!(\"ORDER\".equalsIgnoreCase(machineType)) && \"DOCUMENT\".equalsIgnoreCase(machineType)",
"ambiguous" : false
"ambiguous" : true
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "click.kamil.enterprise.machines.document.DocumentState.DRAFT",
"targetState" : "click.kamil.enterprise.machines.document.DocumentState.IN_REVIEW",
"event" : "click.kamil.enterprise.machines.document.DocumentEvent.SUBMIT"
}, {
"sourceState" : "click.kamil.enterprise.machines.document.DocumentState.IN_REVIEW",
"targetState" : "click.kamil.enterprise.machines.document.DocumentState.APPROVED",
"event" : "click.kamil.enterprise.machines.document.DocumentEvent.APPROVE"
}, {
"sourceState" : "click.kamil.enterprise.machines.document.DocumentState.IN_REVIEW",
"targetState" : "click.kamil.enterprise.machines.document.DocumentState.DRAFT",
"event" : "click.kamil.enterprise.machines.document.DocumentEvent.REJECT"
} ]
"matchedTransitions" : null
} ],
"properties" : {
"default" : { }

View File

@@ -364,7 +364,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/enterprise/service/OrderServiceImpl.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "java.lang.String.NEW",
"lineNumber" : 16,
"polymorphicEvents" : [ "java.lang.String.PLACE_ORDER" ],
"external" : false,
@@ -402,7 +402,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/enterprise/service/OrderServiceImpl.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "java.lang.String.PAID",
"lineNumber" : 21,
"polymorphicEvents" : [ "java.lang.String.CANCEL_ORDER" ],
"external" : false,
@@ -469,7 +469,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/enterprise/service/ReactivePaymentService.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "java.lang.String.PENDING_PAYMENT",
"lineNumber" : 18,
"polymorphicEvents" : [ "java.lang.String.PAY_ORDER" ],
"external" : false,
@@ -507,7 +507,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/enterprise/messaging/ShippingJmsListener.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "java.lang.String.PAID",
"lineNumber" : 17,
"polymorphicEvents" : null,
"external" : false,
@@ -545,7 +545,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/enterprise/messaging/ReturnsRabbitListener.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "java.lang.String.DELIVERED",
"lineNumber" : 17,
"polymorphicEvents" : null,
"external" : false,

View File

@@ -79,7 +79,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/inheritance/service/ProcessingServiceImpl.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "java.lang.String.START",
"lineNumber" : 15,
"polymorphicEvents" : [ "java.lang.String.INHERITED_SUBMIT" ],
"external" : false,

View File

@@ -101,7 +101,7 @@
"sourceFile" : "core-module/src/main/java/click/kamil/maven/core/MavenOrderStateMachine.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "java.lang.String.INIT",
"lineNumber" : 40,
"polymorphicEvents" : null,
"external" : false,

View File

@@ -114,7 +114,7 @@
"sourceFile" : "core-module/src/main/java/click/kamil/multi/core/OrderController.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "java.lang.String.NEW",
"lineNumber" : 18,
"polymorphicEvents" : null,
"external" : false,

View File

@@ -158,21 +158,13 @@
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 81,
"polymorphicEvents" : [ "click.kamil.enterprise.machines.order.OrderEvent.PAY", "click.kamil.enterprise.machines.order.OrderEvent.SHIP" ],
"polymorphicEvents" : [ ],
"external" : true,
"constraint" : "\"ORDER\".equalsIgnoreCase(machineType)",
"ambiguous" : false
"ambiguous" : true
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "click.kamil.enterprise.machines.order.OrderState.NEW",
"targetState" : "click.kamil.enterprise.machines.order.OrderState.PENDING",
"event" : "click.kamil.enterprise.machines.order.OrderEvent.PAY"
}, {
"sourceState" : "click.kamil.enterprise.machines.order.OrderState.PENDING",
"targetState" : "click.kamil.enterprise.machines.order.OrderState.SHIPPED",
"event" : "click.kamil.enterprise.machines.order.OrderEvent.SHIP"
} ]
"matchedTransitions" : null
} ],
"properties" : {
"default" : { }

View File

@@ -287,7 +287,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.SUBMITTED",
"lineNumber" : 13,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.polymorphic.OrderEvents.PAY" ],
"external" : false,
@@ -321,7 +321,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.PAID",
"lineNumber" : 13,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.polymorphic.OrderEvents.FULFILL" ],
"external" : false,
@@ -355,7 +355,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.SUBMITTED",
"lineNumber" : 13,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.polymorphic.OrderEvents.CANCEL" ],
"external" : false,
@@ -389,7 +389,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.SUBMITTED",
"lineNumber" : 17,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.polymorphic.OrderEvents.PAY" ],
"external" : false,
@@ -423,7 +423,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.SUBMITTED",
"lineNumber" : 13,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.polymorphic.OrderEvents.PAY" ],
"external" : false,
@@ -457,7 +457,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.SUBMITTED",
"lineNumber" : 13,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.polymorphic.OrderEvents.PAY" ],
"external" : false,
@@ -500,18 +500,10 @@
"polymorphicEvents" : [ "click.kamil.examples.statemachine.polymorphic.OrderEvents.PAY", "click.kamil.examples.statemachine.polymorphic.OrderEvents.CANCEL" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
"ambiguous" : true
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.SUBMITTED",
"targetState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.PAID",
"event" : "click.kamil.examples.statemachine.polymorphic.OrderEvents.PAY"
}, {
"sourceState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.SUBMITTED",
"targetState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.CANCELED",
"event" : "click.kamil.examples.statemachine.polymorphic.OrderEvents.CANCEL"
} ]
"matchedTransitions" : null
}, {
"entryPoint" : {
"type" : "REST",
@@ -533,7 +525,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.SUBMITTED",
"lineNumber" : 13,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.polymorphic.OrderEvents.PAY" ],
"external" : false,
@@ -567,7 +559,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.PAID",
"lineNumber" : 13,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.polymorphic.OrderEvents.FULFILL" ],
"external" : false,
@@ -601,7 +593,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.SUBMITTED",
"lineNumber" : 13,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.polymorphic.OrderEvents.CANCEL" ],
"external" : false,
@@ -635,7 +627,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.PAID",
"lineNumber" : 21,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.polymorphic.OrderEvents.ABCD" ],
"external" : false,
@@ -674,18 +666,10 @@
"polymorphicEvents" : [ "click.kamil.examples.statemachine.polymorphic.OrderEvents.ABCD", "click.kamil.examples.statemachine.polymorphic.OrderEvents.PAY" ],
"external" : false,
"constraint" : null,
"ambiguous" : false
"ambiguous" : true
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.SUBMITTED",
"targetState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.PAID",
"event" : "click.kamil.examples.statemachine.polymorphic.OrderEvents.PAY"
}, {
"sourceState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.PAID",
"targetState" : "click.kamil.examples.statemachine.polymorphic.OrderStates.CANCELED",
"event" : "click.kamil.examples.statemachine.polymorphic.OrderEvents.ABCD"
} ]
"matchedTransitions" : null
} ],
"properties" : {
"default" : {

View File

@@ -156,7 +156,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.layered.document.DocumentState.DRAFT",
"lineNumber" : 81,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.layered.document.DocumentTransitionEvent.SUBMIT" ],
"external" : false,
@@ -190,7 +190,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.layered.document.DocumentState.SUBMITTED",
"lineNumber" : 81,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.layered.document.DocumentTransitionEvent.APPROVE" ],
"external" : false,
@@ -224,7 +224,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.layered.document.DocumentState.SUBMITTED",
"lineNumber" : 81,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.layered.document.DocumentTransitionEvent.REJECT" ],
"external" : false,
@@ -262,7 +262,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.layered.document.DocumentState.DRAFT",
"lineNumber" : 81,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.layered.document.DocumentTransitionEvent.SUBMIT" ],
"external" : false,
@@ -300,7 +300,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.layered.document.DocumentState.SUBMITTED",
"lineNumber" : 81,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.layered.document.DocumentTransitionEvent.APPROVE" ],
"external" : false,
@@ -338,7 +338,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.layered.document.DocumentState.SUBMITTED",
"lineNumber" : 81,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.layered.document.DocumentTransitionEvent.REJECT" ],
"external" : false,

View File

@@ -200,7 +200,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.layered.order.OrderState.NEW",
"lineNumber" : 75,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.layered.order.OrderTransitionEvent.PAY" ],
"external" : false,
@@ -234,7 +234,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.layered.order.OrderState.PAID",
"lineNumber" : 75,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.layered.order.OrderTransitionEvent.SHIP" ],
"external" : false,
@@ -268,7 +268,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.layered.order.OrderState.PAID",
"lineNumber" : 75,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.layered.order.OrderTransitionEvent.CANCEL" ],
"external" : false,
@@ -302,7 +302,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.layered.order.OrderState.NEW",
"lineNumber" : 75,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.layered.order.OrderTransitionEvent.PAY" ],
"external" : false,
@@ -336,7 +336,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.layered.order.OrderState.PAID",
"lineNumber" : 75,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.layered.order.OrderTransitionEvent.SHIP" ],
"external" : false,
@@ -370,7 +370,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.layered.order.OrderState.NEW",
"lineNumber" : 75,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.layered.order.OrderTransitionEvent.PAY" ],
"external" : false,
@@ -404,7 +404,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.layered.order.OrderState.PAID",
"lineNumber" : 75,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.layered.order.OrderTransitionEvent.SHIP" ],
"external" : false,
@@ -442,7 +442,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.layered.order.OrderState.NEW",
"lineNumber" : 75,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.layered.order.OrderTransitionEvent.PAY" ],
"external" : false,
@@ -480,7 +480,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.layered.order.OrderState.PAID",
"lineNumber" : 75,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.layered.order.OrderTransitionEvent.SHIP" ],
"external" : false,
@@ -518,7 +518,7 @@
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.examples.statemachine.layered.order.OrderState.PAID",
"lineNumber" : 75,
"polymorphicEvents" : [ "click.kamil.examples.statemachine.layered.order.OrderTransitionEvent.CANCEL" ],
"external" : false,

View File

@@ -240,7 +240,7 @@
"sourceFile" : "service/src/main/java/click/kamil/service/StateMachineServiceImpl.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.domain.OrderState.NEW",
"lineNumber" : 25,
"polymorphicEvents" : [ "click.kamil.domain.OrderEvent.PROCESS" ],
"external" : false,
@@ -274,7 +274,7 @@
"sourceFile" : "service/src/main/java/click/kamil/service/StateMachineServiceImpl.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.domain.OrderState.PROCESSING",
"lineNumber" : 25,
"polymorphicEvents" : [ "click.kamil.domain.OrderEvent.COMPLETE" ],
"external" : false,
@@ -313,18 +313,10 @@
"polymorphicEvents" : [ "click.kamil.domain.OrderEvent.CANCEL" ],
"external" : false,
"constraint" : "event instanceof OrderEvent",
"ambiguous" : false
"ambiguous" : true
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "click.kamil.domain.OrderState.NEW",
"targetState" : "click.kamil.domain.OrderState.CANCELLED",
"event" : "click.kamil.domain.OrderEvent.CANCEL"
}, {
"sourceState" : "click.kamil.domain.OrderState.PROCESSING",
"targetState" : "click.kamil.domain.OrderState.CANCELLED",
"event" : "click.kamil.domain.OrderEvent.CANCEL"
} ]
"matchedTransitions" : null
}, {
"entryPoint" : {
"type" : "REST",
@@ -346,7 +338,7 @@
"sourceFile" : "service/src/main/java/click/kamil/service/StateMachineServiceImpl.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.domain.OrderState.PROCESSING",
"lineNumber" : 25,
"polymorphicEvents" : [ "click.kamil.domain.OrderEvent.COMPLETE" ],
"external" : false,
@@ -384,7 +376,7 @@
"sourceFile" : "service/src/main/java/click/kamil/service/StateMachineServiceImpl.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.domain.OrderState.NEW",
"lineNumber" : 78,
"polymorphicEvents" : [ "click.kamil.domain.OrderEvent.PROCESS" ],
"external" : false,
@@ -422,7 +414,7 @@
"sourceFile" : "service/src/main/java/click/kamil/service/StateMachineServiceImpl.java",
"sourceModule" : null,
"stateMachineId" : null,
"sourceState" : null,
"sourceState" : "click.kamil.domain.OrderState.NEW",
"lineNumber" : 25,
"polymorphicEvents" : [ "click.kamil.domain.OrderEvent.PROCESS" ],
"external" : false,
@@ -465,18 +457,10 @@
"polymorphicEvents" : [ "click.kamil.domain.OrderEvent.CANCEL" ],
"external" : false,
"constraint" : "event != null",
"ambiguous" : false
"ambiguous" : true
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "click.kamil.domain.OrderState.NEW",
"targetState" : "click.kamil.domain.OrderState.CANCELLED",
"event" : "click.kamil.domain.OrderEvent.CANCEL"
}, {
"sourceState" : "click.kamil.domain.OrderState.PROCESSING",
"targetState" : "click.kamil.domain.OrderState.CANCELLED",
"event" : "click.kamil.domain.OrderEvent.CANCEL"
} ]
"matchedTransitions" : null
} ],
"properties" : {
"default" : { }

View File

@@ -151,21 +151,13 @@
"stateMachineId" : null,
"sourceState" : null,
"lineNumber" : 93,
"polymorphicEvents" : [ "click.kamil.enterprise.machines.user.UserEvent.REGISTER", "click.kamil.enterprise.machines.user.UserEvent.VERIFY" ],
"polymorphicEvents" : [ ],
"external" : true,
"constraint" : "!(\"ORDER\".equalsIgnoreCase(machineType)) && !(\"DOCUMENT\".equalsIgnoreCase(machineType)) && \"USER\".equalsIgnoreCase(machineType)",
"ambiguous" : false
"ambiguous" : true
},
"contextMachineId" : null,
"matchedTransitions" : [ {
"sourceState" : "click.kamil.enterprise.machines.user.UserState.GUEST",
"targetState" : "click.kamil.enterprise.machines.user.UserState.REGISTERED",
"event" : "click.kamil.enterprise.machines.user.UserEvent.REGISTER"
}, {
"sourceState" : "click.kamil.enterprise.machines.user.UserState.REGISTERED",
"targetState" : "click.kamil.enterprise.machines.user.UserState.VERIFIED",
"event" : "click.kamil.enterprise.machines.user.UserEvent.VERIFY"
} ]
"matchedTransitions" : null
} ],
"properties" : {
"default" : { }