Compare commits
4 Commits
ai-branch-
...
29e391c472
| Author | SHA1 | Date | |
|---|---|---|---|
| 29e391c472 | |||
| e00f4dca81 | |||
| bf82cc3562 | |||
| 24b67be64b |
@@ -26,7 +26,10 @@ public class HeuristicEventMatchingEngine implements EventMatchingEngine {
|
||||
if (pe.contains(".")) {
|
||||
simplePe = pe.substring(pe.lastIndexOf('.') + 1);
|
||||
}
|
||||
if (simplePe.equals(smEventRaw) || simplePe.equals(smEvent)) {
|
||||
String simplifiedPe = simplify(simplePe);
|
||||
|
||||
if (simplePe.equals(smEventRaw) || simplePe.equals(smEvent) ||
|
||||
simplePe.equalsIgnoreCase(smEvent) || simplifiedPe.equalsIgnoreCase(smEvent)) {
|
||||
hasPolyMatch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package click.kamil.springstatemachineexporter.analysis.resolver;
|
||||
|
||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PolymorphicEventResolver {
|
||||
|
||||
/**
|
||||
* Attempts to resolve a dynamically passed event variable into a set of
|
||||
* concrete polymorphic event names.
|
||||
*
|
||||
* @param triggerPoint The original trigger point to resolve.
|
||||
* @param resolvedValue The current String expression (e.g., "event.getType()" or "event")
|
||||
* @param path The call path trace leading up to this variable.
|
||||
* @param context The codebase context for deep type resolution.
|
||||
* @return The updated TriggerPoint with polymorphic events set, or the original if unable to resolve.
|
||||
*/
|
||||
TriggerPoint resolvePolymorphicEvents(TriggerPoint triggerPoint, String resolvedValue, List<String> path, CodebaseContext context);
|
||||
|
||||
}
|
||||
@@ -325,6 +325,10 @@ public class GenericEventDetector {
|
||||
}
|
||||
}
|
||||
|
||||
if (expr instanceof ClassInstanceCreation cic) {
|
||||
return cic.getType().toString();
|
||||
}
|
||||
|
||||
if (!(expr instanceof MethodInvocation mi)) return null;
|
||||
|
||||
// Trace back chain: MessageBuilder.withPayload(event).setHeader(...).build()
|
||||
|
||||
@@ -142,43 +142,85 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
||||
}
|
||||
|
||||
List<String> polymorphicEvents = new ArrayList<>();
|
||||
if (resolvedValue.matches(".*\\.[a-zA-Z0-9_]+\\(\\)")) {
|
||||
int lastDot = resolvedValue.lastIndexOf('.');
|
||||
int firstDot = resolvedValue.indexOf('.');
|
||||
int openParen = resolvedValue.indexOf('(', lastDot);
|
||||
|
||||
if (lastDot > 0 && openParen > lastDot) {
|
||||
String varName = resolvedValue.substring(0, firstDot);
|
||||
if (varName.contains("(")) {
|
||||
varName = null;
|
||||
// Parse resolvedValue using JDT to robustly handle complex expressions
|
||||
org.eclipse.jdt.core.dom.ASTParser exprParser = org.eclipse.jdt.core.dom.ASTParser.newParser(org.eclipse.jdt.core.dom.AST.getJLSLatest());
|
||||
exprParser.setSource(resolvedValue.toCharArray());
|
||||
exprParser.setKind(org.eclipse.jdt.core.dom.ASTParser.K_EXPRESSION);
|
||||
org.eclipse.jdt.core.dom.ASTNode exprNode = exprParser.createAST(null);
|
||||
|
||||
String varName = null;
|
||||
String methodName = null;
|
||||
String declaredType = null;
|
||||
String sourceMethod = null;
|
||||
|
||||
if (exprNode instanceof org.eclipse.jdt.core.dom.MethodInvocation mi) {
|
||||
methodName = mi.getName().getIdentifier();
|
||||
if (mi.getExpression() instanceof org.eclipse.jdt.core.dom.SimpleName sn) {
|
||||
varName = sn.getIdentifier();
|
||||
} else if (mi.getExpression() instanceof org.eclipse.jdt.core.dom.ParenthesizedExpression pe &&
|
||||
pe.getExpression() instanceof org.eclipse.jdt.core.dom.CastExpression ce &&
|
||||
ce.getExpression() instanceof org.eclipse.jdt.core.dom.SimpleName sn) {
|
||||
varName = sn.getIdentifier();
|
||||
declaredType = ce.getType().toString();
|
||||
sourceMethod = "inline-cast";
|
||||
} else if (mi.getExpression() instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation cic) {
|
||||
declaredType = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cic.getType());
|
||||
sourceMethod = "inline-instantiation";
|
||||
} else {
|
||||
// Fallback for complex chained expressions
|
||||
String exprStr = mi.getExpression() != null ? mi.getExpression().toString() : "";
|
||||
if (!exprStr.contains("(")) {
|
||||
varName = exprStr;
|
||||
}
|
||||
String methodName = resolvedValue.substring(lastDot + 1, openParen);
|
||||
}
|
||||
} else if (exprNode instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation cic) {
|
||||
declaredType = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cic.getType());
|
||||
sourceMethod = "inline-instantiation";
|
||||
polymorphicEvents.add(declaredType); // Track the payload type as the event
|
||||
} else if (exprNode instanceof org.eclipse.jdt.core.dom.ConditionalExpression cond) {
|
||||
if (cond.getThenExpression() instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation cicThen) {
|
||||
polymorphicEvents.add(click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cicThen.getType()));
|
||||
}
|
||||
if (cond.getElseExpression() instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation cicElse) {
|
||||
polymorphicEvents.add(click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cicElse.getType()));
|
||||
}
|
||||
sourceMethod = "inline-ternary";
|
||||
declaredType = polymorphicEvents.isEmpty() ? null : polymorphicEvents.get(0);
|
||||
} else if (exprNode instanceof org.eclipse.jdt.core.dom.SimpleName sn) {
|
||||
varName = sn.getIdentifier();
|
||||
methodName = "VariableReference"; // We just want to trigger deep trace
|
||||
} else if (exprNode instanceof org.eclipse.jdt.core.dom.ParenthesizedExpression pe &&
|
||||
pe.getExpression() instanceof org.eclipse.jdt.core.dom.CastExpression ce &&
|
||||
ce.getExpression() instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation cic) {
|
||||
declaredType = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cic.getType());
|
||||
sourceMethod = "inline-instantiation";
|
||||
polymorphicEvents.add(declaredType);
|
||||
} else if (exprNode instanceof org.eclipse.jdt.core.dom.CastExpression ce &&
|
||||
ce.getExpression() instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation cic) {
|
||||
declaredType = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cic.getType());
|
||||
sourceMethod = "inline-instantiation";
|
||||
polymorphicEvents.add(declaredType);
|
||||
}
|
||||
|
||||
String declaredType = null;
|
||||
String sourceMethod = null;
|
||||
|
||||
if (varName != null) {
|
||||
for (String methodFqn : path) {
|
||||
declaredType = getVariableDeclaredType(methodFqn, varName);
|
||||
if (declaredType != null) {
|
||||
sourceMethod = methodFqn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String baseExpr = resolvedValue.substring(0, lastDot);
|
||||
if (baseExpr.contains("new ")) {
|
||||
int newIdx = baseExpr.indexOf("new ");
|
||||
int openParenBase = baseExpr.indexOf('(', newIdx);
|
||||
if (openParenBase > newIdx) {
|
||||
declaredType = baseExpr.substring(newIdx + 4, openParenBase).trim();
|
||||
if (declaredType.contains("<")) {
|
||||
declaredType = declaredType.substring(0, declaredType.indexOf('<'));
|
||||
}
|
||||
sourceMethod = "inline-instantiation";
|
||||
}
|
||||
if (methodName != null) {
|
||||
if (varName != null && declaredType == null) {
|
||||
for (String methodFqn : path) {
|
||||
declaredType = getVariableDeclaredType(methodFqn, varName);
|
||||
if (declaredType != null) {
|
||||
sourceMethod = methodFqn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If it wasn't a variable, it might be a static method call (e.g., EventBuilder.buildEvent())
|
||||
if (declaredType == null && varName.matches("^[A-Z].*")) {
|
||||
org.eclipse.jdt.core.dom.TypeDeclaration staticTd = context.getTypeDeclaration(varName);
|
||||
if (staticTd != null) {
|
||||
declaredType = context.getFqn(staticTd);
|
||||
sourceMethod = "static-call";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (declaredType != null) {
|
||||
System.out.println("DEEP TRACE: " + sourceMethod + " " + (varName != null ? varName : "inline") + " -> " + declaredType);
|
||||
@@ -215,9 +257,6 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LAST RESORT FALLBACK: If AST deep trace failed to find a valid ALL_CAPS constant
|
||||
// (e.g. it returned a field name like 'type', or nothing), we can scrape the resolvedValue string directly
|
||||
// for string literals or enum-like constants.
|
||||
boolean hasValidConstant = false;
|
||||
@@ -259,7 +298,16 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
||||
return !val.equals(val.toUpperCase()) || val.length() <= 2;
|
||||
});
|
||||
}
|
||||
|
||||
List<String> newPolyEvents = new ArrayList<>();
|
||||
for (String pe : polymorphicEvents) {
|
||||
List<String> resolved = resolveClassConstantReturns(pe, context, null);
|
||||
if (resolved != null && !resolved.isEmpty()) {
|
||||
newPolyEvents.addAll(resolved);
|
||||
} else {
|
||||
newPolyEvents.add(pe);
|
||||
}
|
||||
}
|
||||
polymorphicEvents = newPolyEvents;
|
||||
|
||||
if (!resolvedValue.equals(event) || !polymorphicEvents.isEmpty()) {
|
||||
return TriggerPoint.builder()
|
||||
@@ -384,6 +432,8 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
||||
} else {
|
||||
constants.add(val);
|
||||
}
|
||||
} else if (retExpr instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation cic) {
|
||||
constants.add(click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cic.getType()));
|
||||
} else if (retExpr instanceof QualifiedName qn) {
|
||||
constants.add(qn.toString());
|
||||
} else if (retExpr instanceof SimpleName sn) {
|
||||
@@ -405,6 +455,31 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
||||
return constants;
|
||||
}
|
||||
|
||||
private List<String> resolveClassConstantReturns(String className, click.kamil.springstatemachineexporter.ast.common.CodebaseContext context, CompilationUnit contextCu) {
|
||||
TypeDeclaration td = contextCu != null ? context.getTypeDeclaration(className, contextCu) : context.getTypeDeclaration(className);
|
||||
if (td == null) td = context.getTypeDeclaration(className);
|
||||
if (td == null) return null;
|
||||
|
||||
final List<String> resolvedConstants = new ArrayList<>();
|
||||
for (MethodDeclaration md : td.getMethods()) {
|
||||
if (md.getBody() != null) {
|
||||
md.getBody().accept(new ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(ReturnStatement node) {
|
||||
if (node.getExpression() instanceof QualifiedName qn) {
|
||||
resolvedConstants.add(qn.getName().getIdentifier());
|
||||
} else if (node.getExpression() instanceof org.eclipse.jdt.core.dom.StringLiteral sl) {
|
||||
resolvedConstants.add(sl.getLiteralValue());
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!resolvedConstants.isEmpty()) return resolvedConstants;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String traceLocalVariable(String methodFqn, String varName) {
|
||||
if (methodFqn == null || !methodFqn.contains(".")) return null;
|
||||
String className = methodFqn.substring(0, methodFqn.lastIndexOf('.'));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,10 @@ import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
||||
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||
import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver;
|
||||
import click.kamil.springstatemachineexporter.analysis.spring.InjectionPointAnalyzer;
|
||||
import click.kamil.springstatemachineexporter.analysis.spring.SpringBeanRegistry;
|
||||
import click.kamil.springstatemachineexporter.analysis.spring.SpringContextScanner;
|
||||
import click.kamil.springstatemachineexporter.analysis.spring.SpringDependencyResolver;
|
||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
@@ -32,7 +36,17 @@ public class JdtIntelligenceProvider implements CodebaseIntelligenceProvider {
|
||||
this.eventDetector = new GenericEventDetector(context, constantResolver, context.getLibraryHints());
|
||||
this.mvcDetector = new SpringMvcDetector(context, constantResolver);
|
||||
this.messagingDetector = new MessagingDetector(context);
|
||||
this.callGraphEngine = new HeuristicCallGraphEngine(context);
|
||||
|
||||
SpringBeanRegistry registry = new SpringBeanRegistry();
|
||||
SpringContextScanner scanner = new SpringContextScanner(registry);
|
||||
for (CompilationUnit cu : context.getCompilationUnits()) {
|
||||
cu.accept(scanner);
|
||||
}
|
||||
SpringDependencyResolver dependencyResolver = new SpringDependencyResolver(registry);
|
||||
InjectionPointAnalyzer injectionAnalyzer = new InjectionPointAnalyzer(dependencyResolver);
|
||||
|
||||
this.callGraphEngine = new JdtCallGraphEngine(context, injectionAnalyzer);
|
||||
|
||||
this.lifecycleDetector = new LifecycleDetector(context);
|
||||
this.interceptorDetector = new InterceptorDetector(context);
|
||||
this.componentDetector = new SpringComponentDetector(context);
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package click.kamil.springstatemachineexporter.analysis.spring;
|
||||
|
||||
import org.eclipse.jdt.core.dom.IAnnotationBinding;
|
||||
import org.eclipse.jdt.core.dom.IMemberValuePairBinding;
|
||||
import org.eclipse.jdt.core.dom.IVariableBinding;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class InjectionPointAnalyzer {
|
||||
|
||||
private final SpringDependencyResolver resolver;
|
||||
|
||||
public InjectionPointAnalyzer(SpringDependencyResolver resolver) {
|
||||
this.resolver = resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the Spring bean injected into the given variable.
|
||||
*
|
||||
* @param variableBinding The binding of the field or parameter being injected.
|
||||
* @return The FQN of the resolved concrete bean class, or null if unresolved.
|
||||
*/
|
||||
public String resolveInjectedBeanFqn(IVariableBinding variableBinding) {
|
||||
if (variableBinding == null || variableBinding.getType() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String requiredTypeFqn = variableBinding.getType().getQualifiedName();
|
||||
String injectionName = variableBinding.getName();
|
||||
String qualifier = extractQualifier(variableBinding);
|
||||
|
||||
List<SpringBean> resolvedBeans = resolver.resolve(requiredTypeFqn, qualifier, injectionName);
|
||||
|
||||
if (resolvedBeans != null && resolvedBeans.size() == 1) {
|
||||
return resolvedBeans.get(0).getTypeFqn();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String extractQualifier(IVariableBinding binding) {
|
||||
String qualifier = getQualifierValue(binding.getAnnotations());
|
||||
if (qualifier != null) {
|
||||
return qualifier;
|
||||
}
|
||||
|
||||
if (binding.isField()) {
|
||||
org.eclipse.jdt.core.dom.ITypeBinding declaringClass = binding.getDeclaringClass();
|
||||
if (declaringClass != null) {
|
||||
for (org.eclipse.jdt.core.dom.IMethodBinding method : declaringClass.getDeclaredMethods()) {
|
||||
boolean isAutowiredMethod = false;
|
||||
for (IAnnotationBinding ann : method.getAnnotations()) {
|
||||
if (ann.getAnnotationType() != null && "org.springframework.beans.factory.annotation.Autowired".equals(ann.getAnnotationType().getQualifiedName())) {
|
||||
isAutowiredMethod = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (method.isConstructor() || isAutowiredMethod) {
|
||||
for (int i = 0; i < method.getParameterTypes().length; i++) {
|
||||
org.eclipse.jdt.core.dom.ITypeBinding paramType = method.getParameterTypes()[i];
|
||||
try {
|
||||
IAnnotationBinding[] paramAnns = method.getParameterAnnotations(i);
|
||||
String paramQual = getQualifierValue(paramAnns);
|
||||
if (paramQual != null && paramType.getErasure().isEqualTo(binding.getType().getErasure())) {
|
||||
// For setters, verify it roughly matches the field name or just rely on type.
|
||||
// We will rely on type equality for now as a heuristic.
|
||||
return paramQual;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getQualifierValue(IAnnotationBinding[] annotations) {
|
||||
for (IAnnotationBinding ann : annotations) {
|
||||
if (ann.getAnnotationType() != null && "org.springframework.beans.factory.annotation.Qualifier".equals(ann.getAnnotationType().getQualifiedName())) {
|
||||
for (IMemberValuePairBinding pair : ann.getDeclaredMemberValuePairs()) {
|
||||
if ("value".equals(pair.getName()) && pair.getValue() instanceof String) {
|
||||
return (String) pair.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package click.kamil.springstatemachineexporter.analysis.spring;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class SpringBean {
|
||||
private String typeFqn;
|
||||
|
||||
@Builder.Default
|
||||
private Set<String> assignableTypes = new HashSet<>();
|
||||
|
||||
@Builder.Default
|
||||
private Set<String> beanNames = new HashSet<>();
|
||||
|
||||
@Builder.Default
|
||||
private Set<String> qualifiers = new HashSet<>();
|
||||
|
||||
private boolean isPrimary;
|
||||
private Integer order;
|
||||
private String declaringClassFqn;
|
||||
private String factoryMethodName;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package click.kamil.springstatemachineexporter.analysis.spring;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class SpringBeanRegistry {
|
||||
private final List<SpringBean> beans = new ArrayList<>();
|
||||
|
||||
public void addBean(SpringBean bean) {
|
||||
beans.add(bean);
|
||||
}
|
||||
|
||||
public List<SpringBean> getBeans() {
|
||||
return Collections.unmodifiableList(beans);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
package click.kamil.springstatemachineexporter.analysis.spring;
|
||||
|
||||
import org.eclipse.jdt.core.dom.*;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class SpringContextScanner extends ASTVisitor {
|
||||
private final SpringBeanRegistry registry;
|
||||
|
||||
public SpringContextScanner(SpringBeanRegistry registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(TypeDeclaration node) {
|
||||
ITypeBinding typeBinding = node.resolveBinding();
|
||||
if (typeBinding == null) return true;
|
||||
|
||||
if (isSpringComponent(typeBinding)) {
|
||||
SpringBean bean = SpringBean.builder()
|
||||
.typeFqn(typeBinding.getQualifiedName())
|
||||
.assignableTypes(getAssignableTypes(typeBinding))
|
||||
.isPrimary(hasAnnotation(typeBinding, "org.springframework.context.annotation.Primary"))
|
||||
.qualifiers(extractQualifiers(typeBinding))
|
||||
.order(extractOrder(typeBinding))
|
||||
.declaringClassFqn(typeBinding.getQualifiedName())
|
||||
.build();
|
||||
|
||||
// Default bean name is uncapitalized simple name
|
||||
String defaultName = Character.toLowerCase(node.getName().getIdentifier().charAt(0)) + node.getName().getIdentifier().substring(1);
|
||||
|
||||
// Check if @Component or @Service specifies a name (e.g., @Service("myService"))
|
||||
String explicitName = extractStereotypeValue(typeBinding);
|
||||
if (explicitName != null && !explicitName.isEmpty()) {
|
||||
bean.getBeanNames().add(explicitName);
|
||||
} else {
|
||||
bean.getBeanNames().add(defaultName);
|
||||
}
|
||||
|
||||
registry.addBean(bean);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(MethodDeclaration node) {
|
||||
IMethodBinding methodBinding = node.resolveBinding();
|
||||
if (methodBinding == null) return true;
|
||||
|
||||
if (hasAnnotation(methodBinding, "org.springframework.context.annotation.Bean")) {
|
||||
ITypeBinding returnType = methodBinding.getReturnType();
|
||||
if (returnType != null) {
|
||||
Set<String> assignables = getAssignableTypes(returnType);
|
||||
String[] concreteType = new String[] { returnType.getQualifiedName() };
|
||||
|
||||
if (node.getBody() != null) {
|
||||
node.getBody().accept(new ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(ReturnStatement ret) {
|
||||
if (ret.getExpression() != null) {
|
||||
ITypeBinding exprType = ret.getExpression().resolveTypeBinding();
|
||||
if (exprType != null && exprType.isClass()) {
|
||||
concreteType[0] = exprType.getQualifiedName();
|
||||
assignables.addAll(getAssignableTypes(exprType));
|
||||
}
|
||||
}
|
||||
return super.visit(ret);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SpringBean bean = SpringBean.builder()
|
||||
.typeFqn(concreteType[0])
|
||||
.assignableTypes(assignables)
|
||||
.isPrimary(hasAnnotation(methodBinding, "org.springframework.context.annotation.Primary"))
|
||||
.qualifiers(extractQualifiers(methodBinding))
|
||||
.order(extractOrder(methodBinding))
|
||||
.declaringClassFqn(methodBinding.getDeclaringClass().getQualifiedName())
|
||||
.factoryMethodName(node.getName().getIdentifier())
|
||||
.build();
|
||||
|
||||
// Default bean name is method name
|
||||
String defaultName = node.getName().getIdentifier();
|
||||
|
||||
// Check if @Bean specifies a name (e.g., @Bean("myBean"))
|
||||
String explicitName = extractBeanName(methodBinding);
|
||||
if (explicitName != null && !explicitName.isEmpty()) {
|
||||
bean.getBeanNames().add(explicitName);
|
||||
} else {
|
||||
bean.getBeanNames().add(defaultName);
|
||||
}
|
||||
|
||||
registry.addBean(bean);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isSpringComponent(ITypeBinding binding) {
|
||||
return hasMetaAnnotation(binding, "org.springframework.stereotype.Component") ||
|
||||
hasMetaAnnotation(binding, "org.springframework.web.bind.annotation.RestController");
|
||||
}
|
||||
|
||||
private boolean hasMetaAnnotation(ITypeBinding binding, String targetFqn) {
|
||||
return checkMetaAnnotation(binding.getAnnotations(), targetFqn, new HashSet<>());
|
||||
}
|
||||
|
||||
private boolean checkMetaAnnotation(IAnnotationBinding[] annotations, String targetFqn, Set<String> visited) {
|
||||
for (IAnnotationBinding ann : annotations) {
|
||||
ITypeBinding annType = ann.getAnnotationType();
|
||||
if (annType != null) {
|
||||
String fqn = annType.getQualifiedName();
|
||||
if (fqn.equals(targetFqn)) return true;
|
||||
|
||||
// Avoid circular meta-annotations (e.g. Documented -> Documented)
|
||||
if (visited.add(fqn)) {
|
||||
if (checkMetaAnnotation(annType.getAnnotations(), targetFqn, visited)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasAnnotation(IBinding binding, String annotationFqn) {
|
||||
IAnnotationBinding[] annotations = binding.getAnnotations();
|
||||
for (IAnnotationBinding ann : annotations) {
|
||||
if (ann.getAnnotationType() != null && annotationFqn.equals(ann.getAnnotationType().getQualifiedName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Set<String> getAssignableTypes(ITypeBinding binding) {
|
||||
Set<String> types = new HashSet<>();
|
||||
collectAssignableTypes(binding, types);
|
||||
return types;
|
||||
}
|
||||
|
||||
private void collectAssignableTypes(ITypeBinding binding, Set<String> types) {
|
||||
if (binding == null) return;
|
||||
|
||||
String qName = binding.getQualifiedName();
|
||||
if (qName != null && !qName.isEmpty()) {
|
||||
types.add(qName);
|
||||
}
|
||||
|
||||
ITypeBinding erasure = binding.getErasure();
|
||||
if (erasure != null) {
|
||||
String erasureName = erasure.getQualifiedName();
|
||||
if (erasureName != null && !erasureName.isEmpty()) {
|
||||
types.add(erasureName);
|
||||
}
|
||||
}
|
||||
|
||||
collectAssignableTypes(binding.getSuperclass(), types);
|
||||
if (binding.getInterfaces() != null) {
|
||||
for (ITypeBinding iface : binding.getInterfaces()) {
|
||||
collectAssignableTypes(iface, types);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> extractQualifiers(IBinding binding) {
|
||||
Set<String> qualifiers = new HashSet<>();
|
||||
for (IAnnotationBinding ann : binding.getAnnotations()) {
|
||||
if (ann.getAnnotationType() != null && "org.springframework.beans.factory.annotation.Qualifier".equals(ann.getAnnotationType().getQualifiedName())) {
|
||||
for (IMemberValuePairBinding pair : ann.getDeclaredMemberValuePairs()) {
|
||||
if ("value".equals(pair.getName()) && pair.getValue() instanceof String) {
|
||||
qualifiers.add((String) pair.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return qualifiers;
|
||||
}
|
||||
|
||||
private Integer extractOrder(IBinding binding) {
|
||||
for (IAnnotationBinding ann : binding.getAnnotations()) {
|
||||
if (ann.getAnnotationType() != null && "org.springframework.core.annotation.Order".equals(ann.getAnnotationType().getQualifiedName())) {
|
||||
for (IMemberValuePairBinding pair : ann.getDeclaredMemberValuePairs()) {
|
||||
if ("value".equals(pair.getName())) {
|
||||
Object val = pair.getValue();
|
||||
if (val instanceof Integer) {
|
||||
return (Integer) val;
|
||||
} else if (val instanceof IVariableBinding) {
|
||||
Object constantValue = ((IVariableBinding) val).getConstantValue();
|
||||
if (constantValue instanceof Integer) {
|
||||
return (Integer) constantValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Integer.MAX_VALUE; // Spring's default for @Order()
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String extractStereotypeValue(ITypeBinding binding) {
|
||||
for (IAnnotationBinding ann : binding.getAnnotations()) {
|
||||
// Check if annotation itself is meta-annotated with @Component
|
||||
if (checkMetaAnnotation(new IAnnotationBinding[]{ann}, "org.springframework.stereotype.Component", new HashSet<>()) ||
|
||||
"org.springframework.stereotype.Component".equals(ann.getAnnotationType().getQualifiedName())) {
|
||||
for (IMemberValuePairBinding pair : ann.getDeclaredMemberValuePairs()) {
|
||||
if ("value".equals(pair.getName()) && pair.getValue() instanceof String) {
|
||||
return (String) pair.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String extractBeanName(IMethodBinding binding) {
|
||||
for (IAnnotationBinding ann : binding.getAnnotations()) {
|
||||
if ("org.springframework.context.annotation.Bean".equals(ann.getAnnotationType().getQualifiedName())) {
|
||||
for (IMemberValuePairBinding pair : ann.getDeclaredMemberValuePairs()) {
|
||||
if (("value".equals(pair.getName()) || "name".equals(pair.getName())) && pair.getValue() instanceof String) {
|
||||
return (String) pair.getValue();
|
||||
} else if (("value".equals(pair.getName()) || "name".equals(pair.getName())) && pair.getValue() instanceof Object[]) {
|
||||
Object[] vals = (Object[]) pair.getValue();
|
||||
if (vals.length > 0 && vals[0] instanceof String) {
|
||||
return (String) vals[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package click.kamil.springstatemachineexporter.analysis.spring;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SpringDependencyResolver {
|
||||
private final SpringBeanRegistry registry;
|
||||
|
||||
public SpringDependencyResolver(SpringBeanRegistry registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the exact SpringBean that would be injected for a given injection point.
|
||||
*
|
||||
* @param requiredTypeFqn The exact FQN of the type requested at the injection point.
|
||||
* @param qualifier The value of the @Qualifier annotation on the injection point (if any).
|
||||
* @param injectionName The name of the field or parameter being injected (used as fallback).
|
||||
* @return A list of resolved beans. Usually 1. If 0, unresolved. If > 1, ambiguous.
|
||||
*/
|
||||
public List<SpringBean> resolve(String requiredTypeFqn, String qualifier, String injectionName) {
|
||||
if (requiredTypeFqn == null) return new ArrayList<>();
|
||||
|
||||
// 1. Filter by Type (Exact FQN or Assignable Type)
|
||||
System.out.println("RESOLVING " + requiredTypeFqn + " qual=" + qualifier + " injectionName=" + injectionName);
|
||||
List<SpringBean> candidates = registry.getBeans().stream()
|
||||
.filter(bean -> requiredTypeFqn.equals(bean.getTypeFqn()) || bean.getAssignableTypes().contains(requiredTypeFqn))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
System.out.println("CANDIDATES AFTER TYPE: " + candidates.stream().map(c -> c.getTypeFqn() + " (primary=" + c.isPrimary() + " names=" + c.getBeanNames() + " qual=" + c.getQualifiers() + ")").collect(Collectors.toList()));
|
||||
if (candidates.isEmpty() || candidates.size() == 1) {
|
||||
System.out.println("RETURNING: " + candidates.size());
|
||||
return candidates;
|
||||
}
|
||||
|
||||
// 2. Filter by Qualifier (if provided)
|
||||
if (qualifier != null && !qualifier.isEmpty()) {
|
||||
List<SpringBean> qualifiedCandidates = candidates.stream()
|
||||
.filter(bean -> bean.getQualifiers().contains(qualifier) || bean.getBeanNames().contains(qualifier))
|
||||
.collect(Collectors.toList());
|
||||
if (qualifiedCandidates.size() == 1) {
|
||||
return qualifiedCandidates;
|
||||
} else if (!qualifiedCandidates.isEmpty()) {
|
||||
candidates = qualifiedCandidates;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Filter by @Primary
|
||||
List<SpringBean> primaryCandidates = candidates.stream()
|
||||
.filter(SpringBean::isPrimary)
|
||||
.collect(Collectors.toList());
|
||||
if (primaryCandidates.size() == 1) {
|
||||
return primaryCandidates;
|
||||
}
|
||||
|
||||
// 4. Fallback to Injection Name (field name or parameter name)
|
||||
if (injectionName != null && !injectionName.isEmpty()) {
|
||||
List<SpringBean> namedCandidates = candidates.stream()
|
||||
.filter(bean -> bean.getBeanNames().contains(injectionName))
|
||||
.collect(Collectors.toList());
|
||||
if (namedCandidates.size() == 1) {
|
||||
return namedCandidates;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Fallback to @Order (lowest order wins)
|
||||
Integer minOrder = candidates.stream()
|
||||
.map(SpringBean::getOrder)
|
||||
.filter(o -> o != null)
|
||||
.min(Integer::compareTo)
|
||||
.orElse(null);
|
||||
|
||||
if (minOrder != null) {
|
||||
List<SpringBean> orderedCandidates = candidates.stream()
|
||||
.filter(bean -> bean.getOrder() != null && bean.getOrder().equals(minOrder))
|
||||
.collect(Collectors.toList());
|
||||
if (!orderedCandidates.isEmpty()) {
|
||||
candidates = orderedCandidates;
|
||||
}
|
||||
}
|
||||
|
||||
// Return whatever candidates remain (could be ambiguous)
|
||||
System.out.println("RETURNING: " + candidates.size());
|
||||
return candidates;
|
||||
}
|
||||
}
|
||||
@@ -292,6 +292,15 @@ public class AstTransitionParser {
|
||||
// Try same file
|
||||
ASTNode declNode = cu.findDeclaringNode(binding.getKey());
|
||||
if (declNode instanceof MethodDeclaration md) {
|
||||
if (md.getBody() != null && md.getBody().statements().size() == 1) {
|
||||
Object stmt = md.getBody().statements().get(0);
|
||||
if (stmt instanceof org.eclipse.jdt.core.dom.ReturnStatement rs) {
|
||||
Expression retExpr = rs.getExpression();
|
||||
if (isLambdaOrAnonymous(retExpr)) {
|
||||
return retExpr.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
return md.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -238,6 +238,7 @@ public class CodebaseContext {
|
||||
public List<String> getImplementations(String interfaceName) {
|
||||
Set<String> allImpls = new HashSet<>();
|
||||
collectImplementations(interfaceName, allImpls, new HashSet<>());
|
||||
System.out.println("GET IMPLEMENTATIONS FOR: " + interfaceName + " -> " + allImpls);
|
||||
return new ArrayList<>(allImpls);
|
||||
}
|
||||
|
||||
|
||||
@@ -143,6 +143,12 @@ public class RegressionTest {
|
||||
root.resolve("state_machines/complex_multi_module_sm"),
|
||||
Path.of("src/test/resources/golden/StateMachineConfig"),
|
||||
"StateMachineConfig"
|
||||
),
|
||||
new TestScenario(
|
||||
"Polymorphic Events Sample",
|
||||
root.resolve("state_machines/polymorphic_events_sample"),
|
||||
Path.of("src/test/resources/golden/PolymorphicStateMachineConfiguration"),
|
||||
"PolymorphicStateMachineConfiguration"
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -155,7 +161,7 @@ public class RegressionTest {
|
||||
if (scenario.inputPath() == null) System.out.println("inputPath is NULL");
|
||||
if (tempDir == null) System.out.println("tempDir is NULL");
|
||||
|
||||
exportService.runExporter(scenario.inputPath(), tempDir, List.of("puml", "dot", "scxml", "json"), true, scenario.activeProfiles());
|
||||
exportService.runExporter(scenario.inputPath(), tempDir, List.of("puml", "dot", "scxml", "json"), true, scenario.activeProfiles(), null, null, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn, true);
|
||||
|
||||
// Find the generated directory (it might be named with FQN)
|
||||
List<Path> generatedDirs;
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
package click.kamil.springstatemachineexporter.analysis.service;
|
||||
|
||||
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
||||
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||
import click.kamil.springstatemachineexporter.analysis.spring.InjectionPointAnalyzer;
|
||||
import click.kamil.springstatemachineexporter.analysis.spring.SpringBeanRegistry;
|
||||
import click.kamil.springstatemachineexporter.analysis.spring.SpringContextScanner;
|
||||
import click.kamil.springstatemachineexporter.analysis.spring.SpringDependencyResolver;
|
||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||
import click.kamil.springstatemachineexporter.analysis.service.DynamicClasspathResolver;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class JdtCallGraphEngineIntegrationTest {
|
||||
|
||||
private CodebaseContext context;
|
||||
private SpringBeanRegistry registry;
|
||||
private JdtCallGraphEngine engine;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws IOException {
|
||||
Path projectRoot = Path.of("../state_machines/extended_analysis_sample").toAbsolutePath().normalize();
|
||||
|
||||
context = new CodebaseContext();
|
||||
context.setProjectRoot(projectRoot);
|
||||
context.setSourcepath(List.of(projectRoot.resolve("src/main/java").toString()));
|
||||
|
||||
DynamicClasspathResolver resolver = new DynamicClasspathResolver();
|
||||
List<String> cp = resolver.resolveClasspath(projectRoot);
|
||||
if (!cp.isEmpty()) {
|
||||
context.setClasspath(cp);
|
||||
}
|
||||
context.setResolveBindings(true);
|
||||
|
||||
context.scan(Set.of(projectRoot), Collections.emptySet());
|
||||
|
||||
registry = new SpringBeanRegistry();
|
||||
SpringContextScanner scanner = new SpringContextScanner(registry);
|
||||
|
||||
for (var cu : context.getCompilationUnits()) {
|
||||
cu.accept(scanner);
|
||||
}
|
||||
|
||||
SpringDependencyResolver dependencyResolver = new SpringDependencyResolver(registry);
|
||||
InjectionPointAnalyzer injectionAnalyzer = new InjectionPointAnalyzer(dependencyResolver);
|
||||
|
||||
engine = new JdtCallGraphEngine(context, injectionAnalyzer);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolvePrimaryBeanInjection() {
|
||||
EntryPoint ep = EntryPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.web.QuirkController")
|
||||
.methodName("testPrimary")
|
||||
.build();
|
||||
TriggerPoint tp = TriggerPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.service.PrimaryQuirkService")
|
||||
.methodName("doQuirk")
|
||||
.build();
|
||||
|
||||
List<CallChain> chains = engine.findChains(List.of(ep), List.of(tp));
|
||||
System.out.println("CHAINS FOUND: " + chains.size()); assertThat(chains).hasSize(1);
|
||||
assertThat(chains.get(0).getMethodChain())
|
||||
.containsExactly(
|
||||
"click.kamil.examples.statemachine.extended.web.QuirkController.testPrimary",
|
||||
"click.kamil.examples.statemachine.extended.service.PrimaryQuirkService.doQuirk"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveQualifierBeanInjection() {
|
||||
EntryPoint ep = EntryPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.web.QuirkController")
|
||||
.methodName("testQualifier")
|
||||
.build();
|
||||
TriggerPoint tp = TriggerPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.service.QualifierQuirkService")
|
||||
.methodName("doQuirk")
|
||||
.build();
|
||||
|
||||
List<CallChain> chains = engine.findChains(List.of(ep), List.of(tp));
|
||||
System.out.println("CHAINS FOUND: " + chains.size()); assertThat(chains).hasSize(1);
|
||||
assertThat(chains.get(0).getMethodChain())
|
||||
.containsExactly(
|
||||
"click.kamil.examples.statemachine.extended.web.QuirkController.testQualifier",
|
||||
"click.kamil.examples.statemachine.extended.service.QualifierQuirkService.doQuirk"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveNamedBeanInjection() {
|
||||
EntryPoint ep = EntryPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.web.QuirkController")
|
||||
.methodName("testNamed")
|
||||
.build();
|
||||
TriggerPoint tp = TriggerPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.service.NamedQuirkService")
|
||||
.methodName("doQuirk")
|
||||
.build();
|
||||
|
||||
List<CallChain> chains = engine.findChains(List.of(ep), List.of(tp));
|
||||
System.out.println("CHAINS FOUND: " + chains.size()); assertThat(chains).hasSize(1);
|
||||
assertThat(chains.get(0).getMethodChain())
|
||||
.containsExactly(
|
||||
"click.kamil.examples.statemachine.extended.web.QuirkController.testNamed",
|
||||
"click.kamil.examples.statemachine.extended.service.NamedQuirkService.doQuirk"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveFallbackBeanInjection() {
|
||||
EntryPoint ep = EntryPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.web.QuirkController")
|
||||
.methodName("testFallback")
|
||||
.build();
|
||||
TriggerPoint tp = TriggerPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.service.PrimaryQuirkService")
|
||||
.methodName("doQuirk")
|
||||
.build();
|
||||
|
||||
List<CallChain> chains = engine.findChains(List.of(ep), List.of(tp));
|
||||
System.out.println("CHAINS FOUND: " + chains.size()); assertThat(chains).hasSize(1);
|
||||
assertThat(chains.get(0).getMethodChain())
|
||||
.containsExactly(
|
||||
"click.kamil.examples.statemachine.extended.web.QuirkController.testFallback",
|
||||
"click.kamil.examples.statemachine.extended.service.PrimaryQuirkService.doQuirk"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveBeanParameterInjection() {
|
||||
EntryPoint ep = EntryPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.config.BeanParameterTestConfig")
|
||||
.methodName("myBeanParamTester")
|
||||
.build();
|
||||
TriggerPoint tp = TriggerPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.service.NamedQuirkService")
|
||||
.methodName("doQuirk")
|
||||
.build();
|
||||
|
||||
List<CallChain> chains = engine.findChains(List.of(ep), List.of(tp));
|
||||
System.out.println("CHAINS FOUND: " + chains.size()); assertThat(chains).hasSize(1);
|
||||
assertThat(chains.get(0).getMethodChain())
|
||||
.containsExactly(
|
||||
"click.kamil.examples.statemachine.extended.config.BeanParameterTestConfig.myBeanParamTester",
|
||||
"click.kamil.examples.statemachine.extended.service.NamedQuirkService.doQuirk"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveOrderedBeanInjection() {
|
||||
EntryPoint ep = EntryPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.web.OrderedTestController")
|
||||
.methodName("testOrder")
|
||||
.build();
|
||||
TriggerPoint tp = TriggerPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.service.HighPriorityOrderedService")
|
||||
.methodName("doAction")
|
||||
.build();
|
||||
|
||||
List<CallChain> chains = engine.findChains(List.of(ep), List.of(tp));
|
||||
System.out.println("CHAINS FOUND: " + chains.size()); assertThat(chains).hasSize(1);
|
||||
assertThat(chains.get(0).getMethodChain())
|
||||
.containsExactly(
|
||||
"click.kamil.examples.statemachine.extended.web.OrderedTestController.testOrder",
|
||||
"click.kamil.examples.statemachine.extended.service.HighPriorityOrderedService.doAction"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveFieldInjection() {
|
||||
EntryPoint ep = EntryPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.web.FieldInjectionController")
|
||||
.methodName("testField")
|
||||
.build();
|
||||
TriggerPoint tp = TriggerPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.service.NamedQuirkService")
|
||||
.methodName("doQuirk")
|
||||
.build();
|
||||
|
||||
List<CallChain> chains = engine.findChains(List.of(ep), List.of(tp));
|
||||
System.out.println("CHAINS FOUND: " + chains.size()); assertThat(chains).hasSize(1);
|
||||
assertThat(chains.get(0).getMethodChain()).contains(
|
||||
"click.kamil.examples.statemachine.extended.web.FieldInjectionController.testField",
|
||||
"click.kamil.examples.statemachine.extended.service.NamedQuirkService.doQuirk"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveSetterInjection() {
|
||||
EntryPoint ep = EntryPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.web.SetterInjectionController")
|
||||
.methodName("testSetter")
|
||||
.build();
|
||||
TriggerPoint tp = TriggerPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.service.NamedQuirkService")
|
||||
.methodName("doQuirk")
|
||||
.build();
|
||||
|
||||
List<CallChain> chains = engine.findChains(List.of(ep), List.of(tp));
|
||||
System.out.println("CHAINS FOUND: " + chains.size()); assertThat(chains).hasSize(1);
|
||||
assertThat(chains.get(0).getMethodChain()).contains(
|
||||
"click.kamil.examples.statemachine.extended.web.SetterInjectionController.testSetter",
|
||||
"click.kamil.examples.statemachine.extended.service.NamedQuirkService.doQuirk"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveAbstractInheritedBean() {
|
||||
EntryPoint ep = EntryPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.config.AbstractBaseConfig")
|
||||
.methodName("inheritedBeanTester")
|
||||
.build();
|
||||
TriggerPoint tp = TriggerPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.service.NamedQuirkService")
|
||||
.methodName("doQuirk")
|
||||
.build();
|
||||
|
||||
List<CallChain> chains = engine.findChains(List.of(ep), List.of(tp));
|
||||
System.out.println("CHAINS FOUND: " + chains.size()); assertThat(chains).hasSize(1);
|
||||
assertThat(chains.get(0).getMethodChain()).contains(
|
||||
"click.kamil.examples.statemachine.extended.config.AbstractBaseConfig.inheritedBeanTester",
|
||||
"click.kamil.examples.statemachine.extended.service.NamedQuirkService.doQuirk"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveAmbiguousListFallback() {
|
||||
EntryPoint ep = EntryPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.web.ListInjectionController")
|
||||
.methodName("testList")
|
||||
.build();
|
||||
TriggerPoint tp1 = TriggerPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.service.AmbiguousA")
|
||||
.methodName("doAmbig")
|
||||
.build();
|
||||
TriggerPoint tp2 = TriggerPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.service.AmbiguousB")
|
||||
.methodName("doAmbig")
|
||||
.build();
|
||||
|
||||
List<CallChain> chains1 = engine.findChains(List.of(ep), List.of(tp1));
|
||||
List<CallChain> chains2 = engine.findChains(List.of(ep), List.of(tp2));
|
||||
|
||||
assertThat(chains1).isNotEmpty();
|
||||
assertThat(chains1.get(0).getMethodChain()).contains(
|
||||
"click.kamil.examples.statemachine.extended.web.ListInjectionController.testList",
|
||||
"click.kamil.examples.statemachine.extended.service.AmbiguousA.doAmbig"
|
||||
);
|
||||
|
||||
assertThat(chains2).isNotEmpty();
|
||||
assertThat(chains2.get(0).getMethodChain()).contains(
|
||||
"click.kamil.examples.statemachine.extended.web.ListInjectionController.testList",
|
||||
"click.kamil.examples.statemachine.extended.service.AmbiguousB.doAmbig"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveConcreteReturnType() {
|
||||
EntryPoint ep = EntryPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.web.ConcreteReturnTypeController")
|
||||
.methodName("testConcrete")
|
||||
.build();
|
||||
TriggerPoint tp = TriggerPoint.builder()
|
||||
.className("click.kamil.examples.statemachine.extended.service.QualifierQuirkService")
|
||||
.methodName("doQuirk")
|
||||
.build();
|
||||
|
||||
List<CallChain> chains = engine.findChains(List.of(ep), List.of(tp));
|
||||
System.out.println("CHAINS FOUND: " + chains.size()); assertThat(chains).hasSize(1);
|
||||
assertThat(chains.get(0).getMethodChain()).contains(
|
||||
"click.kamil.examples.statemachine.extended.web.ConcreteReturnTypeController.testConcrete",
|
||||
"click.kamil.examples.statemachine.extended.service.QualifierQuirkService.doQuirk"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package click.kamil.springstatemachineexporter.analysis.spring;
|
||||
|
||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||
import click.kamil.springstatemachineexporter.analysis.service.DynamicClasspathResolver;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class SpringContextScannerIntegrationTest {
|
||||
|
||||
private CodebaseContext context;
|
||||
private SpringBeanRegistry registry;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws IOException {
|
||||
Path projectRoot = Path.of("../state_machines/extended_analysis_sample").toAbsolutePath().normalize();
|
||||
|
||||
context = new CodebaseContext();
|
||||
context.setProjectRoot(projectRoot);
|
||||
context.setSourcepath(List.of(projectRoot.resolve("src/main/java").toString()));
|
||||
|
||||
DynamicClasspathResolver resolver = new DynamicClasspathResolver();
|
||||
List<String> cp = resolver.resolveClasspath(projectRoot);
|
||||
if (!cp.isEmpty()) {
|
||||
context.setClasspath(cp);
|
||||
}
|
||||
context.setResolveBindings(true);
|
||||
|
||||
context.scan(Set.of(projectRoot), Collections.emptySet());
|
||||
|
||||
registry = new SpringBeanRegistry();
|
||||
SpringContextScanner scanner = new SpringContextScanner(registry);
|
||||
|
||||
for (var cu : context.getCompilationUnits()) {
|
||||
cu.accept(scanner);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFindStereotypeBeans() {
|
||||
List<SpringBean> beans = registry.getBeans();
|
||||
|
||||
// PrimaryQuirkService
|
||||
assertThat(beans).anySatisfy(bean -> {
|
||||
assertThat(bean.getTypeFqn()).isEqualTo("click.kamil.examples.statemachine.extended.service.PrimaryQuirkService");
|
||||
assertThat(bean.isPrimary()).isTrue();
|
||||
assertThat(bean.getAssignableTypes()).contains("click.kamil.examples.statemachine.extended.service.QuirkService");
|
||||
assertThat(bean.getBeanNames()).contains("primaryQuirkService");
|
||||
});
|
||||
|
||||
// QualifierQuirkService
|
||||
assertThat(beans).anySatisfy(bean -> {
|
||||
assertThat(bean.getTypeFqn()).isEqualTo("click.kamil.examples.statemachine.extended.service.QualifierQuirkService");
|
||||
assertThat(bean.isPrimary()).isFalse();
|
||||
assertThat(bean.getBeanNames()).contains("qualifierQuirkService");
|
||||
});
|
||||
|
||||
// NamedQuirkService (should have explicit name via @Service("customName"))
|
||||
assertThat(beans).anySatisfy(bean -> {
|
||||
assertThat(bean.getTypeFqn()).isEqualTo("click.kamil.examples.statemachine.extended.service.NamedQuirkService");
|
||||
assertThat(bean.getBeanNames()).contains("customName");
|
||||
});
|
||||
|
||||
// PaymentController (should be detected via @RestController)
|
||||
assertThat(beans).anySatisfy(bean -> {
|
||||
assertThat(bean.getTypeFqn()).isEqualTo("click.kamil.examples.statemachine.extended.web.PaymentController");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFindMethodBeans() {
|
||||
List<SpringBean> beans = registry.getBeans();
|
||||
|
||||
// Beans created via @Bean
|
||||
assertThat(beans).anySatisfy(bean -> {
|
||||
assertThat(bean.getTypeFqn()).isEqualTo("java.lang.String");
|
||||
assertThat(bean.getFactoryMethodName()).isEqualTo("myStringBean");
|
||||
assertThat(bean.getBeanNames()).contains("customMockBean");
|
||||
assertThat(bean.getDeclaringClassFqn()).isEqualTo("click.kamil.examples.statemachine.extended.config.MockBeanConfig");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package click.kamil.springstatemachineexporter.analysis.spring;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class SpringDependencyResolverTest {
|
||||
|
||||
private SpringBeanRegistry registry;
|
||||
private SpringDependencyResolver resolver;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
registry = new SpringBeanRegistry();
|
||||
resolver = new SpringDependencyResolver(registry);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveSingleMatchByType() {
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.PaymentServiceImpl")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.build());
|
||||
|
||||
List<SpringBean> resolved = resolver.resolve("com.example.PaymentService", null, "paymentService");
|
||||
|
||||
assertThat(resolved).hasSize(1);
|
||||
assertThat(resolved.get(0).getTypeFqn()).isEqualTo("com.example.PaymentServiceImpl");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveByPrimaryWhenMultipleImplementationsExist() {
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.NormalService")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.isPrimary(false)
|
||||
.build());
|
||||
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.PrimaryService")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.isPrimary(true)
|
||||
.build());
|
||||
|
||||
List<SpringBean> resolved = resolver.resolve("com.example.PaymentService", null, "paymentService");
|
||||
|
||||
assertThat(resolved).hasSize(1);
|
||||
assertThat(resolved.get(0).getTypeFqn()).isEqualTo("com.example.PrimaryService");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveByQualifierWhenMultipleImplementationsExist() {
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.ServiceA")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.qualifiers(Set.of("serviceA"))
|
||||
.build());
|
||||
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.ServiceB")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.qualifiers(Set.of("serviceB"))
|
||||
.build());
|
||||
|
||||
List<SpringBean> resolved = resolver.resolve("com.example.PaymentService", "serviceB", "paymentService");
|
||||
|
||||
assertThat(resolved).hasSize(1);
|
||||
assertThat(resolved.get(0).getTypeFqn()).isEqualTo("com.example.ServiceB");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveByInjectionNameFallback() {
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.ServiceA")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.beanNames(Set.of("serviceA"))
|
||||
.build());
|
||||
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.ServiceB")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.beanNames(Set.of("specialService"))
|
||||
.build());
|
||||
|
||||
// Qualifier is null, Primary is false. Should match by variable name "specialService"
|
||||
List<SpringBean> resolved = resolver.resolve("com.example.PaymentService", null, "specialService");
|
||||
|
||||
assertThat(resolved).hasSize(1);
|
||||
assertThat(resolved.get(0).getTypeFqn()).isEqualTo("com.example.ServiceB");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveByOrderWhenOtherFiltersTie() {
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.LowestOrderService")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.order(Integer.MAX_VALUE) // Lowest precedence
|
||||
.build());
|
||||
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.HighestOrderService")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.order(Integer.MIN_VALUE) // Highest precedence
|
||||
.build());
|
||||
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.MiddleOrderService")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.order(0)
|
||||
.build());
|
||||
|
||||
List<SpringBean> resolved = resolver.resolve("com.example.PaymentService", null, "someUnknownName");
|
||||
|
||||
assertThat(resolved).hasSize(1);
|
||||
assertThat(resolved.get(0).getTypeFqn()).isEqualTo("com.example.HighestOrderService");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveByOrderWithCustomValues() {
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.ServiceA")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.order(400)
|
||||
.build());
|
||||
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.ServiceB")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.order(100)
|
||||
.build());
|
||||
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.ServiceC")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.order(2147483647 + 400) // This wraps around in Java due to overflow, but let's test a very large valid value
|
||||
.build());
|
||||
|
||||
List<SpringBean> resolved = resolver.resolve("com.example.PaymentService", null, "someUnknownName");
|
||||
|
||||
assertThat(resolved).hasSize(1);
|
||||
assertThat(resolved.get(0).getTypeFqn()).isEqualTo("com.example.ServiceC");
|
||||
}
|
||||
|
||||
@Test
|
||||
void primaryShouldOverrideOrder() {
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.PrimaryButLowestOrder")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.isPrimary(true)
|
||||
.order(Integer.MAX_VALUE)
|
||||
.build());
|
||||
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.NotPrimaryButHighestOrder")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.isPrimary(false)
|
||||
.order(Integer.MIN_VALUE)
|
||||
.build());
|
||||
|
||||
List<SpringBean> resolved = resolver.resolve("com.example.PaymentService", null, "someUnknownName");
|
||||
|
||||
assertThat(resolved).hasSize(1);
|
||||
assertThat(resolved.get(0).getTypeFqn()).isEqualTo("com.example.PrimaryButLowestOrder");
|
||||
}
|
||||
|
||||
@Test
|
||||
void qualifierShouldOverridePrimaryAndOrder() {
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.PrimaryService")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.isPrimary(true)
|
||||
.order(Integer.MIN_VALUE)
|
||||
.qualifiers(Set.of("primary"))
|
||||
.build());
|
||||
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.QualifiedService")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.isPrimary(false)
|
||||
.order(Integer.MAX_VALUE)
|
||||
.qualifiers(Set.of("targetQualifier"))
|
||||
.build());
|
||||
|
||||
List<SpringBean> resolved = resolver.resolve("com.example.PaymentService", "targetQualifier", "someUnknownName");
|
||||
|
||||
assertThat(resolved).hasSize(1);
|
||||
assertThat(resolved.get(0).getTypeFqn()).isEqualTo("com.example.QualifiedService");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnMultipleWhenOrderIsTied() {
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.ServiceA")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.order(10)
|
||||
.build());
|
||||
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.ServiceB")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.order(10)
|
||||
.build());
|
||||
|
||||
registry.addBean(SpringBean.builder()
|
||||
.typeFqn("com.example.ServiceC")
|
||||
.assignableTypes(Set.of("com.example.PaymentService"))
|
||||
.order(20)
|
||||
.build());
|
||||
|
||||
List<SpringBean> resolved = resolver.resolve("com.example.PaymentService", null, "someUnknownName");
|
||||
|
||||
assertThat(resolved).hasSize(2);
|
||||
assertThat(resolved.stream().map(SpringBean::getTypeFqn))
|
||||
.containsExactlyInAnyOrder("com.example.ServiceA", "com.example.ServiceB");
|
||||
}
|
||||
}
|
||||
@@ -261,6 +261,56 @@ class AstTransitionParserTest {
|
||||
.contains("System.out.println(\"Hello\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExtractInternalLogicForSameFileMethodReturningLambda() {
|
||||
String source = """
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
public class TestClass {
|
||||
public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source("S1")
|
||||
.target("S2")
|
||||
.guard(guardVarEquals("test"));
|
||||
}
|
||||
|
||||
protected Guard<String, String> guardVarEquals(String expected) {
|
||||
return context -> {
|
||||
return expected.equals(context.getMessage());
|
||||
};
|
||||
}
|
||||
}
|
||||
""";
|
||||
MethodDeclaration method = createMethodDeclarationWithBindings(source);
|
||||
|
||||
List<Transition> transitions = AstTransitionParser.parseTransitions(method, context);
|
||||
|
||||
assertThat(transitions).hasSize(1);
|
||||
assertThat(transitions.getFirst().getGuard().internalLogic())
|
||||
.contains("return expected.equals(context.getMessage());")
|
||||
.doesNotContain("protected Guard<String, String> guardVarEquals");
|
||||
}
|
||||
|
||||
private MethodDeclaration createMethodDeclarationWithBindings(String source) {
|
||||
ASTParser parser = ASTParser.newParser(AST.getJLSLatest());
|
||||
parser.setKind(ASTParser.K_COMPILATION_UNIT);
|
||||
parser.setResolveBindings(true);
|
||||
parser.setBindingsRecovery(true);
|
||||
parser.setEnvironment(new String[0], new String[0], null, true);
|
||||
parser.setUnitName("TestClass.java");
|
||||
parser.setSource(source.toCharArray());
|
||||
CompilationUnit cu = (CompilationUnit) parser.createAST(null);
|
||||
if (cu.types().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
TypeDeclaration typeDecl = (TypeDeclaration) cu.types().getFirst();
|
||||
MethodDeclaration[] methods = typeDecl.getMethods();
|
||||
if (methods.length > 0) {
|
||||
return methods[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private MethodDeclaration createMethodDeclaration(String source) {
|
||||
ASTParser parser = ASTParser.newParser(AST.getJLSLatest());
|
||||
parser.setKind(ASTParser.K_COMPILATION_UNIT);
|
||||
|
||||
@@ -20,6 +20,36 @@
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 16,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "FALLBACK_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.FallbackQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/FallbackQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "PROFILED_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.ProfiledQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/ProfiledQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 19,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "PRIMARY_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PrimaryQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/PrimaryQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 19,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "EXTERNAL_TRIGGER",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||
@@ -60,6 +90,56 @@
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 29,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "QUALIFIER_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.QualifierQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/QualifierQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "NAMED_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.NamedQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/NamedQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "AUTHORIZE",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
|
||||
"methodName" : "processPayment",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/PaymentServiceImpl.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 22,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "CAPTURE",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
|
||||
"methodName" : "capturePayment",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/PaymentServiceImpl.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 35,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "[LIFECYCLE:RESTORE]",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
|
||||
"methodName" : "capturePayment",
|
||||
"sourceFile" : null,
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 28,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "REACTIVE_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.ReactiveOrderService",
|
||||
@@ -144,6 +224,135 @@
|
||||
"interceptorType" : "Spring MVC Interceptor"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "GET /test-setter",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.SetterInjectionController",
|
||||
"methodName" : "testSetter",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/SetterInjectionController.java",
|
||||
"metadata" : {
|
||||
"path" : "/test-setter",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/quirk/primary",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.QuirkController",
|
||||
"methodName" : "testPrimary",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/QuirkController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/quirk/primary",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/quirk/named",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.QuirkController",
|
||||
"methodName" : "testNamed",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/QuirkController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/quirk/named",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/quirk/qualifier",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.QuirkController",
|
||||
"methodName" : "testQualifier",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/QuirkController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/quirk/qualifier",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/quirk/fallback",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.QuirkController",
|
||||
"methodName" : "testFallback",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/QuirkController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/quirk/fallback",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "GET /test-order",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.OrderedTestController",
|
||||
"methodName" : "testOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/OrderedTestController.java",
|
||||
"metadata" : {
|
||||
"path" : "/test-order",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "GET /test-concrete",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.ConcreteReturnTypeController",
|
||||
"methodName" : "testConcrete",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/ConcreteReturnTypeController.java",
|
||||
"metadata" : {
|
||||
"path" : "/test-concrete",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "GET /test-field",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.FieldInjectionController",
|
||||
"methodName" : "testField",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/FieldInjectionController.java",
|
||||
"metadata" : {
|
||||
"path" : "/test-field",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "GET /test-list",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.ListInjectionController",
|
||||
"methodName" : "testList",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/ListInjectionController.java",
|
||||
"metadata" : {
|
||||
"path" : "/test-list",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "GET /api/base/{id}",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.PaymentController",
|
||||
"methodName" : "processBaseEndpoint",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/PaymentController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/base/{id}",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "id",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/payment/{id}/capture",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.PaymentController",
|
||||
"methodName" : "capturePaymentEndpoint",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/PaymentController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/payment/{id}/capture",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "id",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
} ]
|
||||
} ],
|
||||
"callChains" : [ {
|
||||
"entryPoint" : {
|
||||
@@ -365,6 +574,288 @@
|
||||
"targetState" : "START",
|
||||
"event" : "AUDIT_EVENT"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "GET /test-setter",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.SetterInjectionController",
|
||||
"methodName" : "testSetter",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/SetterInjectionController.java",
|
||||
"metadata" : {
|
||||
"path" : "/test-setter",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.SetterInjectionController.testSetter", "click.kamil.examples.statemachine.extended.service.NamedQuirkService.doQuirk" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "NAMED_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.NamedQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/NamedQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/quirk/primary",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.QuirkController",
|
||||
"methodName" : "testPrimary",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/QuirkController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/quirk/primary",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.QuirkController.testPrimary", "click.kamil.examples.statemachine.extended.service.PrimaryQuirkService.doQuirk" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "PRIMARY_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PrimaryQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/PrimaryQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 19,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/quirk/named",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.QuirkController",
|
||||
"methodName" : "testNamed",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/QuirkController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/quirk/named",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.QuirkController.testNamed", "click.kamil.examples.statemachine.extended.service.NamedQuirkService.doQuirk" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "NAMED_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.NamedQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/NamedQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/quirk/qualifier",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.QuirkController",
|
||||
"methodName" : "testQualifier",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/QuirkController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/quirk/qualifier",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.QuirkController.testQualifier", "click.kamil.examples.statemachine.extended.service.QualifierQuirkService.doQuirk" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "QUALIFIER_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.QualifierQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/QualifierQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/quirk/fallback",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.QuirkController",
|
||||
"methodName" : "testFallback",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/QuirkController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/quirk/fallback",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.QuirkController.testFallback", "click.kamil.examples.statemachine.extended.service.PrimaryQuirkService.doQuirk" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "PRIMARY_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PrimaryQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/PrimaryQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 19,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "GET /test-concrete",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.ConcreteReturnTypeController",
|
||||
"methodName" : "testConcrete",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/ConcreteReturnTypeController.java",
|
||||
"metadata" : {
|
||||
"path" : "/test-concrete",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.ConcreteReturnTypeController.testConcrete", "click.kamil.examples.statemachine.extended.service.QualifierQuirkService.doQuirk" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "QUALIFIER_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.QualifierQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/QualifierQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "GET /test-field",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.FieldInjectionController",
|
||||
"methodName" : "testField",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/FieldInjectionController.java",
|
||||
"metadata" : {
|
||||
"path" : "/test-field",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.FieldInjectionController.testField", "click.kamil.examples.statemachine.extended.service.NamedQuirkService.doQuirk" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "NAMED_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.NamedQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/NamedQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "GET /api/base/{id}",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.PaymentController",
|
||||
"methodName" : "processBaseEndpoint",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/PaymentController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/base/{id}",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "id",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.PaymentController.processBaseEndpoint", "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl.processPayment" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "AUTHORIZE",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
|
||||
"methodName" : "processPayment",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/PaymentServiceImpl.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 22,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/payment/{id}/capture",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.PaymentController",
|
||||
"methodName" : "capturePaymentEndpoint",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/PaymentController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/payment/{id}/capture",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "id",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.PaymentController.capturePaymentEndpoint", "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl.capturePayment" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "CAPTURE",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
|
||||
"methodName" : "capturePayment",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/PaymentServiceImpl.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 35,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : "paymentId",
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/payment/{id}/capture",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.PaymentController",
|
||||
"methodName" : "capturePaymentEndpoint",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/PaymentController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/payment/{id}/capture",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "id",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.PaymentController.capturePaymentEndpoint", "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl.capturePayment" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "[LIFECYCLE:RESTORE]",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
|
||||
"methodName" : "capturePayment",
|
||||
"sourceFile" : null,
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 28,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : "paymentId",
|
||||
"matchedTransitions" : null
|
||||
} ],
|
||||
"properties" : {
|
||||
"default" : {
|
||||
|
||||
@@ -20,6 +20,36 @@
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 16,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "FALLBACK_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.FallbackQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/FallbackQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "PROFILED_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.ProfiledQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/ProfiledQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 19,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "PRIMARY_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PrimaryQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/PrimaryQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 19,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "EXTERNAL_TRIGGER",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||
@@ -60,6 +90,56 @@
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 29,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "QUALIFIER_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.QualifierQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/QualifierQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "NAMED_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.NamedQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/NamedQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "AUTHORIZE",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
|
||||
"methodName" : "processPayment",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/PaymentServiceImpl.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 22,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "CAPTURE",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
|
||||
"methodName" : "capturePayment",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/PaymentServiceImpl.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 35,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "[LIFECYCLE:RESTORE]",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
|
||||
"methodName" : "capturePayment",
|
||||
"sourceFile" : null,
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 28,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "REACTIVE_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.ReactiveOrderService",
|
||||
@@ -144,6 +224,135 @@
|
||||
"interceptorType" : "Spring MVC Interceptor"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "GET /test-setter",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.SetterInjectionController",
|
||||
"methodName" : "testSetter",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/SetterInjectionController.java",
|
||||
"metadata" : {
|
||||
"path" : "/test-setter",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/quirk/primary",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.QuirkController",
|
||||
"methodName" : "testPrimary",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/QuirkController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/quirk/primary",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/quirk/named",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.QuirkController",
|
||||
"methodName" : "testNamed",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/QuirkController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/quirk/named",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/quirk/qualifier",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.QuirkController",
|
||||
"methodName" : "testQualifier",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/QuirkController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/quirk/qualifier",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/quirk/fallback",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.QuirkController",
|
||||
"methodName" : "testFallback",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/QuirkController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/quirk/fallback",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "GET /test-order",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.OrderedTestController",
|
||||
"methodName" : "testOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/OrderedTestController.java",
|
||||
"metadata" : {
|
||||
"path" : "/test-order",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "GET /test-concrete",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.ConcreteReturnTypeController",
|
||||
"methodName" : "testConcrete",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/ConcreteReturnTypeController.java",
|
||||
"metadata" : {
|
||||
"path" : "/test-concrete",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "GET /test-field",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.FieldInjectionController",
|
||||
"methodName" : "testField",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/FieldInjectionController.java",
|
||||
"metadata" : {
|
||||
"path" : "/test-field",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "GET /test-list",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.ListInjectionController",
|
||||
"methodName" : "testList",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/ListInjectionController.java",
|
||||
"metadata" : {
|
||||
"path" : "/test-list",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "GET /api/base/{id}",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.PaymentController",
|
||||
"methodName" : "processBaseEndpoint",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/PaymentController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/base/{id}",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "id",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/payment/{id}/capture",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.PaymentController",
|
||||
"methodName" : "capturePaymentEndpoint",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/PaymentController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/payment/{id}/capture",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "id",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
} ]
|
||||
} ],
|
||||
"callChains" : [ {
|
||||
"entryPoint" : {
|
||||
@@ -365,6 +574,288 @@
|
||||
"targetState" : "START",
|
||||
"event" : "AUDIT_EVENT"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "GET /test-setter",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.SetterInjectionController",
|
||||
"methodName" : "testSetter",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/SetterInjectionController.java",
|
||||
"metadata" : {
|
||||
"path" : "/test-setter",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.SetterInjectionController.testSetter", "click.kamil.examples.statemachine.extended.service.NamedQuirkService.doQuirk" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "NAMED_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.NamedQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/NamedQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/quirk/primary",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.QuirkController",
|
||||
"methodName" : "testPrimary",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/QuirkController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/quirk/primary",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.QuirkController.testPrimary", "click.kamil.examples.statemachine.extended.service.PrimaryQuirkService.doQuirk" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "PRIMARY_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PrimaryQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/PrimaryQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 19,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/quirk/named",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.QuirkController",
|
||||
"methodName" : "testNamed",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/QuirkController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/quirk/named",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.QuirkController.testNamed", "click.kamil.examples.statemachine.extended.service.NamedQuirkService.doQuirk" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "NAMED_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.NamedQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/NamedQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/quirk/qualifier",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.QuirkController",
|
||||
"methodName" : "testQualifier",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/QuirkController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/quirk/qualifier",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.QuirkController.testQualifier", "click.kamil.examples.statemachine.extended.service.QualifierQuirkService.doQuirk" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "QUALIFIER_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.QualifierQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/QualifierQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/quirk/fallback",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.QuirkController",
|
||||
"methodName" : "testFallback",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/QuirkController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/quirk/fallback",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.QuirkController.testFallback", "click.kamil.examples.statemachine.extended.service.PrimaryQuirkService.doQuirk" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "PRIMARY_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PrimaryQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/PrimaryQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 19,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "GET /test-concrete",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.ConcreteReturnTypeController",
|
||||
"methodName" : "testConcrete",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/ConcreteReturnTypeController.java",
|
||||
"metadata" : {
|
||||
"path" : "/test-concrete",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.ConcreteReturnTypeController.testConcrete", "click.kamil.examples.statemachine.extended.service.QualifierQuirkService.doQuirk" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "QUALIFIER_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.QualifierQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/QualifierQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "GET /test-field",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.FieldInjectionController",
|
||||
"methodName" : "testField",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/FieldInjectionController.java",
|
||||
"metadata" : {
|
||||
"path" : "/test-field",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.FieldInjectionController.testField", "click.kamil.examples.statemachine.extended.service.NamedQuirkService.doQuirk" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "NAMED_EVENT",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.NamedQuirkService",
|
||||
"methodName" : "doQuirk",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/NamedQuirkService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "GET /api/base/{id}",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.PaymentController",
|
||||
"methodName" : "processBaseEndpoint",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/PaymentController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/base/{id}",
|
||||
"verb" : "GET"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "id",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.PaymentController.processBaseEndpoint", "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl.processPayment" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "AUTHORIZE",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
|
||||
"methodName" : "processPayment",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/PaymentServiceImpl.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 22,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/payment/{id}/capture",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.PaymentController",
|
||||
"methodName" : "capturePaymentEndpoint",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/PaymentController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/payment/{id}/capture",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "id",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.PaymentController.capturePaymentEndpoint", "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl.capturePayment" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "CAPTURE",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
|
||||
"methodName" : "capturePayment",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/PaymentServiceImpl.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 35,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : "paymentId",
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/payment/{id}/capture",
|
||||
"className" : "click.kamil.examples.statemachine.extended.web.PaymentController",
|
||||
"methodName" : "capturePaymentEndpoint",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/web/PaymentController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/payment/{id}/capture",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "id",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.extended.web.PaymentController.capturePaymentEndpoint", "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl.capturePayment" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "[LIFECYCLE:RESTORE]",
|
||||
"className" : "click.kamil.examples.statemachine.extended.service.PaymentServiceImpl",
|
||||
"methodName" : "capturePayment",
|
||||
"sourceFile" : null,
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 28,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : "paymentId",
|
||||
"matchedTransitions" : null
|
||||
} ],
|
||||
"properties" : {
|
||||
"default" : {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
digraph statemachine {
|
||||
rankdir=LR;
|
||||
node [shape=rounded, style=filled, fillcolor=white, fontname="Arial"];
|
||||
edge [fontname="Arial", fontsize=10];
|
||||
|
||||
_start [shape=circle, label="", fillcolor=black, width=0.1];
|
||||
_start -> SUBMITTED;
|
||||
CANCELED [fillcolor=lightgray];
|
||||
FULFILLED [fillcolor=lightgray];
|
||||
SUBMITTED -> PAID [label="OrderEvents.PAY", style="solid", color="black"];
|
||||
PAID -> FULFILLED [label="OrderEvents.FULFILL", style="solid", color="black"];
|
||||
SUBMITTED -> CANCELED [label="OrderEvents.CANCEL", style="solid", color="black"];
|
||||
PAID -> CANCELED [label="OrderEvents.ABCD", style="solid", color="black"];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,639 @@
|
||||
{
|
||||
"metadata" : {
|
||||
"triggers" : [ {
|
||||
"event" : "event",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||
"methodName" : "processEvent",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "payload",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||
"methodName" : "processPayloadEvent",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : null
|
||||
}, {
|
||||
"event" : "event",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||
"methodName" : "processCustomEvent",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 21,
|
||||
"polymorphicEvents" : null
|
||||
} ],
|
||||
"entryPoints" : [ {
|
||||
"type" : "REST",
|
||||
"name" : "POST /pay",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "pay",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/pay",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /fulfill",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "fulfill",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/fulfill",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /cancel",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "cancel",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/cancel",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /payload-pay",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "payloadPay",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/payload-pay",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /pay-variable",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "payVariable",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/pay-variable",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /pay-cast",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "payCast",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/pay-cast",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /pay-ternary",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "payTernary",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/pay-ternary",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "isPay",
|
||||
"type" : "boolean",
|
||||
"annotations" : [ ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /pay-list",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "payList",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/pay-list",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /pay-builder-static",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "payBuilderStatic",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/pay-builder-static",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /pay-builder-instance",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "payBuilderInstance",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/pay-builder-instance",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /abcd",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "abcd",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/abcd",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /mystery",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "mystery",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/mystery",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
} ],
|
||||
"callChains" : [ {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /pay",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "pay",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/pay",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.pay", "click.kamil.examples.statemachine.polymorphic.OrderService.processEvent" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "new PayEvent()",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||
"methodName" : "processEvent",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : [ "PAY" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "OrderStates.SUBMITTED",
|
||||
"targetState" : "OrderStates.PAID",
|
||||
"event" : "OrderEvents.PAY"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /fulfill",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "fulfill",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/fulfill",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.fulfill", "click.kamil.examples.statemachine.polymorphic.OrderService.processEvent" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "new FulfillEvent()",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||
"methodName" : "processEvent",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : [ "FULFILL" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "OrderStates.PAID",
|
||||
"targetState" : "OrderStates.FULFILLED",
|
||||
"event" : "OrderEvents.FULFILL"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /cancel",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "cancel",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/cancel",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.cancel", "click.kamil.examples.statemachine.polymorphic.OrderService.processEvent" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "new CancelEvent()",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||
"methodName" : "processEvent",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : [ "CANCEL" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "OrderStates.SUBMITTED",
|
||||
"targetState" : "OrderStates.CANCELED",
|
||||
"event" : "OrderEvents.CANCEL"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /payload-pay",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "payloadPay",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/payload-pay",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.payloadPay", "click.kamil.examples.statemachine.polymorphic.OrderService.processPayloadEvent" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "new PayEvent()",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||
"methodName" : "processPayloadEvent",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : [ "PAY" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "OrderStates.SUBMITTED",
|
||||
"targetState" : "OrderStates.PAID",
|
||||
"event" : "OrderEvents.PAY"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /pay-variable",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "payVariable",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/pay-variable",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.payVariable", "click.kamil.examples.statemachine.polymorphic.OrderService.processEvent" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "new PayEvent()",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||
"methodName" : "processEvent",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : [ "PAY" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "OrderStates.SUBMITTED",
|
||||
"targetState" : "OrderStates.PAID",
|
||||
"event" : "OrderEvents.PAY"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /pay-cast",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "payCast",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/pay-cast",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.payCast", "click.kamil.examples.statemachine.polymorphic.OrderService.processEvent" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "(BaseEvent)new PayEvent()",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||
"methodName" : "processEvent",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : [ "PAY" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "OrderStates.SUBMITTED",
|
||||
"targetState" : "OrderStates.PAID",
|
||||
"event" : "OrderEvents.PAY"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /pay-ternary",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "payTernary",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/pay-ternary",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "isPay",
|
||||
"type" : "boolean",
|
||||
"annotations" : [ ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.payTernary", "click.kamil.examples.statemachine.polymorphic.OrderService.processEvent" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "isPay ? new PayEvent() : new CancelEvent()",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||
"methodName" : "processEvent",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : [ ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /pay-list",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "payList",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/pay-list",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.payList", "click.kamil.examples.statemachine.polymorphic.OrderService.processEvent" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "event",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||
"methodName" : "processEvent",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : null
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "OrderStates.SUBMITTED",
|
||||
"targetState" : "OrderStates.PAID",
|
||||
"event" : "OrderEvents.PAY"
|
||||
}, {
|
||||
"sourceState" : "OrderStates.PAID",
|
||||
"targetState" : "OrderStates.FULFILLED",
|
||||
"event" : "OrderEvents.FULFILL"
|
||||
}, {
|
||||
"sourceState" : "OrderStates.SUBMITTED",
|
||||
"targetState" : "OrderStates.CANCELED",
|
||||
"event" : "OrderEvents.CANCEL"
|
||||
}, {
|
||||
"sourceState" : "OrderStates.PAID",
|
||||
"targetState" : "OrderStates.CANCELED",
|
||||
"event" : "OrderEvents.ABCD"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /pay-builder-static",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "payBuilderStatic",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/pay-builder-static",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.payBuilderStatic", "click.kamil.examples.statemachine.polymorphic.OrderService.processEvent" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "EventBuilder.buildEvent()",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||
"methodName" : "processEvent",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : [ "FULFILL" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "OrderStates.PAID",
|
||||
"targetState" : "OrderStates.FULFILLED",
|
||||
"event" : "OrderEvents.FULFILL"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /pay-builder-instance",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "payBuilderInstance",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/pay-builder-instance",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.payBuilderInstance", "click.kamil.examples.statemachine.polymorphic.OrderService.processEvent" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "new EventBuilder().buildInstanceEvent()",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||
"methodName" : "processEvent",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : [ "CANCEL" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "OrderStates.SUBMITTED",
|
||||
"targetState" : "OrderStates.CANCELED",
|
||||
"event" : "OrderEvents.CANCEL"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /abcd",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "abcd",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/abcd",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.abcd", "click.kamil.examples.statemachine.polymorphic.OrderService.processCustomEvent" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "new AbcdEvent()",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||
"methodName" : "processCustomEvent",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 21,
|
||||
"polymorphicEvents" : [ "ABCD" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "OrderStates.PAID",
|
||||
"targetState" : "OrderStates.CANCELED",
|
||||
"event" : "OrderEvents.ABCD"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /mystery",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.PolymorphicController",
|
||||
"methodName" : "mystery",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/PolymorphicController.java",
|
||||
"metadata" : {
|
||||
"path" : "/mystery",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.mystery", "click.kamil.examples.statemachine.polymorphic.OrderService.processCustomEvent" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "new MysteryPayload()",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||
"methodName" : "processCustomEvent",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 21,
|
||||
"polymorphicEvents" : [ "ABCD", "PAY" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "OrderStates.SUBMITTED",
|
||||
"targetState" : "OrderStates.PAID",
|
||||
"event" : "OrderEvents.PAY"
|
||||
}, {
|
||||
"sourceState" : "OrderStates.PAID",
|
||||
"targetState" : "OrderStates.CANCELED",
|
||||
"event" : "OrderEvents.ABCD"
|
||||
} ]
|
||||
} ],
|
||||
"properties" : {
|
||||
"default" : {
|
||||
"spring.application.name" : "statemachinedemo"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name" : "click.kamil.examples.statemachine.polymorphic.PolymorphicStateMachineConfiguration",
|
||||
"renderChoicesAsDiamonds" : true,
|
||||
"startStates" : [ "OrderStates.SUBMITTED" ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "OrderStates.SUBMITTED",
|
||||
"fullIdentifier" : "OrderStates.SUBMITTED"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "OrderStates.PAID",
|
||||
"fullIdentifier" : "OrderStates.PAID"
|
||||
} ],
|
||||
"event" : {
|
||||
"rawName" : "OrderEvents.PAY",
|
||||
"fullIdentifier" : "OrderEvents.PAY"
|
||||
},
|
||||
"guard" : null,
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
}, {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "OrderStates.PAID",
|
||||
"fullIdentifier" : "OrderStates.PAID"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "OrderStates.FULFILLED",
|
||||
"fullIdentifier" : "OrderStates.FULFILLED"
|
||||
} ],
|
||||
"event" : {
|
||||
"rawName" : "OrderEvents.FULFILL",
|
||||
"fullIdentifier" : "OrderEvents.FULFILL"
|
||||
},
|
||||
"guard" : null,
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
}, {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "OrderStates.SUBMITTED",
|
||||
"fullIdentifier" : "OrderStates.SUBMITTED"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "OrderStates.CANCELED",
|
||||
"fullIdentifier" : "OrderStates.CANCELED"
|
||||
} ],
|
||||
"event" : {
|
||||
"rawName" : "OrderEvents.CANCEL",
|
||||
"fullIdentifier" : "OrderEvents.CANCEL"
|
||||
},
|
||||
"guard" : null,
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
}, {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "OrderStates.PAID",
|
||||
"fullIdentifier" : "OrderStates.PAID"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "OrderStates.CANCELED",
|
||||
"fullIdentifier" : "OrderStates.CANCELED"
|
||||
} ],
|
||||
"event" : {
|
||||
"rawName" : "OrderEvents.ABCD",
|
||||
"fullIdentifier" : "OrderEvents.ABCD"
|
||||
},
|
||||
"guard" : null,
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
} ],
|
||||
"endStates" : [ "OrderStates.CANCELED", "OrderStates.FULFILLED" ]
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
@startuml
|
||||
!pragma layout smetana
|
||||
set separator none
|
||||
hide empty description
|
||||
hide stereotype
|
||||
skinparam state {
|
||||
BackgroundColor white
|
||||
BorderColor #94a3b8
|
||||
BorderThickness 1
|
||||
FontName Inter
|
||||
FontSize 9
|
||||
FontStyle bold
|
||||
RoundCorner 20
|
||||
Padding 1
|
||||
}
|
||||
skinparam shadowing false
|
||||
skinparam ArrowFontName JetBrains Mono
|
||||
skinparam ArrowFontSize 8
|
||||
skinparam ArrowColor #cbd5e1
|
||||
skinparam ArrowThickness 1
|
||||
skinparam dpi 110
|
||||
skinparam svgLinkTarget _self
|
||||
|
||||
[*] --> OrderStates.SUBMITTED
|
||||
|
||||
|
||||
OrderStates.SUBMITTED -[#1E90FF,bold]-> OrderStates.PAID <<external>> : OrderEvents.PAY
|
||||
OrderStates.PAID -[#1E90FF,bold]-> OrderStates.FULFILLED <<external>> : OrderEvents.FULFILL
|
||||
OrderStates.SUBMITTED -[#1E90FF,bold]-> OrderStates.CANCELED <<external>> : OrderEvents.CANCEL
|
||||
OrderStates.PAID -[#1E90FF,bold]-> OrderStates.CANCELED <<external>> : OrderEvents.ABCD
|
||||
|
||||
OrderStates.CANCELED --> [*]
|
||||
OrderStates.FULFILLED --> [*]
|
||||
@enduml
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="SUBMITTED">
|
||||
<state id="SUBMITTED">
|
||||
<transition target="PAID" event="OrderEvents.PAY"/>
|
||||
<transition target="CANCELED" event="OrderEvents.CANCEL"/>
|
||||
</state>
|
||||
<state id="PAID">
|
||||
<transition target="FULFILLED" event="OrderEvents.FULFILL"/>
|
||||
<transition target="CANCELED" event="OrderEvents.ABCD"/>
|
||||
</state>
|
||||
<state id="FULFILLED">
|
||||
</state>
|
||||
<state id="CANCELED">
|
||||
</state>
|
||||
</scxml>
|
||||
|
||||
@@ -1,6 +1,37 @@
|
||||
HELP.md
|
||||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
.gradle/
|
||||
.idea/
|
||||
*.class
|
||||
*.log
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package click.kamil.examples.statemachine.extended.config;
|
||||
|
||||
import click.kamil.examples.statemachine.extended.service.QuirkService;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
public abstract class AbstractBaseConfig {
|
||||
|
||||
@Bean
|
||||
public String inheritedBeanTester(@Qualifier("customName") QuirkService someService) {
|
||||
someService.doQuirk();
|
||||
return "inherited";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package click.kamil.examples.statemachine.extended.config;
|
||||
|
||||
import click.kamil.examples.statemachine.extended.service.QuirkService;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class BeanParameterTestConfig extends AbstractBaseConfig {
|
||||
|
||||
@Bean
|
||||
public String myBeanParamTester(@Qualifier("customName") QuirkService someService) {
|
||||
// We will use this method as an entry point in the test
|
||||
someService.doQuirk();
|
||||
return "tested";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package click.kamil.examples.statemachine.extended.config;
|
||||
|
||||
import click.kamil.examples.statemachine.extended.service.QuirkService;
|
||||
import click.kamil.examples.statemachine.extended.service.QualifierQuirkService;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class ConcreteReturnTypeConfig {
|
||||
|
||||
@Bean
|
||||
public QuirkService hiddenConcreteService() {
|
||||
return new QualifierQuirkService(); // It returns an interface, but the concrete type is QualifierQuirkService
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package click.kamil.examples.statemachine.extended.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class MockBeanConfig {
|
||||
|
||||
@Bean(name = {"customMockBean", "aliasMockBean"})
|
||||
public String myStringBean() {
|
||||
return "Hello World";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package click.kamil.examples.statemachine.extended.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine(name = "paymentStateMachine")
|
||||
public class PaymentStateMachineConfig extends StateMachineConfigurerAdapter<String, String> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<String, String> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial("NEW")
|
||||
.state("AUTHORIZED")
|
||||
.state("CAPTURED")
|
||||
.state("DECLINED")
|
||||
.state("QUIRK1")
|
||||
.state("QUIRK2")
|
||||
.state("QUIRK3")
|
||||
.state("QUIRK4");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source("NEW").target("AUTHORIZED")
|
||||
.event("AUTHORIZE")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("AUTHORIZED").target("CAPTURED")
|
||||
.event("CAPTURE")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("NEW").target("DECLINED")
|
||||
.event("DECLINE")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("NEW").target("QUIRK1")
|
||||
.event("PRIMARY_EVENT")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("NEW").target("QUIRK2")
|
||||
.event("NAMED_EVENT")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("NEW").target("QUIRK3")
|
||||
.event("QUALIFIER_EVENT")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("NEW").target("QUIRK4")
|
||||
.event("FALLBACK_EVENT");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package click.kamil.examples.statemachine.extended.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class AmbiguousA implements AmbiguousService {
|
||||
@Override
|
||||
public void doAmbig() {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package click.kamil.examples.statemachine.extended.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class AmbiguousB implements AmbiguousService {
|
||||
@Override
|
||||
public void doAmbig() {}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package click.kamil.examples.statemachine.extended.service;
|
||||
|
||||
public interface AmbiguousService {
|
||||
void doAmbig();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package click.kamil.examples.statemachine.extended.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class FallbackQuirkService implements QuirkService {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("paymentStateMachine")
|
||||
private StateMachine<String, String> stateMachine;
|
||||
|
||||
@Override
|
||||
public void doQuirk() {
|
||||
stateMachine.sendEvent("FALLBACK_EVENT");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package click.kamil.examples.statemachine.extended.service;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public class HighPriorityOrderedService implements OrderedService {
|
||||
@Override
|
||||
public void doAction() {}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package click.kamil.examples.statemachine.extended.service;
|
||||
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@Order(10)
|
||||
public class LowPriorityOrderedService implements OrderedService {
|
||||
@Override
|
||||
public void doAction() {}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package click.kamil.examples.statemachine.extended.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service("customName")
|
||||
public class NamedQuirkService implements QuirkService {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("paymentStateMachine")
|
||||
private StateMachine<String, String> stateMachine;
|
||||
|
||||
@Override
|
||||
public void doQuirk() {
|
||||
stateMachine.sendEvent("NAMED_EVENT");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package click.kamil.examples.statemachine.extended.service;
|
||||
|
||||
public interface OrderedService {
|
||||
void doAction();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package click.kamil.examples.statemachine.extended.service;
|
||||
|
||||
public interface PaymentService {
|
||||
void processPayment(String paymentId);
|
||||
void capturePayment(String paymentId);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package click.kamil.examples.statemachine.extended.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.persist.StateMachinePersister;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class PaymentServiceImpl implements PaymentService {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("paymentStateMachine")
|
||||
private StateMachine<String, String> stateMachine;
|
||||
|
||||
@Autowired
|
||||
private StateMachinePersister<String, String, String> persister;
|
||||
|
||||
@Override
|
||||
public void processPayment(String paymentId) {
|
||||
// Send a trigger mapped strictly to PaymentStateMachineConfig
|
||||
stateMachine.sendEvent("AUTHORIZE");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void capturePayment(String paymentId) {
|
||||
try {
|
||||
persister.restore(stateMachine, paymentId);
|
||||
} catch (Exception e) {}
|
||||
|
||||
org.springframework.messaging.Message<String> msg = org.springframework.messaging.support.MessageBuilder
|
||||
.withPayload("CAPTURE")
|
||||
.setHeader("paymentId", paymentId)
|
||||
.build();
|
||||
stateMachine.sendEvent(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package click.kamil.examples.statemachine.extended.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@Primary
|
||||
public class PrimaryQuirkService implements QuirkService {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("paymentStateMachine")
|
||||
private StateMachine<String, String> stateMachine;
|
||||
|
||||
@Override
|
||||
public void doQuirk() {
|
||||
stateMachine.sendEvent("PRIMARY_EVENT");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package click.kamil.examples.statemachine.extended.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@Profile("nonexistent")
|
||||
public class ProfiledQuirkService implements QuirkService {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("paymentStateMachine")
|
||||
private StateMachine<String, String> stateMachine;
|
||||
|
||||
@Override
|
||||
public void doQuirk() {
|
||||
stateMachine.sendEvent("PROFILED_EVENT");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package click.kamil.examples.statemachine.extended.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class QualifierQuirkService implements QuirkService {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("paymentStateMachine")
|
||||
private StateMachine<String, String> stateMachine;
|
||||
|
||||
@Override
|
||||
public void doQuirk() {
|
||||
stateMachine.sendEvent("QUALIFIER_EVENT");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package click.kamil.examples.statemachine.extended.service;
|
||||
|
||||
public interface QuirkService {
|
||||
void doQuirk();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package click.kamil.examples.statemachine.extended.web;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
|
||||
public interface BaseController {
|
||||
|
||||
@GetMapping("/api/base/{id}")
|
||||
String processBaseEndpoint(@PathVariable String id);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package click.kamil.examples.statemachine.extended.web;
|
||||
|
||||
import click.kamil.examples.statemachine.extended.service.QuirkService;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class ConcreteReturnTypeController {
|
||||
|
||||
private final QuirkService hiddenConcreteService;
|
||||
|
||||
public ConcreteReturnTypeController(@Qualifier("hiddenConcreteService") QuirkService hiddenConcreteService) {
|
||||
this.hiddenConcreteService = hiddenConcreteService;
|
||||
}
|
||||
|
||||
@GetMapping("/test-concrete")
|
||||
public void testConcrete() {
|
||||
hiddenConcreteService.doQuirk();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package click.kamil.examples.statemachine.extended.web;
|
||||
|
||||
import click.kamil.examples.statemachine.extended.service.QuirkService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class FieldInjectionController {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("customName")
|
||||
private QuirkService myFieldService;
|
||||
|
||||
@GetMapping("/test-field")
|
||||
public void testField() {
|
||||
myFieldService.doQuirk();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package click.kamil.examples.statemachine.extended.web;
|
||||
|
||||
import click.kamil.examples.statemachine.extended.service.AmbiguousService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
public class ListInjectionController {
|
||||
|
||||
private final List<AmbiguousService> services;
|
||||
|
||||
public ListInjectionController(List<AmbiguousService> services) {
|
||||
this.services = services;
|
||||
}
|
||||
|
||||
@GetMapping("/test-list")
|
||||
public void testList() {
|
||||
for (AmbiguousService s : services) {
|
||||
s.doAmbig();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package click.kamil.examples.statemachine.extended.web;
|
||||
|
||||
import click.kamil.examples.statemachine.extended.service.OrderedService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class OrderedTestController {
|
||||
|
||||
private final OrderedService service;
|
||||
|
||||
// The name "service" does not match HighPriorityOrderedService or LowPriorityOrderedService
|
||||
// So the Fallback by name will fail, and it will fall back to @Order
|
||||
public OrderedTestController(OrderedService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@GetMapping("/test-order")
|
||||
public void testOrder() {
|
||||
service.doAction();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package click.kamil.examples.statemachine.extended.web;
|
||||
|
||||
import click.kamil.examples.statemachine.extended.service.PaymentService;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
|
||||
@RestController
|
||||
public class PaymentController implements BaseController {
|
||||
|
||||
private final PaymentService paymentService;
|
||||
|
||||
public PaymentController(PaymentService paymentService) {
|
||||
this.paymentService = paymentService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String processBaseEndpoint(@PathVariable String id) {
|
||||
paymentService.processPayment(id);
|
||||
return "Started Base Payment: " + id;
|
||||
}
|
||||
|
||||
@PostMapping("/api/payment/{id}/capture")
|
||||
public String capturePaymentEndpoint(@PathVariable String id) {
|
||||
paymentService.capturePayment(id);
|
||||
return "Captured: " + id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package click.kamil.examples.statemachine.extended.web;
|
||||
|
||||
import click.kamil.examples.statemachine.extended.service.QuirkService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class QuirkController {
|
||||
|
||||
@Autowired
|
||||
private QuirkService quirkService; // Should resolve to PrimaryQuirkService
|
||||
|
||||
@Autowired
|
||||
@Qualifier("customName")
|
||||
private QuirkService someService; // Should resolve to NamedQuirkService
|
||||
|
||||
@Autowired
|
||||
@Qualifier("qualifierQuirkService")
|
||||
private QuirkService anotherService; // Should resolve to QualifierQuirkService
|
||||
|
||||
@Autowired
|
||||
private QuirkService fallbackQuirkService; // Should resolve to FallbackQuirkService
|
||||
|
||||
@PostMapping("/api/quirk/primary")
|
||||
public void testPrimary() {
|
||||
quirkService.doQuirk();
|
||||
}
|
||||
|
||||
@PostMapping("/api/quirk/named")
|
||||
public void testNamed() {
|
||||
someService.doQuirk();
|
||||
}
|
||||
|
||||
@PostMapping("/api/quirk/qualifier")
|
||||
public void testQualifier() {
|
||||
anotherService.doQuirk();
|
||||
}
|
||||
|
||||
@PostMapping("/api/quirk/fallback")
|
||||
public void testFallback() {
|
||||
fallbackQuirkService.doQuirk();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package click.kamil.examples.statemachine.extended.web;
|
||||
|
||||
import click.kamil.examples.statemachine.extended.service.QuirkService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class SetterInjectionController {
|
||||
|
||||
private QuirkService setterService;
|
||||
|
||||
@Autowired
|
||||
public void setSetterService(@Qualifier("customName") QuirkService setterService) {
|
||||
this.setterService = setterService;
|
||||
}
|
||||
|
||||
@GetMapping("/test-setter")
|
||||
public void testSetter() {
|
||||
setterService.doQuirk();
|
||||
}
|
||||
}
|
||||
37
state_machines/polymorphic_events_sample/.gitignore
vendored
Normal file
37
state_machines/polymorphic_events_sample/.gitignore
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
HELP.md
|
||||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
45
state_machines/polymorphic_events_sample/build.gradle
Normal file
45
state_machines/polymorphic_events_sample/build.gradle
Normal file
@@ -0,0 +1,45 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '3.5.3'
|
||||
id 'io.spring.dependency-management' version '1.1.7'
|
||||
}
|
||||
|
||||
group = 'click.kamil'
|
||||
version = '0.0.1-SNAPSHOT'
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
configurations {
|
||||
compileOnly {
|
||||
extendsFrom annotationProcessor
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter'
|
||||
implementation 'org.springframework.statemachine:spring-statemachine-starter:3.2.0'
|
||||
|
||||
compileOnly 'org.projectlombok:lombok'
|
||||
annotationProcessor 'org.projectlombok:lombok'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
bootJar { enabled = false }
|
||||
jar { enabled = true }
|
||||
@@ -0,0 +1,8 @@
|
||||
package click.kamil.examples.statemachine.polymorphic;
|
||||
|
||||
public class AbcdEvent implements CustomCodeEventInterface {
|
||||
@Override
|
||||
public OrderEvents resolveEventCode() {
|
||||
return OrderEvents.ABCD;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
package click.kamil.examples.statemachine.polymorphic;
|
||||
public interface BaseEvent { OrderEvents getType(); }
|
||||
@@ -0,0 +1,4 @@
|
||||
package click.kamil.examples.statemachine.polymorphic;
|
||||
public class CancelEvent implements BaseEvent {
|
||||
@Override public OrderEvents getType() { return OrderEvents.CANCEL; }
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package click.kamil.examples.statemachine.polymorphic;
|
||||
|
||||
public interface CustomCodeEventInterface {
|
||||
OrderEvents resolveEventCode();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package click.kamil.examples.statemachine.polymorphic;
|
||||
|
||||
public class EventBuilder {
|
||||
public static BaseEvent buildEvent() {
|
||||
return new FulfillEvent();
|
||||
}
|
||||
|
||||
public BaseEvent buildInstanceEvent() {
|
||||
return new CancelEvent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package click.kamil.examples.statemachine.polymorphic;
|
||||
public class FulfillEvent implements BaseEvent {
|
||||
@Override public OrderEvents getType() { return OrderEvents.FULFILL; }
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package click.kamil.examples.statemachine.polymorphic;
|
||||
|
||||
public class MysteryPayload implements CustomCodeEventInterface {
|
||||
@Override
|
||||
public OrderEvents resolveEventCode() {
|
||||
int a = (int) (Math.random() * 10);
|
||||
if (a > 5) {
|
||||
return OrderEvents.ABCD;
|
||||
} else {
|
||||
return OrderEvents.PAY;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
package click.kamil.examples.statemachine.polymorphic;
|
||||
public enum OrderEvents { FULFILL, PAY, CANCEL, ABCD, NOPE, IGNORE }
|
||||
@@ -0,0 +1,23 @@
|
||||
package click.kamil.examples.statemachine.polymorphic;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
@Service
|
||||
public class OrderService {
|
||||
private final StateMachine<OrderStates, OrderEvents> sm;
|
||||
|
||||
public OrderService(StateMachine<OrderStates, OrderEvents> sm) { this.sm = sm; }
|
||||
|
||||
public void processEvent(BaseEvent event) {
|
||||
sm.sendEvent(event.getType());
|
||||
}
|
||||
|
||||
public void processPayloadEvent(Object payload) {
|
||||
sm.sendEvent(MessageBuilder.withPayload(payload).build());
|
||||
}
|
||||
|
||||
public void processCustomEvent(CustomCodeEventInterface event) {
|
||||
sm.sendEvent(event.resolveEventCode());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
package click.kamil.examples.statemachine.polymorphic;
|
||||
public enum OrderStates { SUBMITTED, PAID, FULFILLED, CANCELED, PAID1, PAID2, PAID3, INVALID, HAPPEN, SKIPPED, NEVER }
|
||||
@@ -0,0 +1,4 @@
|
||||
package click.kamil.examples.statemachine.polymorphic;
|
||||
public class PayEvent implements BaseEvent {
|
||||
@Override public OrderEvents getType() { return OrderEvents.PAY; }
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package click.kamil.examples.statemachine.polymorphic;
|
||||
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class PolymorphicController {
|
||||
|
||||
private final OrderService orderService;
|
||||
|
||||
public PolymorphicController(OrderService orderService) {
|
||||
this.orderService = orderService;
|
||||
}
|
||||
|
||||
@PostMapping("/pay")
|
||||
public void pay() {
|
||||
orderService.processEvent(new PayEvent());
|
||||
}
|
||||
|
||||
@PostMapping("/fulfill")
|
||||
public void fulfill() {
|
||||
orderService.processEvent(new FulfillEvent());
|
||||
}
|
||||
|
||||
@PostMapping("/cancel")
|
||||
public void cancel() {
|
||||
orderService.processEvent(new CancelEvent());
|
||||
}
|
||||
|
||||
@PostMapping("/payload-pay")
|
||||
public void payloadPay() {
|
||||
orderService.processPayloadEvent(new PayEvent());
|
||||
}
|
||||
|
||||
@PostMapping("/pay-variable")
|
||||
public void payVariable() {
|
||||
PayEvent event = new PayEvent();
|
||||
orderService.processEvent(event);
|
||||
}
|
||||
|
||||
@PostMapping("/pay-cast")
|
||||
public void payCast() {
|
||||
orderService.processEvent((BaseEvent) new PayEvent());
|
||||
}
|
||||
|
||||
@PostMapping("/pay-ternary")
|
||||
public void payTernary(boolean isPay) {
|
||||
orderService.processEvent(isPay ? new PayEvent() : new CancelEvent());
|
||||
}
|
||||
|
||||
@PostMapping("/pay-list")
|
||||
public void payList() {
|
||||
java.util.List<BaseEvent> events = java.util.Arrays.asList(new PayEvent(), new FulfillEvent());
|
||||
for (BaseEvent event : events) {
|
||||
orderService.processEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/pay-builder-static")
|
||||
public void payBuilderStatic() {
|
||||
orderService.processEvent(EventBuilder.buildEvent());
|
||||
}
|
||||
|
||||
@PostMapping("/pay-builder-instance")
|
||||
public void payBuilderInstance() {
|
||||
EventBuilder builder = new EventBuilder();
|
||||
orderService.processEvent(builder.buildInstanceEvent());
|
||||
}
|
||||
|
||||
@PostMapping("/abcd")
|
||||
public void abcd() {
|
||||
orderService.processCustomEvent(new AbcdEvent());
|
||||
}
|
||||
|
||||
@PostMapping("/mystery")
|
||||
public void mystery() {
|
||||
orderService.processCustomEvent(new MysteryPayload());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package click.kamil.examples.statemachine.polymorphic;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
public class PolymorphicStateMachineConfiguration extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
|
||||
states.withStates().initial(OrderStates.SUBMITTED).state(OrderStates.PAID).state(OrderStates.FULFILLED).state(OrderStates.CANCELED);
|
||||
}
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal().source(OrderStates.SUBMITTED).target(OrderStates.PAID).event(OrderEvents.PAY).and()
|
||||
.withExternal().source(OrderStates.PAID).target(OrderStates.FULFILLED).event(OrderEvents.FULFILL).and()
|
||||
.withExternal().source(OrderStates.SUBMITTED).target(OrderStates.CANCELED).event(OrderEvents.CANCEL).and()
|
||||
.withExternal().source(OrderStates.PAID).target(OrderStates.CANCELED).event(OrderEvents.ABCD);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package click.kamil.examples.statemachine.polymorphic.app;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
@SpringBootApplication
|
||||
public class StateMachineApplication {
|
||||
public static void main(String[] args) { SpringApplication.run(StateMachineApplication.class, args); }
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
spring.application.name=statemachinedemo
|
||||
Reference in New Issue
Block a user