Prune switch-dispatch call paths and scope analysis per state machine.
Switch-case constraints on call-graph edges and forward path binding stop string-key endpoints from linking every dispatch arm; machine-scoped triggers/call chains reduce cross-config noise, with pipeline bug fixes and regression coverage. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -18,8 +18,9 @@ public class CallChainEnricher implements AnalysisEnricher {
|
|||||||
|
|
||||||
List<CallChain> chains = intelligence.findCallChains(
|
List<CallChain> chains = intelligence.findCallChains(
|
||||||
result.getMetadata().getEntryPoints(),
|
result.getMetadata().getEntryPoints(),
|
||||||
result.getMetadata().getTriggers()
|
intelligence.findTriggerPoints()
|
||||||
);
|
);
|
||||||
|
chains = MachineScopeFilter.filterCallChainsForMachine(chains, result.getName(), context);
|
||||||
|
|
||||||
result.addMetadata(CodebaseMetadata.builder()
|
result.addMetadata(CodebaseMetadata.builder()
|
||||||
.callChains(chains)
|
.callChains(chains)
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.enricher;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.enricher.routing.BeanResolutionEngine;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.enricher.routing.HeuristicBeanResolutionEngine;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters codebase-wide analysis artifacts to those relevant for a single state machine config.
|
||||||
|
*/
|
||||||
|
public final class MachineScopeFilter {
|
||||||
|
|
||||||
|
private static final BeanResolutionEngine ROUTING = new HeuristicBeanResolutionEngine();
|
||||||
|
|
||||||
|
private MachineScopeFilter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<TriggerPoint> filterTriggersForMachine(
|
||||||
|
List<TriggerPoint> triggers, String machineName, CodebaseContext context) {
|
||||||
|
if (triggers == null || machineName == null) {
|
||||||
|
return triggers == null ? List.of() : triggers;
|
||||||
|
}
|
||||||
|
List<TriggerPoint> filtered = new ArrayList<>();
|
||||||
|
for (TriggerPoint trigger : triggers) {
|
||||||
|
if (isTriggerForMachine(trigger, machineName, context)) {
|
||||||
|
filtered.add(trigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<CallChain> filterCallChainsForMachine(
|
||||||
|
List<CallChain> chains, String machineName, CodebaseContext context) {
|
||||||
|
if (chains == null || machineName == null) {
|
||||||
|
return chains == null ? List.of() : chains;
|
||||||
|
}
|
||||||
|
List<CallChain> filtered = new ArrayList<>();
|
||||||
|
for (CallChain chain : chains) {
|
||||||
|
if (ROUTING.isRoutedToCorrectMachine(chain, machineName, context)) {
|
||||||
|
filtered.add(chain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isTriggerForMachine(TriggerPoint trigger, String machineName, CodebaseContext context) {
|
||||||
|
if (trigger == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CallChain probe = CallChain.builder().triggerPoint(trigger).methodChain(List.of()).build();
|
||||||
|
return ROUTING.isRoutedToCorrectMachine(probe, machineName, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -93,9 +93,11 @@ public class TransitionLinkerEnricher implements AnalysisEnricher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the metadata with the new call chains
|
List<TriggerPoint> scopedTriggers = MachineScopeFilter.filterTriggersForMachine(
|
||||||
|
result.getMetadata().getTriggers(), result.getName(), context);
|
||||||
|
|
||||||
CodebaseMetadata updatedMetadata = CodebaseMetadata.builder()
|
CodebaseMetadata updatedMetadata = CodebaseMetadata.builder()
|
||||||
.triggers(result.getMetadata().getTriggers())
|
.triggers(scopedTriggers)
|
||||||
.entryPoints(result.getMetadata().getEntryPoints())
|
.entryPoints(result.getMetadata().getEntryPoints())
|
||||||
.callChains(updatedChains)
|
.callChains(updatedChains)
|
||||||
.properties(result.getMetadata().getProperties())
|
.properties(result.getMetadata().getProperties())
|
||||||
|
|||||||
@@ -16,9 +16,8 @@ public class TriggerEnricher implements AnalysisEnricher {
|
|||||||
log.info("Enriching {} with triggers", result.getName());
|
log.info("Enriching {} with triggers", result.getName());
|
||||||
|
|
||||||
List<TriggerPoint> triggers = intelligence.findTriggerPoints();
|
List<TriggerPoint> triggers = intelligence.findTriggerPoints();
|
||||||
|
triggers = MachineScopeFilter.filterTriggersForMachine(triggers, result.getName(), context);
|
||||||
// Initially, we just add all triggers found in the codebase to the metadata.
|
|
||||||
// In later steps of Phase 2, we will filter them by State Machine ID.
|
|
||||||
result.addMetadata(click.kamil.springstatemachineexporter.analysis.model.CodebaseMetadata.builder()
|
result.addMetadata(click.kamil.springstatemachineexporter.analysis.model.CodebaseMetadata.builder()
|
||||||
.triggers(triggers)
|
.triggers(triggers)
|
||||||
.build());
|
.build());
|
||||||
|
|||||||
@@ -219,6 +219,18 @@ public final class AccessorResolver {
|
|||||||
if (!cicValues.isEmpty()) {
|
if (!cicValues.isEmpty()) {
|
||||||
return cicValues;
|
return cicValues;
|
||||||
}
|
}
|
||||||
|
} else if (receiverContext != null && receiverContext.cic() != null) {
|
||||||
|
ReceiverContext enriched = new ReceiverContext(
|
||||||
|
context.getTypeDeclaration(ownerFqn),
|
||||||
|
receiverContext.cic(),
|
||||||
|
receiverContext.contextCu(),
|
||||||
|
receiverContext.anonymousGetter(),
|
||||||
|
receiverContext.fieldValues());
|
||||||
|
List<String> cicValues = resolveWithCic(
|
||||||
|
ownerFqn, methodName, enriched, indexedAccessor.orElse(null), visited);
|
||||||
|
if (!cicValues.isEmpty()) {
|
||||||
|
return cicValues;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (constantExtractor != null && !budget.isMethodReturnExhausted(0)) {
|
if (constantExtractor != null && !budget.isMethodReturnExhausted(0)) {
|
||||||
@@ -258,7 +270,6 @@ public final class AccessorResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (receiverContext != null
|
if (receiverContext != null
|
||||||
&& receiverContext.cic() != null
|
|
||||||
&& receiverContext.fieldValues() != null
|
&& receiverContext.fieldValues() != null
|
||||||
&& accessor.fieldName() != null) {
|
&& accessor.fieldName() != null) {
|
||||||
String fieldValue = receiverContext.fieldValues().get(accessor.fieldName());
|
String fieldValue = receiverContext.fieldValues().get(accessor.fieldName());
|
||||||
@@ -276,6 +287,9 @@ public final class AccessorResolver {
|
|||||||
AccessorSummary indexedAccessor,
|
AccessorSummary indexedAccessor,
|
||||||
Set<String> visited) {
|
Set<String> visited) {
|
||||||
TypeDeclaration td = receiverContext.ownerType();
|
TypeDeclaration td = receiverContext.ownerType();
|
||||||
|
if (td == null) {
|
||||||
|
td = context.getTypeDeclaration(ownerFqn);
|
||||||
|
}
|
||||||
ClassInstanceCreation cic = receiverContext.cic();
|
ClassInstanceCreation cic = receiverContext.cic();
|
||||||
Map<String, String> fieldValues = receiverContext.fieldValues() != null
|
Map<String, String> fieldValues = receiverContext.fieldValues() != null
|
||||||
? receiverContext.fieldValues()
|
? receiverContext.fieldValues()
|
||||||
@@ -400,7 +414,10 @@ public final class AccessorResolver {
|
|||||||
String cicKey = receiverContext != null && receiverContext.cic() != null
|
String cicKey = receiverContext != null && receiverContext.cic() != null
|
||||||
? receiverContext.cic().toString()
|
? receiverContext.cic().toString()
|
||||||
: "";
|
: "";
|
||||||
return ownerFqn + "#" + methodName + "#" + cicKey;
|
String fieldValuesKey = receiverContext != null && receiverContext.fieldValues() != null
|
||||||
|
? receiverContext.fieldValues().toString()
|
||||||
|
: "";
|
||||||
|
return ownerFqn + "#" + methodName + "#" + cicKey + "#" + fieldValuesKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String simplifyTypeName(String typeFqn) {
|
private static String simplifyTypeName(String typeFqn) {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package click.kamil.springstatemachineexporter.analysis.resolver;
|
package click.kamil.springstatemachineexporter.analysis.resolver;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -60,6 +62,66 @@ public final class BooleanConstraintEvaluator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates a constraint against known variable bindings gathered while walking a call path forward.
|
||||||
|
* Supports {@code var == VALUE} where VALUE may be a quoted string or enum constant; suffix matching
|
||||||
|
* is used so {@code command == ORDER_PAY} matches {@code DomainCommand.ORDER_PAY}.
|
||||||
|
*/
|
||||||
|
public static boolean isCompatibleWithBindings(String constraint, Map<String, String> bindings) {
|
||||||
|
if (constraint == null || constraint.isBlank()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (bindings == null || bindings.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (String varName : extractEqualityVariables(constraint)) {
|
||||||
|
if (!bindings.containsKey(varName) || varName.equals(bindings.get(varName))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String expr = constraint;
|
||||||
|
for (Map.Entry<String, String> entry : bindings.entrySet()) {
|
||||||
|
expr = substituteVariableBindings(expr, entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
return evaluateBooleanExpression(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<String> extractEqualityVariables(String constraint) {
|
||||||
|
Set<String> vars = new HashSet<>();
|
||||||
|
Pattern pattern = Pattern.compile("([a-zA-Z][\\w]*)\\s*==");
|
||||||
|
Matcher matcher = pattern.matcher(constraint);
|
||||||
|
while (matcher.find()) {
|
||||||
|
vars.add(matcher.group(1));
|
||||||
|
}
|
||||||
|
return vars;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String substituteVariableBindings(String expr, String varName, String boundValue) {
|
||||||
|
if (boundValue == null || boundValue.isBlank()) {
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
String cleanValue = boundValue;
|
||||||
|
if (cleanValue.startsWith("\"") && cleanValue.endsWith("\"")) {
|
||||||
|
cleanValue = cleanValue.substring(1, cleanValue.length() - 1);
|
||||||
|
}
|
||||||
|
String suffix = cleanValue.contains(".") ? cleanValue.substring(cleanValue.lastIndexOf('.') + 1) : cleanValue;
|
||||||
|
|
||||||
|
Pattern eqPattern = Pattern.compile(
|
||||||
|
"(?i)" + Pattern.quote(varName) + "\\s*==\\s*([\\w.\"']+)");
|
||||||
|
Matcher matcher = eqPattern.matcher(expr);
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
while (matcher.find()) {
|
||||||
|
String rhs = matcher.group(1).replace("\"", "").replace("'", "");
|
||||||
|
boolean matches = cleanValue.equals(rhs)
|
||||||
|
|| cleanValue.endsWith("." + rhs)
|
||||||
|
|| suffix.equalsIgnoreCase(rhs)
|
||||||
|
|| cleanValue.equalsIgnoreCase(rhs);
|
||||||
|
matcher.appendReplacement(sb, matches ? "true" : "false");
|
||||||
|
}
|
||||||
|
matcher.appendTail(sb);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true}/{@code false} when both sides are compile-time string literals; otherwise {@code null}.
|
* Returns {@code true}/{@code false} when both sides are compile-time string literals; otherwise {@code null}.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
protected final ExpressionAccessClassifier expressionAccessClassifier;
|
protected final ExpressionAccessClassifier expressionAccessClassifier;
|
||||||
protected final AccessorResolver accessorResolver;
|
protected final AccessorResolver accessorResolver;
|
||||||
protected final FieldInitializerFinder fieldInitializerFinder;
|
protected final FieldInitializerFinder fieldInitializerFinder;
|
||||||
|
protected final PathBindingEvaluator pathBindingEvaluator;
|
||||||
private final Map<String, ASTNode> parsedNodeCache = new HashMap<>();
|
private final Map<String, ASTNode> parsedNodeCache = new HashMap<>();
|
||||||
|
|
||||||
private ASTNode parseExpressionString(String expr) {
|
private ASTNode parseExpressionString(String expr) {
|
||||||
@@ -91,6 +92,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
this.accessorResolver = new AccessorResolver(context, constantExtractor, constructorAnalyzer, constantResolver);
|
this.accessorResolver = new AccessorResolver(context, constantExtractor, constructorAnalyzer, constantResolver);
|
||||||
this.constantExtractor.setAccessorResolver(this.accessorResolver);
|
this.constantExtractor.setAccessorResolver(this.accessorResolver);
|
||||||
this.fieldInitializerFinder = new FieldInitializerFinder(context);
|
this.fieldInitializerFinder = new FieldInitializerFinder(context);
|
||||||
|
this.pathBindingEvaluator = new PathBindingEvaluator(context, variableTracer, constantResolver, typeResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<CallChain> findChains(List<EntryPoint> entryPoints, List<TriggerPoint> triggers) {
|
public List<CallChain> findChains(List<EntryPoint> entryPoints, List<TriggerPoint> triggers) {
|
||||||
@@ -111,6 +113,9 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
}
|
}
|
||||||
Set<List<String>> uniquePaths = new LinkedHashSet<>(allPaths);
|
Set<List<String>> uniquePaths = new LinkedHashSet<>(allPaths);
|
||||||
for (List<String> path : uniquePaths) {
|
for (List<String> path : uniquePaths) {
|
||||||
|
if (!pathBindingEvaluator.isPathCompatible(path, callGraph, pathFinder)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
foundAny = true;
|
foundAny = true;
|
||||||
TriggerPoint resolvedTp = resolveTriggerPointParameters(tp, path, callGraph);
|
TriggerPoint resolvedTp = resolveTriggerPointParameters(tp, path, callGraph);
|
||||||
if (resolvedTp != null) {
|
if (resolvedTp != null) {
|
||||||
@@ -1281,25 +1286,17 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
if (currentTd != null) {
|
if (currentTd != null) {
|
||||||
String receiverType = context.getSuperclassFqn(currentTd);
|
String receiverType = context.getSuperclassFqn(currentTd);
|
||||||
if (receiverType != null) {
|
if (receiverType != null) {
|
||||||
Optional<AccessorSummary> indexedSuperGetter =
|
CompilationUnit contextCu = currentTd.getRoot() instanceof CompilationUnit cu ? cu : null;
|
||||||
context.getAccessorIndex().lookup(receiverType, getterName);
|
List<String> resolved = accessorResolver.resolve(
|
||||||
if (indexedSuperGetter.isPresent() && indexedSuperGetter.get().isGetter()) {
|
receiverType,
|
||||||
CompilationUnit contextCu = currentTd.getRoot() instanceof CompilationUnit cu ? cu : null;
|
getterName,
|
||||||
if (indexedSuperGetter.get().kind()
|
new AccessorResolver.ReceiverContext(
|
||||||
== click.kamil.springstatemachineexporter.analysis.index.AccessorKind.CONSTANT_GETTER) {
|
context.getTypeDeclaration(receiverType), null, contextCu, false, null),
|
||||||
List<String> constants = constantExtractor.resolveMethodReturnConstant(
|
ResolutionBudget.defaults());
|
||||||
receiverType, getterName, 0, new HashSet<>(), contextCu);
|
if (!resolved.isEmpty()) {
|
||||||
if (!constants.isEmpty()) {
|
return resolved;
|
||||||
return constants;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
List<String> fieldConstants = constantExtractor.resolveIndexedGetterFieldConstants(
|
|
||||||
indexedSuperGetter.get(), contextCu, new HashSet<>());
|
|
||||||
if (!fieldConstants.isEmpty()) {
|
|
||||||
return fieldConstants;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return constantExtractor.resolveMethodReturnConstant(receiverType, getterName, 0, new HashSet<>(), null);
|
return constantExtractor.resolveMethodReturnConstant(receiverType, getterName, 0, new HashSet<>(), contextCu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (exprNode instanceof MethodInvocation mi) {
|
} else if (exprNode instanceof MethodInvocation mi) {
|
||||||
@@ -1315,7 +1312,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
receiverType = variableTracer.getVariableDeclaredType(scopeMethod, receiverName);
|
receiverType = variableTracer.getVariableDeclaredType(scopeMethod, receiverName);
|
||||||
} else if (receiver instanceof ClassInstanceCreation cic) {
|
} else if (receiver instanceof ClassInstanceCreation cic) {
|
||||||
directCic = cic;
|
directCic = cic;
|
||||||
receiverType = cic.getType().toString();
|
receiverType = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cic.getType());
|
||||||
} else if (receiver instanceof MethodInvocation receiverMi) {
|
} else if (receiver instanceof MethodInvocation receiverMi) {
|
||||||
GetterChainReceiver chainedReceiver = resolveGetterChainToReceiver(receiverMi, scopeMethod);
|
GetterChainReceiver chainedReceiver = resolveGetterChainToReceiver(receiverMi, scopeMethod);
|
||||||
if (chainedReceiver != null) {
|
if (chainedReceiver != null) {
|
||||||
|
|||||||
@@ -419,7 +419,8 @@ public class ConstantExtractor {
|
|||||||
return super.visit(node);
|
return super.visit(node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (widenToImplementations) {
|
}
|
||||||
|
if (constants.isEmpty() && widenToImplementations) {
|
||||||
List<String> impls = context.getImplementations(className);
|
List<String> impls = context.getImplementations(className);
|
||||||
if (impls != null) {
|
if (impls != null) {
|
||||||
for (String implName : impls) {
|
for (String implName : impls) {
|
||||||
|
|||||||
@@ -170,13 +170,15 @@ public class ConstructorAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (results.isEmpty()) {
|
if (results.isEmpty()) {
|
||||||
// Context-Aware Fallback (Down): Check subclasses/implementations
|
// Context-Aware Fallback (Down): widen only for interface/abstract types
|
||||||
List<String> impls = context.getImplementations(fqn);
|
if (shouldWidenToImplementations(td)) {
|
||||||
if (impls != null) {
|
List<String> impls = context.getImplementations(fqn);
|
||||||
for (String implFqn : impls) {
|
if (impls != null) {
|
||||||
TypeDeclaration implTd = context.getTypeDeclaration(implFqn);
|
for (String implFqn : impls) {
|
||||||
if (implTd != null) {
|
TypeDeclaration implTd = context.getTypeDeclaration(implFqn);
|
||||||
results.addAll(traceFieldInConstructors(implTd, fieldName, context, visited, budget));
|
if (implTd != null) {
|
||||||
|
results.addAll(traceFieldInConstructors(implTd, fieldName, context, visited, budget));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,10 +189,11 @@ public class ConstructorAnalyzer {
|
|||||||
Collection<TypeDeclaration> allTypes = context.getTypeDeclarations();
|
Collection<TypeDeclaration> allTypes = context.getTypeDeclarations();
|
||||||
if (allTypes != null) {
|
if (allTypes != null) {
|
||||||
int matchedCount = 0;
|
int matchedCount = 0;
|
||||||
|
ResolutionBudget localBudget = ResolutionBudget.defaults();
|
||||||
for (TypeDeclaration typeDecl : allTypes) {
|
for (TypeDeclaration typeDecl : allTypes) {
|
||||||
if (typeDecl == null) continue;
|
if (typeDecl == null) continue;
|
||||||
if (context.hasField(typeDecl, fieldName)) {
|
if (context.hasField(typeDecl, fieldName)) {
|
||||||
results.addAll(traceFieldInConstructors(typeDecl, fieldName, context, visited, budget));
|
results.addAll(traceFieldInConstructors(typeDecl, fieldName, context, visited, localBudget));
|
||||||
matchedCount++;
|
matchedCount++;
|
||||||
if (matchedCount >= budget.globalFieldScanLimit()) {
|
if (matchedCount >= budget.globalFieldScanLimit()) {
|
||||||
break;
|
break;
|
||||||
@@ -204,6 +207,13 @@ public class ConstructorAnalyzer {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean shouldWidenToImplementations(TypeDeclaration td) {
|
||||||
|
if (td == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return td.isInterface() || Modifier.isAbstract(td.getModifiers());
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, String> buildFieldValuesFromCIC(
|
public Map<String, String> buildFieldValuesFromCIC(
|
||||||
TypeDeclaration td,
|
TypeDeclaration td,
|
||||||
ClassInstanceCreation cic,
|
ClassInstanceCreation cic,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public class JdtIntelligenceProvider implements CodebaseIntelligenceProvider {
|
|||||||
private final SpringComponentDetector componentDetector;
|
private final SpringComponentDetector componentDetector;
|
||||||
private List<TriggerPoint> cachedTriggers;
|
private List<TriggerPoint> cachedTriggers;
|
||||||
private List<EntryPoint> cachedEntryPoints;
|
private List<EntryPoint> cachedEntryPoints;
|
||||||
|
private List<CallChain> cachedCallChains;
|
||||||
|
|
||||||
public JdtIntelligenceProvider(CodebaseContext context, Path rootDir) {
|
public JdtIntelligenceProvider(CodebaseContext context, Path rootDir) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
@@ -93,8 +94,12 @@ public class JdtIntelligenceProvider implements CodebaseIntelligenceProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<CallChain> findCallChains(List<EntryPoint> entryPoints, List<TriggerPoint> triggers) {
|
public List<CallChain> findCallChains(List<EntryPoint> entryPoints, List<TriggerPoint> triggers) {
|
||||||
|
if (cachedCallChains != null) {
|
||||||
|
return cachedCallChains;
|
||||||
|
}
|
||||||
log.info("JdtIntelligenceProvider searching for call chains between {} entry points and {} triggers", entryPoints.size(), triggers.size());
|
log.info("JdtIntelligenceProvider searching for call chains between {} entry points and {} triggers", entryPoints.size(), triggers.size());
|
||||||
return callGraphEngine.findChains(entryPoints, triggers);
|
cachedCallChains = callGraphEngine.findChains(entryPoints, triggers);
|
||||||
|
return cachedCallChains;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,329 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.service;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.CallEdge;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.resolver.BooleanConstraintEvaluator;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver;
|
||||||
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
|
import org.eclipse.jdt.core.dom.*;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Walks a call path forward, propagating known parameter / local bindings so switch-case
|
||||||
|
* edge constraints can prune impossible branches (e.g. {@code dispatch} arms).
|
||||||
|
*/
|
||||||
|
public class PathBindingEvaluator {
|
||||||
|
|
||||||
|
private final CodebaseContext context;
|
||||||
|
private final VariableTracer variableTracer;
|
||||||
|
private final ConstantResolver constantResolver;
|
||||||
|
private final TypeResolver typeResolver;
|
||||||
|
|
||||||
|
public PathBindingEvaluator(
|
||||||
|
CodebaseContext context,
|
||||||
|
VariableTracer variableTracer,
|
||||||
|
ConstantResolver constantResolver,
|
||||||
|
TypeResolver typeResolver) {
|
||||||
|
this.context = context;
|
||||||
|
this.variableTracer = variableTracer;
|
||||||
|
this.constantResolver = constantResolver;
|
||||||
|
this.typeResolver = typeResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Visible for tests: bindings accumulated while walking a path forward (ignores constraint rejection). */
|
||||||
|
Map<String, String> traceBindings(List<String> path, Map<String, List<CallEdge>> callGraph, CallGraphPathFinder pathFinder) {
|
||||||
|
Map<String, String> bindings = new HashMap<>();
|
||||||
|
if (path == null || path.size() < 2) {
|
||||||
|
return bindings;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < path.size() - 1; i++) {
|
||||||
|
String caller = path.get(i);
|
||||||
|
String target = path.get(i + 1);
|
||||||
|
CallEdge edge = findEdge(caller, target, callGraph, pathFinder);
|
||||||
|
if (edge == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
enrichBindings(caller, target, edge, path, i + 1, callGraph, pathFinder, bindings);
|
||||||
|
}
|
||||||
|
return bindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPathCompatible(List<String> path, Map<String, List<CallEdge>> callGraph, CallGraphPathFinder pathFinder) {
|
||||||
|
if (path == null || path.size() < 2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Map<String, String> bindings = new HashMap<>();
|
||||||
|
for (int i = 0; i < path.size() - 1; i++) {
|
||||||
|
String caller = path.get(i);
|
||||||
|
String target = path.get(i + 1);
|
||||||
|
CallEdge edge = findEdge(caller, target, callGraph, pathFinder);
|
||||||
|
if (edge == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (edge.getConstraint() != null
|
||||||
|
&& !edge.getConstraint().isBlank()
|
||||||
|
&& !BooleanConstraintEvaluator.isCompatibleWithBindings(edge.getConstraint(), bindings)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
enrichBindings(caller, target, edge, path, i + 1, callGraph, pathFinder, bindings);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enrichBindings(
|
||||||
|
String caller,
|
||||||
|
String target,
|
||||||
|
CallEdge edge,
|
||||||
|
List<String> path,
|
||||||
|
int pathIndex,
|
||||||
|
Map<String, List<CallEdge>> callGraph,
|
||||||
|
CallGraphPathFinder pathFinder,
|
||||||
|
Map<String, String> bindings) {
|
||||||
|
Map<String, String> paramValues = variableTracer.buildParameterValuesMap(caller, target, callGraph, path, pathIndex);
|
||||||
|
mergeResolvedBindings(bindings, paramValues, caller);
|
||||||
|
|
||||||
|
List<String> targetParams = typeResolver.getParameterNames(target);
|
||||||
|
if (targetParams == null || edge.getArguments() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < edge.getArguments().size() && i < targetParams.size(); i++) {
|
||||||
|
String arg = edge.getArguments().get(i);
|
||||||
|
String paramName = targetParams.get(i);
|
||||||
|
String resolved = resolveBindingValue(caller, arg, bindings);
|
||||||
|
if (shouldStoreBinding(paramName, resolved)) {
|
||||||
|
bindings.put(paramName, resolved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldStoreBinding(String paramName, String resolved) {
|
||||||
|
if (resolved == null || resolved.isBlank() || resolved.equals(paramName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (resolved.contains("(") && !isEnumLikeConstant(resolved)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mergeResolvedBindings(Map<String, String> bindings, Map<String, String> paramValues, String caller) {
|
||||||
|
if (paramValues == null || paramValues.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, String> entry : paramValues.entrySet()) {
|
||||||
|
String resolved = resolveBindingValue(caller, entry.getValue(), bindings);
|
||||||
|
if (shouldStoreBinding(entry.getKey(), resolved)) {
|
||||||
|
bindings.put(entry.getKey(), resolved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveBindingValue(String callerFqn, String rawValue, Map<String, String> bindings) {
|
||||||
|
if (rawValue == null || rawValue.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (rawValue.startsWith("\"") || isEnumLikeConstant(rawValue)) {
|
||||||
|
return normalizeLiteral(rawValue);
|
||||||
|
}
|
||||||
|
if (bindings.containsKey(rawValue)) {
|
||||||
|
return bindings.get(rawValue);
|
||||||
|
}
|
||||||
|
String traced = variableTracer.traceLocalVariable(callerFqn, rawValue, bindings);
|
||||||
|
if (traced != null && !traced.isBlank()) {
|
||||||
|
if (traced.startsWith("\"") || isEnumLikeConstant(traced)) {
|
||||||
|
return traced;
|
||||||
|
}
|
||||||
|
String fromCall = resolveMethodCallFromSource(callerFqn, rawValue, bindings);
|
||||||
|
if (fromCall != null) {
|
||||||
|
return fromCall;
|
||||||
|
}
|
||||||
|
if (!traced.equals(rawValue)) {
|
||||||
|
return traced;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rawValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveMethodCallFromSource(String callerFqn, String varName, Map<String, String> bindings) {
|
||||||
|
MethodDeclaration md = findMethodDeclaration(callerFqn);
|
||||||
|
if (md == null || md.getBody() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final Expression[] initializer = new Expression[1];
|
||||||
|
md.getBody().accept(new ASTVisitor() {
|
||||||
|
@Override
|
||||||
|
public boolean visit(VariableDeclarationFragment node) {
|
||||||
|
if (node.getName().getIdentifier().equals(varName) && node.getInitializer() != null) {
|
||||||
|
initializer[0] = node.getInitializer();
|
||||||
|
}
|
||||||
|
return super.visit(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (initializer[0] == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return evaluateExpressionValue(initializer[0], callerFqn, bindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String evaluateExpressionValue(Expression expr, String callerFqn, Map<String, String> bindings) {
|
||||||
|
if (expr instanceof StringLiteral sl) {
|
||||||
|
return "\"" + sl.getLiteralValue() + "\"";
|
||||||
|
}
|
||||||
|
if (expr instanceof QualifiedName qn) {
|
||||||
|
return qn.getFullyQualifiedName();
|
||||||
|
}
|
||||||
|
if (expr instanceof SimpleName sn) {
|
||||||
|
return bindings.getOrDefault(sn.getIdentifier(), sn.getIdentifier());
|
||||||
|
}
|
||||||
|
if (expr instanceof MethodInvocation mi) {
|
||||||
|
return evaluateMethodInvocationReturn(mi, callerFqn, bindings);
|
||||||
|
}
|
||||||
|
if (expr instanceof SwitchExpression se) {
|
||||||
|
return constantResolver.evaluateSwitchWithParams(se, bindings, context);
|
||||||
|
}
|
||||||
|
String resolved = constantResolver.resolve(expr, context);
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String evaluateMethodInvocationReturn(MethodInvocation mi, String callerFqn, Map<String, String> bindings) {
|
||||||
|
String ownerFqn = resolveMethodOwnerFqn(mi, callerFqn);
|
||||||
|
if (ownerFqn == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String methodName = mi.getName().getIdentifier();
|
||||||
|
TypeDeclaration owner = context.getTypeDeclaration(ownerFqn);
|
||||||
|
if (owner == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
MethodDeclaration md = context.findMethodDeclaration(owner, methodName, true);
|
||||||
|
if (md == null || md.getBody() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> callBindings = new HashMap<>(bindings);
|
||||||
|
for (int i = 0; i < md.parameters().size() && i < mi.arguments().size(); i++) {
|
||||||
|
SingleVariableDeclaration param = (SingleVariableDeclaration) md.parameters().get(i);
|
||||||
|
String paramName = param.getName().getIdentifier();
|
||||||
|
String argValue = evaluateExpressionValue((Expression) mi.arguments().get(i), callerFqn, callBindings);
|
||||||
|
if (argValue != null) {
|
||||||
|
callBindings.put(paramName, argValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final String[] result = new String[1];
|
||||||
|
md.getBody().accept(new ASTVisitor() {
|
||||||
|
@Override
|
||||||
|
public boolean visit(ReturnStatement node) {
|
||||||
|
if (node.getExpression() instanceof SwitchExpression se) {
|
||||||
|
result[0] = constantResolver.evaluateSwitchWithParams(se, callBindings, context);
|
||||||
|
} else if (node.getExpression() != null) {
|
||||||
|
result[0] = evaluateExpressionValue(node.getExpression(), ownerFqn + "." + methodName, callBindings);
|
||||||
|
}
|
||||||
|
return super.visit(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveFieldTypeFqn(String callerFqn, String fieldName) {
|
||||||
|
if (callerFqn == null || !callerFqn.contains(".")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String className = callerFqn.substring(0, callerFqn.lastIndexOf('.'));
|
||||||
|
TypeDeclaration td = context.getTypeDeclaration(className);
|
||||||
|
if (td == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (FieldDeclaration field : td.getFields()) {
|
||||||
|
for (Object fragmentObj : field.fragments()) {
|
||||||
|
VariableDeclarationFragment fragment = (VariableDeclarationFragment) fragmentObj;
|
||||||
|
if (fragment.getName().getIdentifier().equals(fieldName)) {
|
||||||
|
IVariableBinding binding = fragment.resolveBinding();
|
||||||
|
if (binding != null && binding.getType() != null) {
|
||||||
|
return binding.getType().getErasure().getQualifiedName();
|
||||||
|
}
|
||||||
|
String simpleType = field.getType().toString();
|
||||||
|
TypeDeclaration resolved = context.getTypeDeclaration(simpleType);
|
||||||
|
if (resolved != null) {
|
||||||
|
return context.getFqn(resolved);
|
||||||
|
}
|
||||||
|
if (td.getRoot() instanceof CompilationUnit cu && cu.getPackage() != null) {
|
||||||
|
String pkg = cu.getPackage().getName().getFullyQualifiedName();
|
||||||
|
resolved = context.getTypeDeclaration(pkg + "." + simpleType);
|
||||||
|
if (resolved != null) {
|
||||||
|
return context.getFqn(resolved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return simpleType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveMethodOwnerFqn(MethodInvocation mi, String callerFqn) {
|
||||||
|
IMethodBinding methodBinding = mi.resolveMethodBinding();
|
||||||
|
if (methodBinding != null && methodBinding.getDeclaringClass() != null) {
|
||||||
|
return methodBinding.getDeclaringClass().getErasure().getQualifiedName();
|
||||||
|
}
|
||||||
|
Expression receiver = mi.getExpression();
|
||||||
|
if (receiver instanceof SimpleName sn) {
|
||||||
|
return resolveFieldTypeFqn(callerFqn, sn.getIdentifier());
|
||||||
|
}
|
||||||
|
if (receiver instanceof FieldAccess fa) {
|
||||||
|
IVariableBinding fieldBinding = fa.resolveFieldBinding();
|
||||||
|
if (fieldBinding != null && fieldBinding.getType() != null) {
|
||||||
|
return fieldBinding.getType().getErasure().getQualifiedName();
|
||||||
|
}
|
||||||
|
return resolveFieldTypeFqn(callerFqn, fa.getName().getIdentifier());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodDeclaration findMethodDeclaration(String methodFqn) {
|
||||||
|
if (methodFqn == null || !methodFqn.contains(".")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String className = methodFqn.substring(0, methodFqn.lastIndexOf('.'));
|
||||||
|
String methodName = methodFqn.substring(methodFqn.lastIndexOf('.') + 1);
|
||||||
|
TypeDeclaration td = context.getTypeDeclaration(className);
|
||||||
|
if (td == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return context.findMethodDeclaration(td, methodName, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String normalizeLiteral(String value) {
|
||||||
|
if (value != null && value.startsWith("\"") && value.endsWith("\"")) {
|
||||||
|
return value.substring(1, value.length() - 1);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isEnumLikeConstant(String value) {
|
||||||
|
if (value == null || value.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int dot = value.lastIndexOf('.');
|
||||||
|
if (dot <= 0 || dot >= value.length() - 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Character.isUpperCase(value.charAt(dot + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CallEdge findEdge(
|
||||||
|
String caller, String target, Map<String, List<CallEdge>> callGraph, CallGraphPathFinder pathFinder) {
|
||||||
|
List<CallEdge> edges = callGraph.get(caller);
|
||||||
|
if (edges == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (CallEdge edge : edges) {
|
||||||
|
if (edge.getTargetMethod().equals(target) || pathFinder.isHeuristicMatch(edge.getTargetMethod(), target)) {
|
||||||
|
return edge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,6 +30,28 @@ public class TypeResolver {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getParameterNames(String methodFqn) {
|
||||||
|
if (methodFqn == null || !methodFqn.contains(".")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String className = methodFqn.substring(0, methodFqn.lastIndexOf('.'));
|
||||||
|
String methodName = methodFqn.substring(methodFqn.lastIndexOf('.') + 1);
|
||||||
|
TypeDeclaration td = context.getTypeDeclaration(className);
|
||||||
|
if (td == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
MethodDeclaration md = context.findMethodDeclaration(td, methodName, true);
|
||||||
|
if (md == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<String> paramNames = new ArrayList<>();
|
||||||
|
for (Object paramObj : md.parameters()) {
|
||||||
|
SingleVariableDeclaration param = (SingleVariableDeclaration) paramObj;
|
||||||
|
paramNames.add(param.getName().getIdentifier());
|
||||||
|
}
|
||||||
|
return paramNames;
|
||||||
|
}
|
||||||
|
|
||||||
public String getParameterName(String methodFqn, int index) {
|
public String getParameterName(String methodFqn, int index) {
|
||||||
if (methodFqn == null || !methodFqn.contains(".") || index < 0) return null;
|
if (methodFqn == null || !methodFqn.contains(".") || index < 0) return null;
|
||||||
String className = methodFqn.substring(0, methodFqn.lastIndexOf('.'));
|
String className = methodFqn.substring(0, methodFqn.lastIndexOf('.'));
|
||||||
|
|||||||
@@ -113,8 +113,100 @@ public final class AstUtils {
|
|||||||
}
|
}
|
||||||
current = parent;
|
current = parent;
|
||||||
}
|
}
|
||||||
|
String switchConstraint = buildSwitchCaseConstraint(findEnclosingSwitchCase(node));
|
||||||
|
if (switchConstraint != null) {
|
||||||
|
conditions.add(switchConstraint);
|
||||||
|
}
|
||||||
if (conditions.isEmpty()) return null;
|
if (conditions.isEmpty()) return null;
|
||||||
java.util.Collections.reverse(conditions);
|
java.util.Collections.reverse(conditions);
|
||||||
return String.join(" && ", conditions);
|
return String.join(" && ", conditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static org.eclipse.jdt.core.dom.SwitchCase findEnclosingSwitchCase(org.eclipse.jdt.core.dom.ASTNode node) {
|
||||||
|
org.eclipse.jdt.core.dom.ASTNode current = node;
|
||||||
|
while (current != null && !(current instanceof org.eclipse.jdt.core.dom.MethodDeclaration)) {
|
||||||
|
if (current instanceof org.eclipse.jdt.core.dom.SwitchCase switchCase) {
|
||||||
|
return switchCase;
|
||||||
|
}
|
||||||
|
current = current.getParent();
|
||||||
|
}
|
||||||
|
current = node;
|
||||||
|
while (current != null && !(current instanceof org.eclipse.jdt.core.dom.MethodDeclaration)) {
|
||||||
|
if (current instanceof org.eclipse.jdt.core.dom.YieldStatement yieldStatement) {
|
||||||
|
org.eclipse.jdt.core.dom.ASTNode parent = yieldStatement.getParent();
|
||||||
|
if (parent instanceof org.eclipse.jdt.core.dom.SwitchStatement switchStatement) {
|
||||||
|
org.eclipse.jdt.core.dom.SwitchCase switchCase =
|
||||||
|
findSwitchCaseForStatement(switchStatement.statements(), yieldStatement);
|
||||||
|
if (switchCase != null) {
|
||||||
|
return switchCase;
|
||||||
|
}
|
||||||
|
} else if (parent instanceof org.eclipse.jdt.core.dom.SwitchExpression switchExpression) {
|
||||||
|
org.eclipse.jdt.core.dom.SwitchCase switchCase =
|
||||||
|
findSwitchCaseForStatement(switchExpression.statements(), yieldStatement);
|
||||||
|
if (switchCase != null) {
|
||||||
|
return switchCase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (current instanceof org.eclipse.jdt.core.dom.ExpressionStatement expressionStatement) {
|
||||||
|
org.eclipse.jdt.core.dom.ASTNode parent = expressionStatement.getParent();
|
||||||
|
if (parent instanceof org.eclipse.jdt.core.dom.SwitchCase switchCase) {
|
||||||
|
return switchCase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current = current.getParent();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static org.eclipse.jdt.core.dom.SwitchCase findSwitchCaseForStatement(
|
||||||
|
java.util.List<?> statements, org.eclipse.jdt.core.dom.ASTNode bodyStatement) {
|
||||||
|
int idx = statements.indexOf(bodyStatement);
|
||||||
|
if (idx < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (int i = idx - 1; i >= 0; i--) {
|
||||||
|
if (statements.get(i) instanceof org.eclipse.jdt.core.dom.SwitchCase switchCase) {
|
||||||
|
return switchCase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildSwitchCaseConstraint(org.eclipse.jdt.core.dom.SwitchCase switchCase) {
|
||||||
|
if (switchCase == null || switchCase.isDefault()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
org.eclipse.jdt.core.dom.Expression switchSelector = null;
|
||||||
|
org.eclipse.jdt.core.dom.ASTNode switchParent = switchCase.getParent();
|
||||||
|
if (switchParent instanceof org.eclipse.jdt.core.dom.SwitchStatement ss) {
|
||||||
|
switchSelector = ss.getExpression();
|
||||||
|
} else if (switchParent instanceof org.eclipse.jdt.core.dom.SwitchExpression se) {
|
||||||
|
switchSelector = se.getExpression();
|
||||||
|
}
|
||||||
|
if (switchSelector == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
java.util.List<String> caseLabels = new java.util.ArrayList<>();
|
||||||
|
for (Object exprObj : switchCase.expressions()) {
|
||||||
|
if (exprObj instanceof org.eclipse.jdt.core.dom.Expression caseExpr) {
|
||||||
|
caseLabels.add(caseExpr.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (caseLabels.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String selector = switchSelector.toString();
|
||||||
|
if (caseLabels.size() == 1) {
|
||||||
|
return selector + " == " + caseLabels.get(0);
|
||||||
|
}
|
||||||
|
StringBuilder combined = new StringBuilder("(");
|
||||||
|
for (int i = 0; i < caseLabels.size(); i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
combined.append(" || ");
|
||||||
|
}
|
||||||
|
combined.append(selector).append(" == ").append(caseLabels.get(i));
|
||||||
|
}
|
||||||
|
combined.append(')');
|
||||||
|
return combined.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -323,6 +323,9 @@ public class JdtDataFlowModel implements DataFlowModel {
|
|||||||
Map<IVariableBinding, Expression> paramBindings,
|
Map<IVariableBinding, Expression> paramBindings,
|
||||||
Map<IVariableBinding, Expression> instanceFieldBindings,
|
Map<IVariableBinding, Expression> instanceFieldBindings,
|
||||||
int depth) {
|
int depth) {
|
||||||
|
if (!context.isInlineAccessors()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
if (ResolutionBudget.defaults().isAccessorInlineExhausted(depth)) {
|
if (ResolutionBudget.defaults().isAccessorInlineExhausted(depth)) {
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
@@ -361,9 +364,17 @@ public class JdtDataFlowModel implements DataFlowModel {
|
|||||||
mi.getExpression(), visited, paramBindings, instanceFieldBindings, depth + 1);
|
mi.getExpression(), visited, paramBindings, instanceFieldBindings, depth + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accessor.isPresent()
|
if (accessor.isPresent()) {
|
||||||
&& shouldSkipAccessorDueToConcreteOverride(declaringFqn, methodName, receiverDefs)) {
|
String accessorOwner = accessor.get().ownerFqn();
|
||||||
return List.of();
|
if (accessorOwner.equals(declaringFqn)) {
|
||||||
|
Optional<AccessorSummary> concreteAccessor =
|
||||||
|
lookupConcreteAccessorOverride(declaringFqn, methodName, receiverDefs);
|
||||||
|
if (concreteAccessor.isPresent()) {
|
||||||
|
accessor = concreteAccessor;
|
||||||
|
} else if (shouldSkipAccessorDueToConcreteOverride(declaringFqn, methodName, receiverDefs)) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accessor.isEmpty()) {
|
if (accessor.isEmpty()) {
|
||||||
@@ -412,6 +423,23 @@ public class JdtDataFlowModel implements DataFlowModel {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Optional<AccessorSummary> lookupConcreteAccessorOverride(
|
||||||
|
String declaringFqn,
|
||||||
|
String methodName,
|
||||||
|
List<Expression> receiverDefs) {
|
||||||
|
for (Expression receiverDef : receiverDefs) {
|
||||||
|
String concreteFqn = concreteReceiverTypeFqn(receiverDef);
|
||||||
|
if (concreteFqn == null || concreteFqn.equals(declaringFqn)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Optional<AccessorSummary> concreteAccessor = context.getAccessorIndex().lookup(concreteFqn, methodName);
|
||||||
|
if (concreteAccessor.isPresent() && concreteAccessor.get().isGetter()) {
|
||||||
|
return concreteAccessor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean shouldSkipAccessorDueToConcreteOverride(
|
private boolean shouldSkipAccessorDueToConcreteOverride(
|
||||||
String declaringFqn,
|
String declaringFqn,
|
||||||
String methodName,
|
String methodName,
|
||||||
|
|||||||
@@ -550,6 +550,28 @@ class AccessorInliningDataFlowTest {
|
|||||||
assertResolvedValue(tempDir, "value", "SUPER");
|
assertResolvedValue(tempDir, "value", "SUPER");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldResolveInterfaceEnumGetterViaConcreteImplementation(@TempDir Path tempDir) throws IOException {
|
||||||
|
writeJava(tempDir, "com/example/Runner.java", """
|
||||||
|
package com.example;
|
||||||
|
public class Runner {
|
||||||
|
public interface RichEvent {
|
||||||
|
EventType getType();
|
||||||
|
}
|
||||||
|
public static class PayEvent implements RichEvent {
|
||||||
|
public EventType getType() { return EventType.PAY; }
|
||||||
|
}
|
||||||
|
public void run() {
|
||||||
|
RichEvent event = new PayEvent();
|
||||||
|
EventType value = event.getType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enum EventType { PAY, SHIP }
|
||||||
|
""");
|
||||||
|
|
||||||
|
assertResolvedValue(tempDir, "value", "EventType.PAY");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldReturnEmptyWhenAccessorIndexHasNoEntry(@TempDir Path tempDir) throws IOException {
|
void shouldReturnEmptyWhenAccessorIndexHasNoEntry(@TempDir Path tempDir) throws IOException {
|
||||||
writeJava(tempDir, "com/example/Runner.java", """
|
writeJava(tempDir, "com/example/Runner.java", """
|
||||||
|
|||||||
@@ -0,0 +1,176 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.pipeline;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.service.ConstructorAnalyzer;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.service.ConstantExtractor;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.service.VariableTracer;
|
||||||
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
|
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class AccessorPipelineBugFixTest {
|
||||||
|
|
||||||
|
@TempDir
|
||||||
|
Path tempDir;
|
||||||
|
|
||||||
|
CodebaseContext context;
|
||||||
|
ConstantExtractor constantExtractor;
|
||||||
|
ConstructorAnalyzer constructorAnalyzer;
|
||||||
|
AccessorResolver accessorResolver;
|
||||||
|
VariableTracer variableTracer;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() throws IOException {
|
||||||
|
context = new CodebaseContext();
|
||||||
|
context.setResolveBindings(true);
|
||||||
|
context.setSourcepath(List.of(tempDir.toAbsolutePath().toString()));
|
||||||
|
ConstantResolver constantResolver = context.getConstantResolver();
|
||||||
|
constantExtractor = new ConstantExtractor(context, constantResolver, mi -> null);
|
||||||
|
constructorAnalyzer = new ConstructorAnalyzer(context, constantResolver);
|
||||||
|
variableTracer = new VariableTracer(context, constantResolver);
|
||||||
|
constantExtractor.setVariableTracer(variableTracer);
|
||||||
|
constantExtractor.setConstructorAnalyzer(constructorAnalyzer);
|
||||||
|
accessorResolver = new AccessorResolver(context, constantExtractor, constructorAnalyzer, constantResolver);
|
||||||
|
constantExtractor.setAccessorResolver(accessorResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeJava(String relativePath, String source) throws IOException {
|
||||||
|
Path file = tempDir.resolve(relativePath);
|
||||||
|
Files.createDirectories(file.getParent());
|
||||||
|
Files.writeString(file, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scanWorkspace() throws IOException {
|
||||||
|
context.scan(tempDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class ConstructorAnalyzerSubclassPollution {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotPullSubclassFieldValuesWhenTracingConcreteBaseType() throws IOException {
|
||||||
|
writeJava("com/example/Base.java", """
|
||||||
|
package com.example;
|
||||||
|
public class Base {
|
||||||
|
protected String token;
|
||||||
|
}
|
||||||
|
""");
|
||||||
|
writeJava("com/example/Sub.java", """
|
||||||
|
package com.example;
|
||||||
|
public class Sub extends Base {
|
||||||
|
public Sub() { this.token = "SUB_ONLY"; }
|
||||||
|
}
|
||||||
|
""");
|
||||||
|
scanWorkspace();
|
||||||
|
|
||||||
|
TypeDeclaration baseTd = context.getTypeDeclaration("com.example.Base");
|
||||||
|
List<String> traced = constructorAnalyzer.traceFieldInConstructors(
|
||||||
|
baseTd, "token", context, new HashSet<>(), ResolutionBudget.defaults());
|
||||||
|
|
||||||
|
assertThat(traced).doesNotContain("SUB_ONLY");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class ConstantExtractorAbstractBodyWidening {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldWidenToImplementationsWhenInterfaceMethodHasNoBody() throws IOException {
|
||||||
|
writeJava("com/example/Code.java", """
|
||||||
|
package com.example;
|
||||||
|
public enum Code { A, B }
|
||||||
|
""");
|
||||||
|
writeJava("com/example/RichEvent.java", """
|
||||||
|
package com.example;
|
||||||
|
public interface RichEvent {
|
||||||
|
Code getCode();
|
||||||
|
}
|
||||||
|
""");
|
||||||
|
writeJava("com/example/EventA.java", """
|
||||||
|
package com.example;
|
||||||
|
public class EventA implements RichEvent {
|
||||||
|
public Code getCode() { return Code.A; }
|
||||||
|
}
|
||||||
|
""");
|
||||||
|
writeJava("com/example/EventB.java", """
|
||||||
|
package com.example;
|
||||||
|
public class EventB implements RichEvent {
|
||||||
|
public Code getCode() { return Code.B; }
|
||||||
|
}
|
||||||
|
""");
|
||||||
|
scanWorkspace();
|
||||||
|
|
||||||
|
List<String> resolved = constantExtractor.resolveMethodReturnConstant(
|
||||||
|
"com.example.RichEvent", "getCode", 0, new HashSet<>(), null);
|
||||||
|
|
||||||
|
assertThat(resolved).containsExactlyInAnyOrder("Code.A", "Code.B");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class AccessorResolverCacheKey {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotReuseCachedResultWhenFieldValuesDiffer() throws IOException {
|
||||||
|
writeJava("com/example/Payload.java", """
|
||||||
|
package com.example;
|
||||||
|
public class Payload {
|
||||||
|
private String event;
|
||||||
|
public String getEvent() { return event; }
|
||||||
|
}
|
||||||
|
""");
|
||||||
|
scanWorkspace();
|
||||||
|
|
||||||
|
TypeDeclaration td = context.getTypeDeclaration("com.example.Payload");
|
||||||
|
Map<String, String> valuesA = new HashMap<>(Map.of("event", "PAY"));
|
||||||
|
Map<String, String> valuesB = new HashMap<>(Map.of("event", "CANCEL"));
|
||||||
|
|
||||||
|
List<String> pay = accessorResolver.resolve(
|
||||||
|
"com.example.Payload",
|
||||||
|
"getEvent",
|
||||||
|
new AccessorResolver.ReceiverContext(td, null, null, false, valuesA),
|
||||||
|
ResolutionBudget.defaults());
|
||||||
|
List<String> cancel = accessorResolver.resolve(
|
||||||
|
"com.example.Payload",
|
||||||
|
"getEvent",
|
||||||
|
new AccessorResolver.ReceiverContext(td, null, null, false, valuesB),
|
||||||
|
ResolutionBudget.defaults());
|
||||||
|
|
||||||
|
assertThat(pay).containsExactly("PAY");
|
||||||
|
assertThat(cancel).containsExactly("CANCEL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class InlineAccessorsDisabled {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldSkipIndexedDataflowShortcutWhenDisabled() throws IOException {
|
||||||
|
writeJava("com/example/Payload.java", """
|
||||||
|
package com.example;
|
||||||
|
public class Payload {
|
||||||
|
private String event = "INLINE";
|
||||||
|
public String getEvent() { return event; }
|
||||||
|
}
|
||||||
|
""");
|
||||||
|
context.setInlineAccessors(false);
|
||||||
|
scanWorkspace();
|
||||||
|
|
||||||
|
assertThat(context.getAccessorIndex().trivialCount()).isZero();
|
||||||
|
assertThat(context.isInlineAccessors()).isFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.resolver;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class BooleanConstraintEvaluatorBindingsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldAcceptMatchingEnumSwitchConstraint() {
|
||||||
|
assertThat(BooleanConstraintEvaluator.isCompatibleWithBindings(
|
||||||
|
"command == ORDER_PAY",
|
||||||
|
Map.of("command", "DomainCommand.ORDER_PAY"))).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRejectMismatchedEnumSwitchConstraint() {
|
||||||
|
assertThat(BooleanConstraintEvaluator.isCompatibleWithBindings(
|
||||||
|
"command == ORDER_SHIP",
|
||||||
|
Map.of("command", "DomainCommand.ORDER_PAY"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldAcceptMatchingStringSwitchConstraint() {
|
||||||
|
assertThat(BooleanConstraintEvaluator.isCompatibleWithBindings(
|
||||||
|
"commandKey == \"order.pay\"",
|
||||||
|
Map.of("commandKey", "order.pay"))).isTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -74,6 +74,32 @@ class AnalysisCacheCorrectnessTest {
|
|||||||
assertThat(context.getCache().get("heuristicCallGraph")).isSameAs(cachedGraph);
|
assertThat(context.getCache().get("heuristicCallGraph")).isSameAs(cachedGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jdtIntelligenceProviderShouldCacheCallChains(@TempDir Path tempDir) throws Exception {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
class A { void go() { new B().run("PAY"); } }
|
||||||
|
class B { void run(String s) { new C().fire(com.example.E.PAY); } }
|
||||||
|
enum E { PAY, SHIP }
|
||||||
|
class C { void fire(E e) {} }
|
||||||
|
""";
|
||||||
|
Files.writeString(tempDir.resolve("App.java"), source);
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.setResolveBindings(true);
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
JdtIntelligenceProvider intelligence = new JdtIntelligenceProvider(context, tempDir);
|
||||||
|
EntryPoint entry = EntryPoint.builder().className("com.example.A").methodName("go").build();
|
||||||
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.C").methodName("fire").event("e").build();
|
||||||
|
List<EntryPoint> entryPoints = List.of(entry);
|
||||||
|
List<TriggerPoint> triggers = List.of(trigger);
|
||||||
|
|
||||||
|
List<CallChain> first = intelligence.findCallChains(entryPoints, triggers);
|
||||||
|
List<CallChain> second = intelligence.findCallChains(entryPoints, triggers);
|
||||||
|
|
||||||
|
assertThat(second).isSameAs(first);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Two consecutive {@link CallGraphEngine#findChains} calls on a cached graph must agree.
|
* Two consecutive {@link CallGraphEngine#findChains} calls on a cached graph must agree.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -58,6 +58,50 @@ class LayeredDispatcherSampleTest {
|
|||||||
"OrderTransitionEvent.PAY", "OrderState.NEW", "OrderState.PAID");
|
"OrderTransitionEvent.PAY", "OrderState.NEW", "OrderState.PAID");
|
||||||
assertEndpointLinksSingleTransition(orderMachine, "POST /api/orders/rich/ship",
|
assertEndpointLinksSingleTransition(orderMachine, "POST /api/orders/rich/ship",
|
||||||
"OrderTransitionEvent.SHIP", "OrderState.PAID", "OrderState.SHIPPED");
|
"OrderTransitionEvent.SHIP", "OrderState.PAID", "OrderState.SHIPPED");
|
||||||
|
|
||||||
|
assertEndpointLinksSingleTransition(orderMachine, "POST /api/string-dispatch/orders/pay",
|
||||||
|
"OrderTransitionEvent.PAY", "OrderState.NEW", "OrderState.PAID");
|
||||||
|
assertEndpointLinksSingleTransition(orderMachine, "POST /api/string-dispatch/orders/ship",
|
||||||
|
"OrderTransitionEvent.SHIP", "OrderState.PAID", "OrderState.SHIPPED");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldLinkOnlyOneMatchedTransitionPerStringDispatchEndpoint(@TempDir Path tempDir) throws Exception {
|
||||||
|
Path sampleRoot = findProjectRoot().resolve("state_machines/layered_dispatcher_sample");
|
||||||
|
ExportService exportService = new ExportService(List.of(new JsonExporter()));
|
||||||
|
exportService.runExporter(sampleRoot, tempDir, List.of("json"), true, List.of(), null, null,
|
||||||
|
click.kamil.springstatemachineexporter.exporter.EnumFormat.fn,
|
||||||
|
click.kamil.springstatemachineexporter.exporter.EnumFormat.fn);
|
||||||
|
|
||||||
|
JsonNode orderMachine = readMachineJson(tempDir, "StandardOrderStateMachineConfiguration", new ObjectMapper());
|
||||||
|
|
||||||
|
assertSingleMatchedChain(orderMachine, "POST /api/string-dispatch/orders/pay", "OrderTransitionEvent.PAY");
|
||||||
|
assertSingleMatchedChain(orderMachine, "POST /api/string-dispatch/orders/ship", "OrderTransitionEvent.SHIP");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertSingleMatchedChain(JsonNode machineJson, String endpointName, String expectedEvent) {
|
||||||
|
List<JsonNode> matchedChains = findMatchedCallChains(machineJson, endpointName);
|
||||||
|
assertThat(matchedChains).as("matched chains for %s", endpointName).hasSize(1);
|
||||||
|
assertThat(matchedChains.get(0).get("matchedTransitions")).hasSize(1);
|
||||||
|
assertThat(matchedChains.get(0).get("matchedTransitions").get(0).get("event").asText()).isEqualTo(expectedEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<JsonNode> findMatchedCallChains(JsonNode machineJson, String endpointName) {
|
||||||
|
JsonNode callChains = machineJson.path("metadata").path("callChains");
|
||||||
|
if (!callChains.isArray()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
List<JsonNode> result = new java.util.ArrayList<>();
|
||||||
|
for (JsonNode chain : callChains) {
|
||||||
|
if (!endpointName.equals(chain.path("entryPoint").path("name").asText())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
JsonNode matched = chain.get("matchedTransitions");
|
||||||
|
if (matched != null && matched.isArray() && !matched.isEmpty()) {
|
||||||
|
result.add(chain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JsonNode readMachineJson(Path outputDir, String configBaseName, ObjectMapper mapper)
|
private static JsonNode readMachineJson(Path outputDir, String configBaseName, ObjectMapper mapper)
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.service;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.CallEdge;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class PathBindingEvaluatorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldPruneImpossibleSwitchDispatchArms(@TempDir Path tempDir) throws Exception {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class ApiController {
|
||||||
|
private final CommandGateway gateway = new CommandGateway();
|
||||||
|
public void pay() { gateway.executeViaMapper("order.pay"); }
|
||||||
|
}
|
||||||
|
enum DomainCommand { ORDER_PAY, ORDER_SHIP }
|
||||||
|
enum OrderEvent { PAY, SHIP }
|
||||||
|
class CommandGateway {
|
||||||
|
private final StringCommandMapper mapper = new StringCommandMapper();
|
||||||
|
private final CentralDispatcher central = new CentralDispatcher();
|
||||||
|
void executeViaMapper(String commandKey) {
|
||||||
|
DomainCommand command = mapper.fromString(commandKey);
|
||||||
|
central.dispatch(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class StringCommandMapper {
|
||||||
|
DomainCommand fromString(String commandKey) {
|
||||||
|
return switch (commandKey) {
|
||||||
|
case "order.pay" -> DomainCommand.ORDER_PAY;
|
||||||
|
case "order.ship" -> DomainCommand.ORDER_SHIP;
|
||||||
|
default -> throw new IllegalArgumentException(commandKey);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class CentralDispatcher {
|
||||||
|
void dispatch(DomainCommand command) {
|
||||||
|
switch (command) {
|
||||||
|
case ORDER_PAY -> orderPay();
|
||||||
|
case ORDER_SHIP -> orderShip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void orderPay() { fire(OrderEvent.PAY); }
|
||||||
|
void orderShip() { fire(OrderEvent.SHIP); }
|
||||||
|
void fire(OrderEvent event) {}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
Files.writeString(tempDir.resolve("App.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.setResolveBindings(true);
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
|
||||||
|
Map<String, List<CallEdge>> graph = engine.buildCallGraph();
|
||||||
|
|
||||||
|
CallGraphPathFinder pathFinder = new CallGraphPathFinder(context);
|
||||||
|
List<List<String>> rawPaths = pathFinder.findAllPaths(
|
||||||
|
"com.example.ApiController.pay",
|
||||||
|
"com.example.CentralDispatcher.fire",
|
||||||
|
graph,
|
||||||
|
new java.util.HashSet<>());
|
||||||
|
assertThat(rawPaths).hasSize(2);
|
||||||
|
|
||||||
|
PathBindingEvaluator evaluator = new PathBindingEvaluator(
|
||||||
|
context, engine.variableTracer, context.getConstantResolver(), engine.typeResolver);
|
||||||
|
|
||||||
|
List<CallEdge> dispatchEdges = graph.get("com.example.CentralDispatcher.dispatch");
|
||||||
|
assertThat(dispatchEdges.stream().map(CallEdge::getConstraint).filter(c -> c != null && !c.isBlank()))
|
||||||
|
.isNotEmpty();
|
||||||
|
|
||||||
|
boolean payPathCompatible = evaluator.isPathCompatible(rawPaths.get(0), graph, pathFinder);
|
||||||
|
Map<String, String> payBindings = evaluator.traceBindings(rawPaths.get(0), graph, pathFinder);
|
||||||
|
assertThat(payBindings)
|
||||||
|
.as("pay path bindings, compatible=%s", payPathCompatible)
|
||||||
|
.containsEntry("command", "DomainCommand.ORDER_PAY");
|
||||||
|
|
||||||
|
EntryPoint entry = EntryPoint.builder().className("com.example.ApiController").methodName("pay").build();
|
||||||
|
TriggerPoint trigger = TriggerPoint.builder()
|
||||||
|
.className("com.example.CentralDispatcher")
|
||||||
|
.methodName("fire")
|
||||||
|
.event("event")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
List<click.kamil.springstatemachineexporter.analysis.model.CallChain> chains =
|
||||||
|
engine.findChains(List.of(entry), List.of(trigger));
|
||||||
|
|
||||||
|
assertThat(chains).hasSize(1);
|
||||||
|
assertThat(chains.get(0).getMethodChain()).contains("com.example.CentralDispatcher.orderPay");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.service;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.exporter.JsonExporter;
|
||||||
|
import click.kamil.springstatemachineexporter.service.ExportService;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies switch-case branch pruning for string-key dispatch chains.
|
||||||
|
*/
|
||||||
|
class StringDispatchCallGraphGapTest {
|
||||||
|
|
||||||
|
private static Path findProjectRoot() {
|
||||||
|
Path current = Path.of(".").toAbsolutePath();
|
||||||
|
while (current != null && !Files.exists(current.resolve("settings.gradle"))) {
|
||||||
|
current = current.getParent();
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void payStringDispatchEndpointShouldMatchOnlyPayTransition(@TempDir Path tempDir) throws Exception {
|
||||||
|
Path sampleRoot = findProjectRoot().resolve("state_machines/layered_dispatcher_sample");
|
||||||
|
ExportService exportService = new ExportService(List.of(new JsonExporter()));
|
||||||
|
exportService.runExporter(sampleRoot, tempDir, List.of("json"), true, List.of(), null, null,
|
||||||
|
click.kamil.springstatemachineexporter.exporter.EnumFormat.fn,
|
||||||
|
click.kamil.springstatemachineexporter.exporter.EnumFormat.fn);
|
||||||
|
|
||||||
|
JsonNode orderMachine = readMachineJson(tempDir, "StandardOrderStateMachineConfiguration", new ObjectMapper());
|
||||||
|
List<JsonNode> matchedChains = findMatchedCallChains(orderMachine, "POST /api/string-dispatch/orders/pay");
|
||||||
|
|
||||||
|
assertThat(matchedChains).hasSize(1);
|
||||||
|
assertThat(matchedChains.get(0).get("matchedTransitions")).hasSize(1);
|
||||||
|
assertThat(matchedChains.get(0).get("matchedTransitions").get(0).get("event").asText())
|
||||||
|
.isEqualTo("OrderTransitionEvent.PAY");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JsonNode readMachineJson(Path outputDir, String configBaseName, ObjectMapper mapper)
|
||||||
|
throws Exception {
|
||||||
|
Path machineDir;
|
||||||
|
try (var stream = Files.list(outputDir)) {
|
||||||
|
machineDir = stream
|
||||||
|
.filter(Files::isDirectory)
|
||||||
|
.filter(path -> path.getFileName().toString().endsWith(configBaseName))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(() -> new IllegalStateException("No output for " + configBaseName));
|
||||||
|
}
|
||||||
|
Path jsonFile = machineDir.resolve(machineDir.getFileName() + ".json");
|
||||||
|
return mapper.readTree(jsonFile.toFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<JsonNode> findMatchedCallChains(JsonNode machineJson, String endpointName) {
|
||||||
|
JsonNode callChains = machineJson.path("metadata").path("callChains");
|
||||||
|
if (!callChains.isArray()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
List<JsonNode> result = new ArrayList<>();
|
||||||
|
for (JsonNode chain : callChains) {
|
||||||
|
if (!endpointName.equals(chain.path("entryPoint").path("name").asText())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
JsonNode matched = chain.get("matchedTransitions");
|
||||||
|
if (matched != null && matched.isArray() && !matched.isEmpty()) {
|
||||||
|
result.add(chain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.ast.common;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
|
import org.eclipse.jdt.core.dom.*;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class AstUtilsSwitchConstraintTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldExtractSwitchCaseConstraintForArrowSwitch(@TempDir Path tempDir) throws Exception {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
class Demo {
|
||||||
|
void dispatch(DomainCommand command) {
|
||||||
|
switch (command) {
|
||||||
|
case ORDER_PAY -> orderPay();
|
||||||
|
case ORDER_SHIP -> orderShip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void orderPay() {}
|
||||||
|
void orderShip() {}
|
||||||
|
}
|
||||||
|
enum DomainCommand { ORDER_PAY, ORDER_SHIP }
|
||||||
|
""";
|
||||||
|
Files.writeString(tempDir.resolve("Demo.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.setResolveBindings(true);
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
AtomicReference<MethodInvocation> orderPayCall = new AtomicReference<>();
|
||||||
|
for (CompilationUnit cu : context.getCompilationUnits()) {
|
||||||
|
cu.accept(new ASTVisitor() {
|
||||||
|
@Override
|
||||||
|
public boolean visit(MethodInvocation node) {
|
||||||
|
if ("orderPay".equals(node.getName().getIdentifier())
|
||||||
|
&& node.getExpression() == null) {
|
||||||
|
orderPayCall.set(node);
|
||||||
|
}
|
||||||
|
return super.visit(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orderPayCall.get() == null) {
|
||||||
|
org.junit.jupiter.api.Assertions.fail("orderPay MethodInvocation not found in AST");
|
||||||
|
}
|
||||||
|
assertThat(AstUtils.findEnclosingSwitchCase(orderPayCall.get())).isNotNull();
|
||||||
|
String constraint = AstUtils.findConditionConstraint(orderPayCall.get());
|
||||||
|
assertThat(constraint).contains("command == ORDER_PAY");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,6 @@
|
|||||||
{
|
{
|
||||||
"metadata" : {
|
"metadata" : {
|
||||||
"triggers" : [ {
|
"triggers" : [ {
|
||||||
"event" : "event",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : null,
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
}, {
|
|
||||||
"event" : "event",
|
"event" : "event",
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
||||||
"methodName" : "sendDocumentEvent",
|
"methodName" : "sendDocumentEvent",
|
||||||
@@ -154,96 +141,6 @@
|
|||||||
} ]
|
} ]
|
||||||
} ],
|
} ],
|
||||||
"callChains" : [ {
|
"callChains" : [ {
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/orders/pay",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.OrderController",
|
|
||||||
"methodName" : "pay",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/OrderController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/orders/pay",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.OrderController.pay", "click.kamil.examples.statemachine.layered.web.CommandGateway.payOrder", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderPay", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.PAY",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.PAY" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/orders/ship",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.OrderController",
|
|
||||||
"methodName" : "ship",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/OrderController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/orders/ship",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.OrderController.ship", "click.kamil.examples.statemachine.layered.web.CommandGateway.shipOrder", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderShip", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.SHIP",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.SHIP" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/orders/cancel",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.OrderController",
|
|
||||||
"methodName" : "cancel",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/OrderController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/orders/cancel",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.OrderController.cancel", "click.kamil.examples.statemachine.layered.web.CommandGateway.cancelOrder", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderCancel", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.CANCEL",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.CANCEL" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
"entryPoint" : {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
"name" : "POST /api/documents/submit",
|
"name" : "POST /api/documents/submit",
|
||||||
@@ -345,552 +242,6 @@
|
|||||||
"targetState" : "DocumentState.REJECTED",
|
"targetState" : "DocumentState.REJECTED",
|
||||||
"event" : "DocumentTransitionEvent.REJECT"
|
"event" : "DocumentTransitionEvent.REJECT"
|
||||||
} ]
|
} ]
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/pay",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "payWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/pay",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.payWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderPay", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.PAY",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.PAY" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/pay",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "payWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/pay",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.payWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderShip", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.SHIP",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.SHIP" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/pay",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "payWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/pay",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.payWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderCancel", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.CANCEL",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.CANCEL" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/pay",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "payWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/pay",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.payWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentSubmit", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.SUBMIT",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.SUBMIT" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : [ {
|
|
||||||
"sourceState" : "DocumentState.DRAFT",
|
|
||||||
"targetState" : "DocumentState.SUBMITTED",
|
|
||||||
"event" : "DocumentTransitionEvent.SUBMIT"
|
|
||||||
} ]
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/pay",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "payWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/pay",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.payWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentApprove", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.APPROVE",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.APPROVE" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : [ {
|
|
||||||
"sourceState" : "DocumentState.SUBMITTED",
|
|
||||||
"targetState" : "DocumentState.APPROVED",
|
|
||||||
"event" : "DocumentTransitionEvent.APPROVE"
|
|
||||||
} ]
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/pay",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "payWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/pay",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.payWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentReject", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.REJECT",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.REJECT" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : [ {
|
|
||||||
"sourceState" : "DocumentState.SUBMITTED",
|
|
||||||
"targetState" : "DocumentState.REJECTED",
|
|
||||||
"event" : "DocumentTransitionEvent.REJECT"
|
|
||||||
} ]
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/ship",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "shipWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/ship",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.shipWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderPay", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.PAY",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.PAY" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/ship",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "shipWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/ship",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.shipWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderShip", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.SHIP",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.SHIP" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/ship",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "shipWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/ship",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.shipWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderCancel", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.CANCEL",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.CANCEL" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/ship",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "shipWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/ship",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.shipWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentSubmit", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.SUBMIT",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.SUBMIT" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : [ {
|
|
||||||
"sourceState" : "DocumentState.DRAFT",
|
|
||||||
"targetState" : "DocumentState.SUBMITTED",
|
|
||||||
"event" : "DocumentTransitionEvent.SUBMIT"
|
|
||||||
} ]
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/ship",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "shipWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/ship",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.shipWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentApprove", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.APPROVE",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.APPROVE" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : [ {
|
|
||||||
"sourceState" : "DocumentState.SUBMITTED",
|
|
||||||
"targetState" : "DocumentState.APPROVED",
|
|
||||||
"event" : "DocumentTransitionEvent.APPROVE"
|
|
||||||
} ]
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/ship",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "shipWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/ship",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.shipWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentReject", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.REJECT",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.REJECT" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : [ {
|
|
||||||
"sourceState" : "DocumentState.SUBMITTED",
|
|
||||||
"targetState" : "DocumentState.REJECTED",
|
|
||||||
"event" : "DocumentTransitionEvent.REJECT"
|
|
||||||
} ]
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/orders/rich/pay",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.RichOrderController",
|
|
||||||
"methodName" : "payViaRichEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/RichOrderController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/orders/rich/pay",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.RichOrderController.payViaRichEvent", "click.kamil.examples.statemachine.layered.web.CommandGateway.payOrderViaRichEvent", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderPayViaRichEvent", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.PAY",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.PAY" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/orders/rich/ship",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.RichOrderController",
|
|
||||||
"methodName" : "shipViaRichEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/RichOrderController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/orders/rich/ship",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.RichOrderController.shipViaRichEvent", "click.kamil.examples.statemachine.layered.web.CommandGateway.shipOrderViaRichEvent", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderShipViaRichEvent", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.SHIP",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.SHIP" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/commands/{commandKey}",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.GenericCommandController",
|
|
||||||
"methodName" : "execute",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/GenericCommandController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/commands/{commandKey}",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ {
|
|
||||||
"name" : "commandKey",
|
|
||||||
"type" : "String",
|
|
||||||
"annotations" : [ "PathVariable" ]
|
|
||||||
} ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.GenericCommandController.execute", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderPay", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.PAY",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.PAY" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/commands/{commandKey}",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.GenericCommandController",
|
|
||||||
"methodName" : "execute",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/GenericCommandController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/commands/{commandKey}",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ {
|
|
||||||
"name" : "commandKey",
|
|
||||||
"type" : "String",
|
|
||||||
"annotations" : [ "PathVariable" ]
|
|
||||||
} ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.GenericCommandController.execute", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderShip", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.SHIP",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.SHIP" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/commands/{commandKey}",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.GenericCommandController",
|
|
||||||
"methodName" : "execute",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/GenericCommandController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/commands/{commandKey}",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ {
|
|
||||||
"name" : "commandKey",
|
|
||||||
"type" : "String",
|
|
||||||
"annotations" : [ "PathVariable" ]
|
|
||||||
} ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.GenericCommandController.execute", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderCancel", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.CANCEL",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.CANCEL" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
}, {
|
||||||
"entryPoint" : {
|
"entryPoint" : {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -920,7 +271,7 @@
|
|||||||
"lineNumber" : 81,
|
"lineNumber" : 81,
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.SUBMIT" ],
|
"polymorphicEvents" : [ "DocumentTransitionEvent.SUBMIT" ],
|
||||||
"external" : false,
|
"external" : false,
|
||||||
"constraint" : null,
|
"constraint" : "command == DOCUMENT_SUBMIT",
|
||||||
"ambiguous" : false
|
"ambiguous" : false
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
@@ -958,7 +309,7 @@
|
|||||||
"lineNumber" : 81,
|
"lineNumber" : 81,
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.APPROVE" ],
|
"polymorphicEvents" : [ "DocumentTransitionEvent.APPROVE" ],
|
||||||
"external" : false,
|
"external" : false,
|
||||||
"constraint" : null,
|
"constraint" : "command == DOCUMENT_APPROVE",
|
||||||
"ambiguous" : false
|
"ambiguous" : false
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
@@ -996,7 +347,7 @@
|
|||||||
"lineNumber" : 81,
|
"lineNumber" : 81,
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.REJECT" ],
|
"polymorphicEvents" : [ "DocumentTransitionEvent.REJECT" ],
|
||||||
"external" : false,
|
"external" : false,
|
||||||
"constraint" : null,
|
"constraint" : "command == DOCUMENT_REJECT",
|
||||||
"ambiguous" : false
|
"ambiguous" : false
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
|
|||||||
@@ -13,19 +13,6 @@
|
|||||||
"external" : false,
|
"external" : false,
|
||||||
"constraint" : null,
|
"constraint" : null,
|
||||||
"ambiguous" : false
|
"ambiguous" : false
|
||||||
}, {
|
|
||||||
"event" : "event",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : null,
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
} ],
|
} ],
|
||||||
"entryPoints" : [ {
|
"entryPoints" : [ {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -255,96 +242,6 @@
|
|||||||
"targetState" : "OrderState.CANCELLED",
|
"targetState" : "OrderState.CANCELLED",
|
||||||
"event" : "OrderTransitionEvent.CANCEL"
|
"event" : "OrderTransitionEvent.CANCEL"
|
||||||
} ]
|
} ]
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/documents/submit",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.DocumentController",
|
|
||||||
"methodName" : "submit",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/DocumentController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/documents/submit",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.DocumentController.submit", "click.kamil.examples.statemachine.layered.web.CommandGateway.submitDocument", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentSubmit", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.SUBMIT",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.SUBMIT" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/documents/approve",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.DocumentController",
|
|
||||||
"methodName" : "approve",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/DocumentController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/documents/approve",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.DocumentController.approve", "click.kamil.examples.statemachine.layered.web.CommandGateway.approveDocument", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentApprove", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.APPROVE",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.APPROVE" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/documents/reject",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.DocumentController",
|
|
||||||
"methodName" : "reject",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/DocumentController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/documents/reject",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.DocumentController.reject", "click.kamil.examples.statemachine.layered.web.CommandGateway.rejectDocument", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentReject", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.REJECT",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.REJECT" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
}, {
|
||||||
"entryPoint" : {
|
"entryPoint" : {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -370,199 +267,7 @@
|
|||||||
"lineNumber" : 75,
|
"lineNumber" : 75,
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.PAY" ],
|
"polymorphicEvents" : [ "OrderTransitionEvent.PAY" ],
|
||||||
"external" : false,
|
"external" : false,
|
||||||
"constraint" : null,
|
"constraint" : "command == ORDER_PAY",
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : [ {
|
|
||||||
"sourceState" : "OrderState.NEW",
|
|
||||||
"targetState" : "OrderState.PAID",
|
|
||||||
"event" : "OrderTransitionEvent.PAY"
|
|
||||||
} ]
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/pay",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "payWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/pay",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.payWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderShip", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.SHIP",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.SHIP" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : [ {
|
|
||||||
"sourceState" : "OrderState.PAID",
|
|
||||||
"targetState" : "OrderState.SHIPPED",
|
|
||||||
"event" : "OrderTransitionEvent.SHIP"
|
|
||||||
} ]
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/pay",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "payWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/pay",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.payWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderCancel", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.CANCEL",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.CANCEL" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : [ {
|
|
||||||
"sourceState" : "OrderState.PAID",
|
|
||||||
"targetState" : "OrderState.CANCELLED",
|
|
||||||
"event" : "OrderTransitionEvent.CANCEL"
|
|
||||||
} ]
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/pay",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "payWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/pay",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.payWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentSubmit", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.SUBMIT",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.SUBMIT" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/pay",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "payWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/pay",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.payWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentApprove", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.APPROVE",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.APPROVE" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/pay",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "payWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/pay",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.payWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentReject", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.REJECT",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.REJECT" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/ship",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "shipWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/ship",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.shipWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderPay", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.PAY",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.PAY" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
"ambiguous" : false
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
@@ -596,7 +301,7 @@
|
|||||||
"lineNumber" : 75,
|
"lineNumber" : 75,
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.SHIP" ],
|
"polymorphicEvents" : [ "OrderTransitionEvent.SHIP" ],
|
||||||
"external" : false,
|
"external" : false,
|
||||||
"constraint" : null,
|
"constraint" : "command == ORDER_SHIP",
|
||||||
"ambiguous" : false
|
"ambiguous" : false
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
@@ -605,130 +310,6 @@
|
|||||||
"targetState" : "OrderState.SHIPPED",
|
"targetState" : "OrderState.SHIPPED",
|
||||||
"event" : "OrderTransitionEvent.SHIP"
|
"event" : "OrderTransitionEvent.SHIP"
|
||||||
} ]
|
} ]
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/ship",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "shipWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/ship",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.shipWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.orderCancel", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendOrderEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "OrderTransitionEvent.CANCEL",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendOrderEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 75,
|
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.CANCEL" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : [ {
|
|
||||||
"sourceState" : "OrderState.PAID",
|
|
||||||
"targetState" : "OrderState.CANCELLED",
|
|
||||||
"event" : "OrderTransitionEvent.CANCEL"
|
|
||||||
} ]
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/ship",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "shipWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/ship",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.shipWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentSubmit", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.SUBMIT",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.SUBMIT" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/ship",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "shipWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/ship",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.shipWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentApprove", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.APPROVE",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.APPROVE" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/string-dispatch/orders/ship",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.StringDispatchController",
|
|
||||||
"methodName" : "shipWithStringKey",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/StringDispatchController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/string-dispatch/orders/ship",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.StringDispatchController.shipWithStringKey", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentReject", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.REJECT",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.REJECT" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
}, {
|
||||||
"entryPoint" : {
|
"entryPoint" : {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -826,7 +407,7 @@
|
|||||||
"lineNumber" : 75,
|
"lineNumber" : 75,
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.PAY" ],
|
"polymorphicEvents" : [ "OrderTransitionEvent.PAY" ],
|
||||||
"external" : false,
|
"external" : false,
|
||||||
"constraint" : null,
|
"constraint" : "command == ORDER_PAY",
|
||||||
"ambiguous" : false
|
"ambiguous" : false
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
@@ -864,7 +445,7 @@
|
|||||||
"lineNumber" : 75,
|
"lineNumber" : 75,
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.SHIP" ],
|
"polymorphicEvents" : [ "OrderTransitionEvent.SHIP" ],
|
||||||
"external" : false,
|
"external" : false,
|
||||||
"constraint" : null,
|
"constraint" : "command == ORDER_SHIP",
|
||||||
"ambiguous" : false
|
"ambiguous" : false
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
@@ -902,7 +483,7 @@
|
|||||||
"lineNumber" : 75,
|
"lineNumber" : 75,
|
||||||
"polymorphicEvents" : [ "OrderTransitionEvent.CANCEL" ],
|
"polymorphicEvents" : [ "OrderTransitionEvent.CANCEL" ],
|
||||||
"external" : false,
|
"external" : false,
|
||||||
"constraint" : null,
|
"constraint" : "command == ORDER_CANCEL",
|
||||||
"ambiguous" : false
|
"ambiguous" : false
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
@@ -911,108 +492,6 @@
|
|||||||
"targetState" : "OrderState.CANCELLED",
|
"targetState" : "OrderState.CANCELLED",
|
||||||
"event" : "OrderTransitionEvent.CANCEL"
|
"event" : "OrderTransitionEvent.CANCEL"
|
||||||
} ]
|
} ]
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/commands/{commandKey}",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.GenericCommandController",
|
|
||||||
"methodName" : "execute",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/GenericCommandController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/commands/{commandKey}",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ {
|
|
||||||
"name" : "commandKey",
|
|
||||||
"type" : "String",
|
|
||||||
"annotations" : [ "PathVariable" ]
|
|
||||||
} ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.GenericCommandController.execute", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentSubmit", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.SUBMIT",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.SUBMIT" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/commands/{commandKey}",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.GenericCommandController",
|
|
||||||
"methodName" : "execute",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/GenericCommandController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/commands/{commandKey}",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ {
|
|
||||||
"name" : "commandKey",
|
|
||||||
"type" : "String",
|
|
||||||
"annotations" : [ "PathVariable" ]
|
|
||||||
} ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.GenericCommandController.execute", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentApprove", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.APPROVE",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.APPROVE" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
}, {
|
|
||||||
"entryPoint" : {
|
|
||||||
"type" : "REST",
|
|
||||||
"name" : "POST /api/commands/{commandKey}",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.web.GenericCommandController",
|
|
||||||
"methodName" : "execute",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/web/GenericCommandController.java",
|
|
||||||
"metadata" : {
|
|
||||||
"path" : "/api/commands/{commandKey}",
|
|
||||||
"verb" : "POST"
|
|
||||||
},
|
|
||||||
"parameters" : [ {
|
|
||||||
"name" : "commandKey",
|
|
||||||
"type" : "String",
|
|
||||||
"annotations" : [ "PathVariable" ]
|
|
||||||
} ]
|
|
||||||
},
|
|
||||||
"methodChain" : [ "click.kamil.examples.statemachine.layered.web.GenericCommandController.execute", "click.kamil.examples.statemachine.layered.web.CommandGateway.executeViaMapper", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.dispatch", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.documentReject", "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher.sendDocumentEvent" ],
|
|
||||||
"triggerPoint" : {
|
|
||||||
"event" : "DocumentTransitionEvent.REJECT",
|
|
||||||
"className" : "click.kamil.examples.statemachine.layered.dispatch.CentralEventDispatcher",
|
|
||||||
"methodName" : "sendDocumentEvent",
|
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/layered/dispatch/CentralEventDispatcher.java",
|
|
||||||
"sourceModule" : null,
|
|
||||||
"stateMachineId" : null,
|
|
||||||
"sourceState" : null,
|
|
||||||
"lineNumber" : 81,
|
|
||||||
"polymorphicEvents" : [ "DocumentTransitionEvent.REJECT" ],
|
|
||||||
"external" : false,
|
|
||||||
"constraint" : null,
|
|
||||||
"ambiguous" : false
|
|
||||||
},
|
|
||||||
"contextMachineId" : null,
|
|
||||||
"matchedTransitions" : null
|
|
||||||
} ],
|
} ],
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"default" : { }
|
"default" : { }
|
||||||
|
|||||||
Reference in New Issue
Block a user