Compare commits
1 Commits
ai-branch-
...
db58c55b84
| Author | SHA1 | Date | |
|---|---|---|---|
| db58c55b84 |
@@ -14,16 +14,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.enricher.routing.HeuristicBeanResolutionEngine;
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.enricher.routing.BeanResolutionEngine;
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.enricher.matching.EventMatchingEngine;
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.enricher.matching.HeuristicEventMatchingEngine;
|
|
||||||
|
|
||||||
public class TransitionLinkerEnricher implements AnalysisEnricher {
|
public class TransitionLinkerEnricher implements AnalysisEnricher {
|
||||||
|
|
||||||
private final BeanResolutionEngine routingEngine = new HeuristicBeanResolutionEngine();
|
|
||||||
private final EventMatchingEngine matchingEngine = new HeuristicEventMatchingEngine();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enrich(AnalysisResult result, CodebaseContext context, CodebaseIntelligenceProvider intelligence) {
|
public void enrich(AnalysisResult result, CodebaseContext context, CodebaseIntelligenceProvider intelligence) {
|
||||||
if (result.getMetadata() == null || result.getMetadata().getCallChains() == null) {
|
if (result.getMetadata() == null || result.getMetadata().getCallChains() == null) {
|
||||||
@@ -46,10 +38,41 @@ public class TransitionLinkerEnricher implements AnalysisEnricher {
|
|||||||
List<MatchedTransition> matched = new ArrayList<>();
|
List<MatchedTransition> matched = new ArrayList<>();
|
||||||
|
|
||||||
for (Transition t : stateMachineTransitions) {
|
for (Transition t : stateMachineTransitions) {
|
||||||
if (t.getEvent() != null && matchingEngine.matches(t.getEvent(), tp)) {
|
if (t.getEvent() != null) {
|
||||||
String smEventRaw = t.getEvent().fullIdentifier() != null ? t.getEvent().fullIdentifier() : t.getEvent().rawName();
|
String smEventRaw = t.getEvent().fullIdentifier() != null ? t.getEvent().fullIdentifier() : t.getEvent().rawName();
|
||||||
// Event matches
|
String smEvent = simplify(smEventRaw);
|
||||||
for (State smSourceState : t.getSourceStates()) {
|
|
||||||
|
boolean isWildcard = triggerEvent.equals("event") || triggerEvent.equals("e") ||
|
||||||
|
triggerEvent.equals("msg") || triggerEvent.equals("message") ||
|
||||||
|
triggerEvent.equals("payload");
|
||||||
|
|
||||||
|
if (isWildcard) {
|
||||||
|
String targetVar = chain.getContextMachineId();
|
||||||
|
if (targetVar == null && chain.getTriggerPoint() != null) {
|
||||||
|
targetVar = chain.getTriggerPoint().getStateMachineId();
|
||||||
|
}
|
||||||
|
// We no longer hard-block wildcards without a specific routing context.
|
||||||
|
// If a project doesn't use standard SM persisters (e.g. restores state manually),
|
||||||
|
// contextMachineId will be null. We should still link the wildcard to provide SOME visibility,
|
||||||
|
// rather than completely hiding the endpoint.
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> polyEvents = tp.getPolymorphicEvents() != null ? tp.getPolymorphicEvents() : java.util.Collections.emptyList();
|
||||||
|
boolean hasPolyMatch = false;
|
||||||
|
for (String pe : polyEvents) {
|
||||||
|
String simplePe = pe;
|
||||||
|
if (pe.contains(".")) {
|
||||||
|
simplePe = pe.substring(pe.lastIndexOf('.') + 1);
|
||||||
|
}
|
||||||
|
if (simplePe.equals(smEventRaw) || simplePe.equals(smEvent)) {
|
||||||
|
hasPolyMatch = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasPolyMatch || (polyEvents.isEmpty() && (isWildcard || smEvent.equals(triggerEvent)))) {
|
||||||
|
// Event matches or is a wildcard
|
||||||
|
for (State smSourceState : t.getSourceStates()) {
|
||||||
String smSourceRaw = smSourceState.fullIdentifier() != null ? smSourceState.fullIdentifier() : smSourceState.rawName();
|
String smSourceRaw = smSourceState.fullIdentifier() != null ? smSourceState.fullIdentifier() : smSourceState.rawName();
|
||||||
String smSource = simplify(smSourceRaw);
|
String smSource = simplify(smSourceRaw);
|
||||||
if (triggerSource == null || triggerSource.equals(smSource)) {
|
if (triggerSource == null || triggerSource.equals(smSource)) {
|
||||||
@@ -79,6 +102,7 @@ public class TransitionLinkerEnricher implements AnalysisEnricher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!matched.isEmpty()) {
|
if (!matched.isEmpty()) {
|
||||||
CallChain newChain = chain.toBuilder().matchedTransitions(matched).build();
|
CallChain newChain = chain.toBuilder().matchedTransitions(matched).build();
|
||||||
@@ -101,7 +125,155 @@ public class TransitionLinkerEnricher implements AnalysisEnricher {
|
|||||||
|
|
||||||
|
|
||||||
private boolean isRoutedToCorrectMachine(CallChain chain, String currentMachineName) {
|
private boolean isRoutedToCorrectMachine(CallChain chain, String currentMachineName) {
|
||||||
return routingEngine.isRoutedToCorrectMachine(chain, currentMachineName);
|
String beanId = chain.getTriggerPoint() != null ? chain.getTriggerPoint().getStateMachineId() : null;
|
||||||
|
|
||||||
|
if (beanId != null && !beanId.isEmpty() && currentMachineName != null) {
|
||||||
|
// currentMachineName format: FQN (Entry Point) or FQN#beanMethodName
|
||||||
|
if (currentMachineName.equals(beanId) || currentMachineName.endsWith("#" + beanId)) {
|
||||||
|
return true; // Exact match!
|
||||||
|
}
|
||||||
|
|
||||||
|
String simplifiedMachineName = currentMachineName.substring(currentMachineName.lastIndexOf('.') + 1).toLowerCase();
|
||||||
|
String simplifiedBeanId = beanId.substring(beanId.lastIndexOf('.') + 1).toLowerCase();
|
||||||
|
|
||||||
|
// If bean was explicitly identified but doesn't match the current machine, try to reject it
|
||||||
|
if (!simplifiedMachineName.contains(simplifiedBeanId) && !simplifiedBeanId.contains(simplifiedMachineName)) {
|
||||||
|
String beanPrefix = getFirstCamelCaseWord(beanId.substring(beanId.lastIndexOf('.') + 1));
|
||||||
|
String machinePrefix = getFirstCamelCaseWord(currentMachineName.substring(currentMachineName.lastIndexOf('.') + 1));
|
||||||
|
if (beanPrefix != null && machinePrefix != null && !beanPrefix.equalsIgnoreCase(machinePrefix)) {
|
||||||
|
// e.g. PaymentStateMachineFactory vs OrderStateMachineConfig
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic approach combining Prefix, Package, and String-distance heuristics
|
||||||
|
// without relying on a hardcoded list of magic keywords.
|
||||||
|
if (chain.getMethodChain() != null && !chain.getMethodChain().isEmpty() && currentMachineName != null) {
|
||||||
|
String smPackage = getPackageName(currentMachineName);
|
||||||
|
String smSimple = getSimpleClassName(currentMachineName);
|
||||||
|
String smPrefix = getFirstCamelCaseWord(smSimple);
|
||||||
|
|
||||||
|
boolean hasPositiveMatch = false;
|
||||||
|
boolean hasStrongMismatch = false;
|
||||||
|
|
||||||
|
for (String method : chain.getMethodChain()) {
|
||||||
|
String chainClass = getClassNameOnly(method);
|
||||||
|
String chainPackage = getPackageName(chainClass);
|
||||||
|
String chainSimple = getSimpleClassName(chainClass);
|
||||||
|
String chainPrefix = getFirstCamelCaseWord(chainSimple);
|
||||||
|
|
||||||
|
// 1. Explicit Prefix Match (e.g. ElectronicsOrderService & ElectronicsStateMachine)
|
||||||
|
if (smPrefix != null && chainPrefix != null && smPrefix.equals(chainPrefix)) {
|
||||||
|
hasPositiveMatch = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Explicit Sub-Package Match (e.g. com.company.electronics.service & com.company.electronics.sm)
|
||||||
|
// We consider it a match if they share a package segment beyond the first 2 (which are usually com.company)
|
||||||
|
String deepShared = getDeepSharedPackage(smPackage, chainPackage);
|
||||||
|
if (deepShared != null) {
|
||||||
|
hasPositiveMatch = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if there is a strong mismatch.
|
||||||
|
// A strong mismatch happens if the chain class has a VERY distinct prefix/package
|
||||||
|
// that contradicts the SM's distinct prefix/package.
|
||||||
|
// Without magic keywords, we guess if a prefix is distinct if it's long enough and doesn't match.
|
||||||
|
if (smPrefix != null && chainPrefix != null && !smPrefix.equals(chainPrefix)) {
|
||||||
|
// They have different first CamelCase words.
|
||||||
|
// This could be Electronics vs ComputerStore, or it could be Common vs ComputerStore.
|
||||||
|
// If they diverge at a deep package level, it's a strong mismatch.
|
||||||
|
if (isDeepPackageDivergence(smPackage, chainPackage)) {
|
||||||
|
hasStrongMismatch = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we found a positive structural match, route it!
|
||||||
|
if (hasPositiveMatch) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's no positive match, but we detected a strong structural mismatch, reject it!
|
||||||
|
if (hasStrongMismatch) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If neither, we allow it (fallback for common/shared endpoints without distinct domains)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPackageName(String fqn) {
|
||||||
|
if (fqn == null || !fqn.contains(".")) return "";
|
||||||
|
return fqn.substring(0, fqn.lastIndexOf('.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSimpleClassName(String fqn) {
|
||||||
|
if (fqn == null) return "";
|
||||||
|
if (!fqn.contains(".")) return fqn;
|
||||||
|
return fqn.substring(fqn.lastIndexOf('.') + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getClassNameOnly(String methodFqn) {
|
||||||
|
if (methodFqn == null) return "";
|
||||||
|
String clean = methodFqn;
|
||||||
|
if (clean.contains("(")) {
|
||||||
|
clean = clean.substring(0, clean.indexOf('('));
|
||||||
|
}
|
||||||
|
if (clean.contains(".")) {
|
||||||
|
clean = clean.substring(0, clean.lastIndexOf('.'));
|
||||||
|
}
|
||||||
|
return clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFirstCamelCaseWord(String simpleName) {
|
||||||
|
if (simpleName == null || simpleName.isEmpty()) return null;
|
||||||
|
// e.g. "ElectronicsOrderService" -> "Electronics"
|
||||||
|
String[] words = simpleName.split("(?<!^)(?=[A-Z])");
|
||||||
|
if (words.length > 0) {
|
||||||
|
return words[0];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDeepSharedPackage(String pkg1, String pkg2) {
|
||||||
|
if (pkg1 == null || pkg2 == null || pkg1.isEmpty() || pkg2.isEmpty()) return null;
|
||||||
|
String[] p1 = pkg1.split("\\.");
|
||||||
|
String[] p2 = pkg2.split("\\.");
|
||||||
|
|
||||||
|
// Find the deepest common segment index
|
||||||
|
int matchIdx = -1;
|
||||||
|
for (int i = 0; i < Math.min(p1.length, p2.length); i++) {
|
||||||
|
if (p1[i].equals(p2[i])) {
|
||||||
|
matchIdx = i;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If they share at least 3 segments (e.g. com.company.electronics), it's a deep match
|
||||||
|
if (matchIdx >= 2) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for(int i = 0; i <= matchIdx; i++) {
|
||||||
|
sb.append(p1[i]).append(".");
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDeepPackageDivergence(String pkg1, String pkg2) {
|
||||||
|
if (pkg1 == null || pkg2 == null || pkg1.isEmpty() || pkg2.isEmpty()) return false;
|
||||||
|
String[] p1 = pkg1.split("\\.");
|
||||||
|
String[] p2 = pkg2.split("\\.");
|
||||||
|
|
||||||
|
// If they share exactly the root (com.company) but diverge at the 3rd segment (e.g. electronics vs computerstore)
|
||||||
|
// Then it's a divergence.
|
||||||
|
if (p1.length >= 3 && p2.length >= 3) {
|
||||||
|
return p1[0].equals(p2[0]) && p1[1].equals(p2[1]) && !p1[2].equals(p2[2]);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String simplify(String name) {
|
private String simplify(String name) {
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
package click.kamil.springstatemachineexporter.analysis.enricher.matching;
|
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
|
||||||
import click.kamil.springstatemachineexporter.model.Event;
|
|
||||||
|
|
||||||
public interface EventMatchingEngine {
|
|
||||||
/**
|
|
||||||
* Determines whether the given trigger point is a match for the state machine event.
|
|
||||||
*/
|
|
||||||
boolean matches(Event stateMachineEvent, TriggerPoint triggerPoint);
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
package click.kamil.springstatemachineexporter.analysis.enricher.matching;
|
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
|
||||||
import click.kamil.springstatemachineexporter.model.Event;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class HeuristicEventMatchingEngine implements EventMatchingEngine {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(Event stateMachineEvent, TriggerPoint triggerPoint) {
|
|
||||||
if (stateMachineEvent == null || triggerPoint == null || triggerPoint.getEvent() == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String triggerEvent = simplify(triggerPoint.getEvent());
|
|
||||||
String smEventRaw = stateMachineEvent.fullIdentifier() != null ? stateMachineEvent.fullIdentifier() : stateMachineEvent.rawName();
|
|
||||||
String smEvent = simplify(smEventRaw);
|
|
||||||
|
|
||||||
boolean isWildcard = isWildcardVariable(triggerEvent);
|
|
||||||
|
|
||||||
List<String> polyEvents = triggerPoint.getPolymorphicEvents() != null ? triggerPoint.getPolymorphicEvents() : java.util.Collections.emptyList();
|
|
||||||
|
|
||||||
boolean hasPolyMatch = false;
|
|
||||||
for (String pe : polyEvents) {
|
|
||||||
String simplePe = pe;
|
|
||||||
if (pe.contains(".")) {
|
|
||||||
simplePe = pe.substring(pe.lastIndexOf('.') + 1);
|
|
||||||
}
|
|
||||||
if (simplePe.equals(smEventRaw) || simplePe.equals(smEvent)) {
|
|
||||||
hasPolyMatch = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hasPolyMatch || smEvent.equals(triggerEvent) || (polyEvents.isEmpty() && isWildcard);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isWildcardVariable(String eventStr) {
|
|
||||||
return eventStr.equals("event") || eventStr.equals("e") ||
|
|
||||||
eventStr.equals("msg") || eventStr.equals("message") ||
|
|
||||||
eventStr.equals("payload");
|
|
||||||
}
|
|
||||||
|
|
||||||
private String simplify(String name) {
|
|
||||||
if (name == null) return null;
|
|
||||||
String simplified = name.replaceAll("(?i)(.*)(Event|Action|Transition|Command)(s)?$", "$1");
|
|
||||||
if (simplified.isEmpty()) {
|
|
||||||
simplified = name;
|
|
||||||
}
|
|
||||||
simplified = simplified.replaceAll("^.*\\.([A-Z0-9_]+)$", "$1");
|
|
||||||
return simplified;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package click.kamil.springstatemachineexporter.analysis.enricher.routing;
|
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
|
||||||
|
|
||||||
public interface BeanResolutionEngine {
|
|
||||||
boolean isRoutedToCorrectMachine(CallChain chain, String currentMachineName);
|
|
||||||
}
|
|
||||||
@@ -1,188 +0,0 @@
|
|||||||
package click.kamil.springstatemachineexporter.analysis.enricher.routing;
|
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class HeuristicBeanResolutionEngine implements BeanResolutionEngine {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isRoutedToCorrectMachine(CallChain chain, String currentMachineName) {
|
|
||||||
String targetVar = chain.getContextMachineId();
|
|
||||||
if (targetVar == null && chain.getTriggerPoint() != null) {
|
|
||||||
targetVar = chain.getTriggerPoint().getStateMachineId();
|
|
||||||
}
|
|
||||||
|
|
||||||
String simplifiedMachineName = currentMachineName != null ? currentMachineName.substring(currentMachineName.lastIndexOf('.') + 1).toLowerCase() : "";
|
|
||||||
|
|
||||||
if (targetVar != null && !targetVar.isEmpty()) {
|
|
||||||
targetVar = targetVar.toLowerCase();
|
|
||||||
if (targetVar.endsWith("statemachine")) {
|
|
||||||
String prefix = targetVar.substring(0, targetVar.length() - "statemachine".length());
|
|
||||||
if (!prefix.isEmpty()) {
|
|
||||||
if (simplifiedMachineName.contains(prefix)) {
|
|
||||||
return true; // Explicit positive match
|
|
||||||
} else {
|
|
||||||
return false; // Explicit negative match
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (simplifiedMachineName.contains(targetVar)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chain.getMethodChain() != null && !chain.getMethodChain().isEmpty() && currentMachineName != null) {
|
|
||||||
String smPackage = getPackageName(currentMachineName);
|
|
||||||
String smSimple = getSimpleClassName(currentMachineName);
|
|
||||||
String smPrefix = getFirstCamelCaseWord(smSimple);
|
|
||||||
|
|
||||||
boolean hasPositiveMatch = false;
|
|
||||||
boolean hasStrongMismatch = false;
|
|
||||||
|
|
||||||
for (String method : chain.getMethodChain()) {
|
|
||||||
String chainClass = getClassNameOnly(method);
|
|
||||||
String chainPackage = getPackageName(chainClass);
|
|
||||||
String chainSimple = getSimpleClassName(chainClass);
|
|
||||||
String chainPrefix = getFirstCamelCaseWord(chainSimple);
|
|
||||||
|
|
||||||
if (smPrefix != null && chainPrefix != null && smPrefix.equals(chainPrefix)) {
|
|
||||||
hasPositiveMatch = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDomainMatch(smPackage, chainPackage, smPrefix, chainPrefix)) {
|
|
||||||
hasPositiveMatch = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDomainMismatch(smPackage, chainPackage, smPrefix, chainPrefix)) {
|
|
||||||
hasStrongMismatch = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasPositiveMatch) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (hasStrongMismatch) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isDomainMatch(String smPackage, String chainPackage, String smPrefix, String chainPrefix) {
|
|
||||||
if (smPackage == null || chainPackage == null || smPackage.isEmpty() || chainPackage.isEmpty()) return false;
|
|
||||||
|
|
||||||
if (smPackage.startsWith(chainPackage) || chainPackage.startsWith(smPackage)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find deepest common package root
|
|
||||||
String[] p1 = smPackage.split("\\.");
|
|
||||||
String[] p2 = chainPackage.split("\\.");
|
|
||||||
int matchIdx = -1;
|
|
||||||
for (int i = 0; i < Math.min(p1.length, p2.length); i++) {
|
|
||||||
if (p1[i].equals(p2[i])) {
|
|
||||||
matchIdx = i;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the state machine's prefix is part of the shared common package,
|
|
||||||
// then the state machine represents the parent domain of the caller.
|
|
||||||
if (smPrefix != null && matchIdx >= 0) {
|
|
||||||
String lowerPrefix = smPrefix.toLowerCase();
|
|
||||||
for (int i = 0; i <= matchIdx; i++) {
|
|
||||||
if (p1[i].toLowerCase().contains(lowerPrefix) || lowerPrefix.contains(p1[i].toLowerCase())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isDomainMismatch(String smPackage, String chainPackage, String smPrefix, String chainPrefix) {
|
|
||||||
if (smPackage == null || chainPackage == null || smPackage.isEmpty() || chainPackage.isEmpty()) return false;
|
|
||||||
|
|
||||||
String[] p1 = smPackage.split("\\.");
|
|
||||||
String[] p2 = chainPackage.split("\\.");
|
|
||||||
|
|
||||||
int matchIdx = -1;
|
|
||||||
for (int i = 0; i < Math.min(p1.length, p2.length); i++) {
|
|
||||||
if (p1[i].equals(p2[i])) {
|
|
||||||
matchIdx = i;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<String> smDivergentTerms = new HashSet<>();
|
|
||||||
if (smPrefix != null) smDivergentTerms.add(smPrefix.toLowerCase());
|
|
||||||
for (int i = matchIdx + 1; i < p1.length; i++) {
|
|
||||||
smDivergentTerms.add(p1[i].toLowerCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<String> chainDivergentTerms = new HashSet<>();
|
|
||||||
if (chainPrefix != null) chainDivergentTerms.add(chainPrefix.toLowerCase());
|
|
||||||
for (int i = matchIdx + 1; i < p2.length; i++) {
|
|
||||||
chainDivergentTerms.add(p2[i].toLowerCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!smDivergentTerms.isEmpty() && !chainDivergentTerms.isEmpty()) {
|
|
||||||
Set<String> intersection = new HashSet<>(smDivergentTerms);
|
|
||||||
intersection.retainAll(chainDivergentTerms);
|
|
||||||
return intersection.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<String> extractDomainTerms(String pkg, String prefix) {
|
|
||||||
Set<String> terms = new HashSet<>();
|
|
||||||
if (pkg != null && !pkg.isEmpty()) {
|
|
||||||
String[] segments = pkg.split("\\.");
|
|
||||||
// Take segments from index 2 onwards if length > 2
|
|
||||||
int start = segments.length > 2 ? 2 : 0;
|
|
||||||
for (int i = start; i < segments.length; i++) {
|
|
||||||
terms.add(segments[i].toLowerCase());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (prefix != null && !prefix.isEmpty()) {
|
|
||||||
terms.add(prefix.toLowerCase());
|
|
||||||
}
|
|
||||||
return terms;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getPackageName(String fqn) {
|
|
||||||
if (fqn == null || !fqn.contains(".")) return "";
|
|
||||||
return fqn.substring(0, fqn.lastIndexOf('.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getSimpleClassName(String fqn) {
|
|
||||||
if (fqn == null) return "";
|
|
||||||
if (!fqn.contains(".")) return fqn;
|
|
||||||
return fqn.substring(fqn.lastIndexOf('.') + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getClassNameOnly(String methodFqn) {
|
|
||||||
if (methodFqn == null) return "";
|
|
||||||
String clean = methodFqn;
|
|
||||||
if (clean.contains("(")) {
|
|
||||||
clean = clean.substring(0, clean.indexOf('('));
|
|
||||||
}
|
|
||||||
if (clean.contains(".")) {
|
|
||||||
clean = clean.substring(0, clean.lastIndexOf('.'));
|
|
||||||
}
|
|
||||||
return clean;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getFirstCamelCaseWord(String simpleName) {
|
|
||||||
if (simpleName == null || simpleName.isEmpty()) return null;
|
|
||||||
String[] words = simpleName.split("(?<!^)(?=[A-Z])");
|
|
||||||
if (words.length > 0) {
|
|
||||||
return words[0];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package click.kamil.springstatemachineexporter.analysis.model;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class CallEdge {
|
|
||||||
private final String targetMethod;
|
|
||||||
private final List<String> arguments;
|
|
||||||
}
|
|
||||||
@@ -21,4 +21,6 @@ public class TriggerPoint {
|
|||||||
private final String sourceState; // Optional: if we can determine the expected current state
|
private final String sourceState; // Optional: if we can determine the expected current state
|
||||||
private final int lineNumber;
|
private final int lineNumber;
|
||||||
private final java.util.List<String> polymorphicEvents; // NEW: stores concrete events resolved via deep polymorphism
|
private final java.util.List<String> polymorphicEvents; // NEW: stores concrete events resolved via deep polymorphism
|
||||||
|
@Builder.Default
|
||||||
|
private final Map<String, String> payloadTypes = new java.util.HashMap<>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,12 +180,44 @@ public class ConstantResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback: Check constructors for assignments
|
||||||
|
for (MethodDeclaration md : td.getMethods()) {
|
||||||
|
if (md.isConstructor() && md.getBody() != null) {
|
||||||
|
String val = findAssignmentInBlock(md.getBody(), fieldName, context, visited);
|
||||||
|
if (val != null) return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
visited.remove(fieldId);
|
visited.remove(fieldId);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String findAssignmentInBlock(Block block, String fieldName, CodebaseContext context, Set<String> visited) {
|
||||||
|
if (block == null) return null;
|
||||||
|
for (Object stmtObj : block.statements()) {
|
||||||
|
if (stmtObj instanceof ExpressionStatement exprStmt) {
|
||||||
|
if (exprStmt.getExpression() instanceof Assignment assignment) {
|
||||||
|
Expression left = assignment.getLeftHandSide();
|
||||||
|
boolean match = false;
|
||||||
|
if (left instanceof SimpleName sn && sn.getIdentifier().equals(fieldName)) {
|
||||||
|
match = true;
|
||||||
|
} else if (left instanceof FieldAccess fa && fa.getName().getIdentifier().equals(fieldName)) {
|
||||||
|
if (fa.getExpression() instanceof ThisExpression) {
|
||||||
|
match = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
return resolveInternal(assignment.getRightHandSide(), context, visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private String findValueFromAnnotation(FieldDeclaration fd) {
|
private String findValueFromAnnotation(FieldDeclaration fd) {
|
||||||
for (Object mod : fd.modifiers()) {
|
for (Object mod : fd.modifiers()) {
|
||||||
if (mod instanceof Annotation ann) {
|
if (mod instanceof Annotation ann) {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package click.kamil.springstatemachineexporter.analysis.service;
|
package click.kamil.springstatemachineexporter.analysis.service;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.CallEdge;
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
@@ -12,14 +11,26 @@ import org.eclipse.jdt.core.dom.*;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class HeuristicCallGraphEngine implements CallGraphEngine {
|
public class CallGraphBuilder {
|
||||||
private final CodebaseContext context;
|
private final CodebaseContext context;
|
||||||
|
private final click.kamil.springstatemachineexporter.analysis.spring.SpringDependencyResolver springDependencyResolver;
|
||||||
private final ConstantResolver constantResolver;
|
private final ConstantResolver constantResolver;
|
||||||
private String currentMethodFqn;
|
private String currentMethodFqn;
|
||||||
private Map<String, List<CallEdge>> graph;
|
private Map<String, List<CallEdge>> graph;
|
||||||
|
|
||||||
public HeuristicCallGraphEngine(CodebaseContext context) {
|
@lombok.Data
|
||||||
|
private static class CallEdge {
|
||||||
|
private final String targetMethod;
|
||||||
|
private final List<String> arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CallGraphBuilder(CodebaseContext context) {
|
||||||
|
this(context, new click.kamil.springstatemachineexporter.analysis.spring.SpringDependencyResolver(context, new click.kamil.springstatemachineexporter.analysis.spring.SpringBeanRegistry(context)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public CallGraphBuilder(CodebaseContext context, click.kamil.springstatemachineexporter.analysis.spring.SpringDependencyResolver springDependencyResolver) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.springDependencyResolver = springDependencyResolver;
|
||||||
this.constantResolver = new ConstantResolver();
|
this.constantResolver = new ConstantResolver();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +198,16 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
|||||||
typesToInspect.add(declaredType);
|
typesToInspect.add(declaredType);
|
||||||
} else {
|
} else {
|
||||||
typesToInspect.add(declaredType);
|
typesToInspect.add(declaredType);
|
||||||
typesToInspect.addAll(context.getImplementations(declaredType));
|
List<click.kamil.springstatemachineexporter.analysis.spring.SpringBean> resolvedBeans = springDependencyResolver.resolve(declaredType, null, varName);
|
||||||
|
if (resolvedBeans.isEmpty()) {
|
||||||
|
typesToInspect.addAll(context.getImplementations(declaredType));
|
||||||
|
} else {
|
||||||
|
for (click.kamil.springstatemachineexporter.analysis.spring.SpringBean bean : resolvedBeans) {
|
||||||
|
if (!bean.getTypeFqn().equals(declaredType)) {
|
||||||
|
typesToInspect.add(bean.getTypeFqn());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
System.out.println("DEEP TRACE IMPLS: " + typesToInspect);
|
System.out.println("DEEP TRACE IMPLS: " + typesToInspect);
|
||||||
|
|
||||||
@@ -535,9 +555,23 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
|||||||
String calledMethod = fallbackTypeFqn + "." + emr.getName().getIdentifier();
|
String calledMethod = fallbackTypeFqn + "." + emr.getName().getIdentifier();
|
||||||
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(calledMethod, args));
|
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(calledMethod, args));
|
||||||
|
|
||||||
List<String> impls = context.getImplementations(fallbackTypeFqn);
|
String receiverName = null;
|
||||||
for (String impl : impls) {
|
if (emr.getExpression() instanceof SimpleName sn) {
|
||||||
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(impl + "." + emr.getName().getIdentifier(), args));
|
receiverName = sn.getIdentifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<click.kamil.springstatemachineexporter.analysis.spring.SpringBean> resolvedBeans = springDependencyResolver.resolve(fallbackTypeFqn, null, receiverName);
|
||||||
|
if (resolvedBeans.isEmpty()) {
|
||||||
|
List<String> impls = context.getImplementations(fallbackTypeFqn);
|
||||||
|
for (String impl : impls) {
|
||||||
|
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(impl + "." + emr.getName().getIdentifier(), args));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (click.kamil.springstatemachineexporter.analysis.spring.SpringBean bean : resolvedBeans) {
|
||||||
|
if (!bean.getTypeFqn().equals(fallbackTypeFqn)) {
|
||||||
|
graph.computeIfAbsent(currentMethodFqn, k -> new ArrayList<>()).add(new CallEdge(bean.getTypeFqn() + "." + emr.getName().getIdentifier(), args));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -644,21 +678,91 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
|||||||
if (baseCalled == null) return Collections.emptyList();
|
if (baseCalled == null) return Collections.emptyList();
|
||||||
|
|
||||||
List<String> allResolved = new ArrayList<>();
|
List<String> allResolved = new ArrayList<>();
|
||||||
allResolved.add(baseCalled);
|
|
||||||
|
|
||||||
if (baseCalled.contains(".")) {
|
if (baseCalled.contains(".")) {
|
||||||
String className = baseCalled.substring(0, baseCalled.lastIndexOf('.'));
|
String className = baseCalled.substring(0, baseCalled.lastIndexOf('.'));
|
||||||
String methodName = baseCalled.substring(baseCalled.lastIndexOf('.') + 1);
|
String methodName = baseCalled.substring(baseCalled.lastIndexOf('.') + 1);
|
||||||
|
String receiverName = null;
|
||||||
List<String> impls = context.getImplementations(className);
|
if (node.getExpression() instanceof SimpleName sn) {
|
||||||
for (String impl : impls) {
|
receiverName = sn.getIdentifier();
|
||||||
allResolved.add(impl + "." + methodName);
|
} else if (node.getExpression() instanceof FieldAccess fa) {
|
||||||
|
receiverName = fa.getName().getIdentifier();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String qualifier = extractQualifierFromVariable(node, receiverName);
|
||||||
|
|
||||||
|
List<click.kamil.springstatemachineexporter.analysis.spring.SpringBean> resolvedBeans = springDependencyResolver.resolve(className, qualifier, receiverName);
|
||||||
|
|
||||||
|
if (resolvedBeans.isEmpty()) {
|
||||||
|
allResolved.add(baseCalled);
|
||||||
|
List<String> impls = context.getImplementations(className);
|
||||||
|
for (String impl : impls) {
|
||||||
|
allResolved.add(impl + "." + methodName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (click.kamil.springstatemachineexporter.analysis.spring.SpringBean bean : resolvedBeans) {
|
||||||
|
allResolved.add(bean.getTypeFqn() + "." + methodName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
allResolved.add(baseCalled);
|
||||||
}
|
}
|
||||||
|
|
||||||
return allResolved;
|
return allResolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String extractQualifierFromVariable(ASTNode node, String varName) {
|
||||||
|
if (varName == null) return null;
|
||||||
|
|
||||||
|
MethodDeclaration md = findEnclosingMethod(node);
|
||||||
|
if (md != null) {
|
||||||
|
for (Object paramObj : md.parameters()) {
|
||||||
|
if (paramObj instanceof SingleVariableDeclaration svd) {
|
||||||
|
if (svd.getName().getIdentifier().equals(varName)) {
|
||||||
|
return extractQualifierFromModifiers(svd.modifiers());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeDeclaration td = findEnclosingType(node);
|
||||||
|
if (td != null) {
|
||||||
|
for (FieldDeclaration fd : td.getFields()) {
|
||||||
|
for (Object fragObj : fd.fragments()) {
|
||||||
|
if (fragObj instanceof VariableDeclarationFragment vdf) {
|
||||||
|
if (vdf.getName().getIdentifier().equals(varName)) {
|
||||||
|
return extractQualifierFromModifiers(fd.modifiers());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractQualifierFromModifiers(List<?> modifiers) {
|
||||||
|
for (Object modObj : modifiers) {
|
||||||
|
if (modObj instanceof NormalAnnotation na) {
|
||||||
|
if ("Qualifier".equals(na.getTypeName().getFullyQualifiedName()) || "org.springframework.beans.factory.annotation.Qualifier".equals(na.getTypeName().getFullyQualifiedName())) {
|
||||||
|
for (Object valObj : na.values()) {
|
||||||
|
if (valObj instanceof MemberValuePair mvp) {
|
||||||
|
if ("value".equals(mvp.getName().getIdentifier()) && mvp.getValue() instanceof StringLiteral sl) {
|
||||||
|
return sl.getLiteralValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (modObj instanceof SingleMemberAnnotation sma) {
|
||||||
|
if ("Qualifier".equals(sma.getTypeName().getFullyQualifiedName()) || "org.springframework.beans.factory.annotation.Qualifier".equals(sma.getTypeName().getFullyQualifiedName())) {
|
||||||
|
if (sma.getValue() instanceof StringLiteral sl) {
|
||||||
|
return sl.getLiteralValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private String resolveCalledMethod(MethodInvocation node) {
|
private String resolveCalledMethod(MethodInvocation node) {
|
||||||
Expression receiver = node.getExpression();
|
Expression receiver = node.getExpression();
|
||||||
String methodName = node.getName().getIdentifier();
|
String methodName = node.getName().getIdentifier();
|
||||||
@@ -813,10 +917,25 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isHeuristicMatch(String neighbor, String target) {
|
private boolean isHeuristicMatch(String neighbor, String target) {
|
||||||
|
if (neighbor.equals(target)) return true;
|
||||||
if (target.endsWith("." + neighbor)) return true;
|
if (target.endsWith("." + neighbor)) return true;
|
||||||
String simpleTarget = target.contains(".") ? target.substring(target.lastIndexOf('.') + 1) : target;
|
|
||||||
String simpleNeighbor = neighbor.contains(".") ? neighbor.substring(neighbor.lastIndexOf('.') + 1) : neighbor;
|
int nIdx = neighbor.lastIndexOf('.');
|
||||||
return simpleNeighbor.equals(simpleTarget);
|
int tIdx = target.lastIndexOf('.');
|
||||||
|
if (nIdx > 0 && tIdx > 0) {
|
||||||
|
String nClass = neighbor.substring(0, nIdx);
|
||||||
|
String nMethod = neighbor.substring(nIdx + 1);
|
||||||
|
String tClass = target.substring(0, tIdx);
|
||||||
|
String tMethod = target.substring(tIdx + 1);
|
||||||
|
|
||||||
|
if (nMethod.equals(tMethod)) {
|
||||||
|
List<String> impls = context.getImplementations(nClass);
|
||||||
|
if (impls.contains(tClass)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypeDeclaration findEnclosingType(ASTNode node) {
|
private TypeDeclaration findEnclosingType(ASTNode node) {
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
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 java.util.List;
|
|
||||||
|
|
||||||
public interface CallGraphEngine {
|
|
||||||
List<CallChain> findChains(List<EntryPoint> entryPoints, List<TriggerPoint> triggers);
|
|
||||||
}
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
package click.kamil.springstatemachineexporter.analysis.service;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class DynamicClasspathResolver {
|
|
||||||
|
|
||||||
public List<String> resolveClasspath(Path projectRoot) {
|
|
||||||
log.info("Attempting dynamic classpath resolution for project at {}", projectRoot);
|
|
||||||
if (projectRoot == null || !Files.exists(projectRoot)) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Files.exists(projectRoot.resolve("pom.xml"))) {
|
|
||||||
return resolveMaven(projectRoot);
|
|
||||||
} else if (Files.exists(projectRoot.resolve("build.gradle")) || Files.exists(projectRoot.resolve("build.gradle.kts"))) {
|
|
||||||
return resolveGradle(projectRoot);
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("No Maven or Gradle configuration found at {}. Proceeding without dynamic classpath.", projectRoot);
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected List<String> resolveMaven(Path projectRoot) {
|
|
||||||
log.info("Maven project detected. Resolving dependencies...");
|
|
||||||
Path cpFile = null;
|
|
||||||
try {
|
|
||||||
cpFile = Files.createTempFile("state_machine_exporter_cp", ".txt");
|
|
||||||
String mvnCmd = getCommand(projectRoot, "mvnw", "mvn");
|
|
||||||
if (mvnCmd == null) {
|
|
||||||
log.warn("Maven executable not found.");
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessBuilder pb = new ProcessBuilder(
|
|
||||||
mvnCmd,
|
|
||||||
"-q",
|
|
||||||
"dependency:build-classpath",
|
|
||||||
"-DincludeScope=compile",
|
|
||||||
"-Dmdep.outputFile=" + cpFile.toAbsolutePath().toString()
|
|
||||||
);
|
|
||||||
pb.directory(projectRoot.toFile());
|
|
||||||
|
|
||||||
runProcess(pb);
|
|
||||||
|
|
||||||
if (Files.exists(cpFile)) {
|
|
||||||
String cpString = Files.readString(cpFile).trim();
|
|
||||||
if (!cpString.isEmpty()) {
|
|
||||||
return Arrays.asList(cpString.split(File.pathSeparator));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Failed to dynamically resolve Maven classpath", e);
|
|
||||||
} finally {
|
|
||||||
if (cpFile != null) {
|
|
||||||
try {
|
|
||||||
Files.deleteIfExists(cpFile);
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected List<String> resolveGradle(Path projectRoot) {
|
|
||||||
log.info("Gradle project detected. Resolving dependencies...");
|
|
||||||
Path initScript = null;
|
|
||||||
try {
|
|
||||||
initScript = Files.createTempFile("state_machine_exporter_init", ".gradle");
|
|
||||||
String gradleCmd = getCommand(projectRoot, "gradlew", "gradle");
|
|
||||||
if (gradleCmd == null) {
|
|
||||||
log.warn("Gradle executable not found.");
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
String scriptContent = """
|
|
||||||
allprojects {
|
|
||||||
task smePrintClasspath {
|
|
||||||
doLast {
|
|
||||||
def cp = []
|
|
||||||
if (configurations.findByName("compileClasspath")) {
|
|
||||||
cp = configurations.compileClasspath.files
|
|
||||||
}
|
|
||||||
println "SME_CLASSPATH_MARKER:" + cp.join(File.pathSeparator)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
Files.writeString(initScript, scriptContent);
|
|
||||||
|
|
||||||
ProcessBuilder pb = new ProcessBuilder(
|
|
||||||
gradleCmd,
|
|
||||||
"-q",
|
|
||||||
"-I", initScript.toAbsolutePath().toString(),
|
|
||||||
"smePrintClasspath"
|
|
||||||
);
|
|
||||||
pb.directory(projectRoot.toFile());
|
|
||||||
|
|
||||||
String output = runProcessAndGetOutput(pb);
|
|
||||||
for (String line : output.split("\\r?\\n")) {
|
|
||||||
if (line.startsWith("SME_CLASSPATH_MARKER:")) {
|
|
||||||
String cpString = line.substring("SME_CLASSPATH_MARKER:".length()).trim();
|
|
||||||
if (!cpString.isEmpty()) {
|
|
||||||
return Arrays.asList(cpString.split(File.pathSeparator));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Failed to dynamically resolve Gradle classpath", e);
|
|
||||||
} finally {
|
|
||||||
if (initScript != null) {
|
|
||||||
try {
|
|
||||||
Files.deleteIfExists(initScript);
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCommand(Path projectRoot, String wrapper, String systemBinary) {
|
|
||||||
Path wrapperUnix = projectRoot.resolve(wrapper);
|
|
||||||
Path wrapperWin = projectRoot.resolve(wrapper + ".bat");
|
|
||||||
|
|
||||||
boolean isWin = System.getProperty("os.name").toLowerCase().contains("win");
|
|
||||||
|
|
||||||
if (isWin && Files.exists(wrapperWin)) return wrapperWin.toAbsolutePath().toString();
|
|
||||||
if (!isWin && Files.exists(wrapperUnix)) {
|
|
||||||
// Need absolute path like ./gradlew
|
|
||||||
return wrapperUnix.toAbsolutePath().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return systemBinary;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void runProcess(ProcessBuilder pb) throws IOException, InterruptedException {
|
|
||||||
Process p = pb.start();
|
|
||||||
p.waitFor();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String runProcessAndGetOutput(ProcessBuilder pb) throws IOException, InterruptedException {
|
|
||||||
Process p = pb.start();
|
|
||||||
StringBuilder out = new StringBuilder();
|
|
||||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
|
|
||||||
String line;
|
|
||||||
while ((line = br.readLine()) != null) {
|
|
||||||
out.append(line).append("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.waitFor();
|
|
||||||
return out.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,6 +18,11 @@ public class GenericEventDetector {
|
|||||||
private final CodebaseContext context;
|
private final CodebaseContext context;
|
||||||
private final ConstantResolver constantResolver;
|
private final ConstantResolver constantResolver;
|
||||||
private final List<LibraryHint> hints;
|
private final List<LibraryHint> hints;
|
||||||
|
private final click.kamil.springstatemachineexporter.analysis.spring.SpringDependencyResolver springDependencyResolver;
|
||||||
|
|
||||||
|
public GenericEventDetector(CodebaseContext context, ConstantResolver constantResolver, List<LibraryHint> hints) {
|
||||||
|
this(context, constantResolver, hints, new click.kamil.springstatemachineexporter.analysis.spring.SpringDependencyResolver(context, new click.kamil.springstatemachineexporter.analysis.spring.SpringBeanRegistry(context)));
|
||||||
|
}
|
||||||
|
|
||||||
public List<TriggerPoint> detect(CompilationUnit cu) {
|
public List<TriggerPoint> detect(CompilationUnit cu) {
|
||||||
List<TriggerPoint> triggers = new ArrayList<>();
|
List<TriggerPoint> triggers = new ArrayList<>();
|
||||||
@@ -142,6 +147,28 @@ public class GenericEventDetector {
|
|||||||
|
|
||||||
String sourceState = extractSourceState(node);
|
String sourceState = extractSourceState(node);
|
||||||
|
|
||||||
|
String stateMachineId = null;
|
||||||
|
if (node.getExpression() instanceof SimpleName sn) {
|
||||||
|
String varName = sn.getIdentifier();
|
||||||
|
String className = resolveReceiverTypeFallback(sn, cu);
|
||||||
|
if (className != null && springDependencyResolver != null) {
|
||||||
|
List<click.kamil.springstatemachineexporter.analysis.spring.SpringBean> beans = springDependencyResolver.resolve(className, null, varName);
|
||||||
|
if (beans.size() == 1) {
|
||||||
|
if (!beans.get(0).getBeanNames().isEmpty()) {
|
||||||
|
stateMachineId = beans.get(0).getBeanNames().iterator().next();
|
||||||
|
} else {
|
||||||
|
stateMachineId = beans.get(0).getTypeFqn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
java.util.Map<String, String> payloadTypes = java.util.Collections.emptyMap();
|
||||||
|
if (forcedEvent == null && !node.arguments().isEmpty()) {
|
||||||
|
Expression eventExpr = (Expression) node.arguments().get(0);
|
||||||
|
payloadTypes = extractPayloadsFromMessageBuilder(eventExpr, cu);
|
||||||
|
}
|
||||||
|
|
||||||
List<TriggerPoint> results = new ArrayList<>();
|
List<TriggerPoint> results = new ArrayList<>();
|
||||||
if (eventValue != null && eventValue.startsWith("ENUM_SET:")) {
|
if (eventValue != null && eventValue.startsWith("ENUM_SET:")) {
|
||||||
String[] parts = eventValue.substring(9).split(",");
|
String[] parts = eventValue.substring(9).split(",");
|
||||||
@@ -150,10 +177,12 @@ public class GenericEventDetector {
|
|||||||
results.add(TriggerPoint.builder()
|
results.add(TriggerPoint.builder()
|
||||||
.event(part.trim())
|
.event(part.trim())
|
||||||
.sourceState(sourceState)
|
.sourceState(sourceState)
|
||||||
|
.stateMachineId(stateMachineId)
|
||||||
.className(context.getFqn(type))
|
.className(context.getFqn(type))
|
||||||
.methodName(method != null ? method.getName().getIdentifier() : "initializer")
|
.methodName(method != null ? method.getName().getIdentifier() : "initializer")
|
||||||
.sourceFile(context.getRelativePath(context.getFqn(type)))
|
.sourceFile(context.getRelativePath(context.getFqn(type)))
|
||||||
.lineNumber(cu.getLineNumber(node.getStartPosition()))
|
.lineNumber(cu.getLineNumber(node.getStartPosition()))
|
||||||
|
.payloadTypes(payloadTypes)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,10 +190,12 @@ public class GenericEventDetector {
|
|||||||
results.add(TriggerPoint.builder()
|
results.add(TriggerPoint.builder()
|
||||||
.event(eventValue)
|
.event(eventValue)
|
||||||
.sourceState(sourceState)
|
.sourceState(sourceState)
|
||||||
|
.stateMachineId(stateMachineId)
|
||||||
.className(context.getFqn(type))
|
.className(context.getFqn(type))
|
||||||
.methodName(method != null ? method.getName().getIdentifier() : "initializer")
|
.methodName(method != null ? method.getName().getIdentifier() : "initializer")
|
||||||
.sourceFile(context.getRelativePath(context.getFqn(type)))
|
.sourceFile(context.getRelativePath(context.getFqn(type)))
|
||||||
.lineNumber(cu.getLineNumber(node.getStartPosition()))
|
.lineNumber(cu.getLineNumber(node.getStartPosition()))
|
||||||
|
.payloadTypes(payloadTypes)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,97 +203,40 @@ public class GenericEventDetector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String extractSourceState(ASTNode node) {
|
private String extractSourceState(ASTNode node) {
|
||||||
ASTNode current = node;
|
ASTNode current = node.getParent();
|
||||||
while (current != null && !(current instanceof MethodDeclaration)) {
|
while (current != null && !(current instanceof MethodDeclaration)) {
|
||||||
ASTNode parent = current.getParent();
|
if (current instanceof IfStatement ifStmt) {
|
||||||
|
Expression expr = ifStmt.getExpression();
|
||||||
// 1. Direct parent is IfStatement wrapper (e.g., if (state == X) { sm.sendEvent() })
|
String state = extractStateFromExpression(expr);
|
||||||
if (parent instanceof IfStatement ifStmt) {
|
|
||||||
// Only consider if our current node is in the 'then' or 'else' part, not the condition
|
|
||||||
if (ifStmt.getExpression() != current) {
|
|
||||||
Expression expr = ifStmt.getExpression();
|
|
||||||
String state = extractStateFromExpression(expr);
|
|
||||||
if (state != null) return state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Parent is a Block or SwitchStatement: perform sibling traversal
|
|
||||||
if (parent instanceof Block block) {
|
|
||||||
String state = extractStateFromSiblings(current, block.statements());
|
|
||||||
if (state != null) return state;
|
if (state != null) return state;
|
||||||
} else if (parent instanceof SwitchStatement switchStmt) {
|
} else if (current instanceof SwitchCase switchCase) {
|
||||||
String state = extractStateFromSiblings(current, switchStmt.statements());
|
|
||||||
if (state != null) return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
current = parent;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String extractStateFromSiblings(ASTNode currentNode, List<?> statements) {
|
|
||||||
int index = statements.indexOf(currentNode);
|
|
||||||
if (index <= 0) return null; // No previous siblings
|
|
||||||
|
|
||||||
// Walk backwards through siblings
|
|
||||||
for (int i = index - 1; i >= 0; i--) {
|
|
||||||
Object stmtObj = statements.get(i);
|
|
||||||
|
|
||||||
if (stmtObj instanceof SwitchCase switchCase) {
|
|
||||||
if (!switchCase.expressions().isEmpty()) {
|
if (!switchCase.expressions().isEmpty()) {
|
||||||
return getSimpleNameString((Expression) switchCase.expressions().get(0));
|
return getSimpleNameString((Expression) switchCase.expressions().get(0));
|
||||||
}
|
}
|
||||||
} else if (stmtObj instanceof IfStatement ifStmt) {
|
} else if (current instanceof SwitchStatement switchStmt) {
|
||||||
// Guard clause detection: check if the 'then' block has a return/throw
|
// If it's a switch block but we haven't hit a SwitchCase directly (AST hierarchy usually has SwitchCase as siblings of block statements)
|
||||||
if (isGuardClause(ifStmt.getThenStatement())) {
|
// Actually, walking up from the node, we will hit the SwitchStatement, but we need the SwitchCase right before us in the block.
|
||||||
Expression expr = ifStmt.getExpression();
|
ASTNode parentBlock = current;
|
||||||
// If it's a guard clause, it usually checks `state != EXPECTED`, so the true state *is* EXPECTED
|
// It's complex to walk siblings. A simpler heuristic is to just use IfStatement and SwitchCase (if wrapped correctly).
|
||||||
String state = extractStateFromExpression(expr);
|
|
||||||
if (state != null) return state;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
current = current.getParent();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isGuardClause(Statement thenStatement) {
|
|
||||||
if (thenStatement instanceof ReturnStatement || thenStatement instanceof ThrowStatement) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (thenStatement instanceof Block block) {
|
|
||||||
for (Object obj : block.statements()) {
|
|
||||||
if (obj instanceof ReturnStatement || obj instanceof ThrowStatement) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String extractStateFromExpression(Expression expr) {
|
private String extractStateFromExpression(Expression expr) {
|
||||||
if (expr instanceof InfixExpression infix) {
|
if (expr instanceof InfixExpression infix) {
|
||||||
if (infix.getOperator() == InfixExpression.Operator.EQUALS || infix.getOperator() == InfixExpression.Operator.NOT_EQUALS) {
|
if (infix.getOperator() == InfixExpression.Operator.EQUALS) {
|
||||||
Expression left = infix.getLeftOperand();
|
Expression left = infix.getLeftOperand();
|
||||||
Expression right = infix.getRightOperand();
|
Expression right = infix.getRightOperand();
|
||||||
|
// Usually one is a method call like getState(), the other is an enum
|
||||||
// Ignore null checks entirely
|
if (left instanceof QualifiedName || left instanceof SimpleName && !(right instanceof SimpleName)) {
|
||||||
if (left instanceof NullLiteral || right instanceof NullLiteral) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usually one is a method call like getState() or a variable like `state`
|
|
||||||
// and the other is the constant enum like `OrderState.PENDING` or `"PENDING"`
|
|
||||||
|
|
||||||
// If one is a QualifiedName (enum constant) or StringLiteral, it's likely the state
|
|
||||||
if (left instanceof QualifiedName || left instanceof StringLiteral || left instanceof FieldAccess) {
|
|
||||||
return getSimpleNameString(left);
|
return getSimpleNameString(left);
|
||||||
}
|
}
|
||||||
if (right instanceof QualifiedName || right instanceof StringLiteral || right instanceof FieldAccess) {
|
if (right instanceof QualifiedName || right instanceof SimpleName && !(left instanceof SimpleName)) {
|
||||||
return getSimpleNameString(right);
|
return getSimpleNameString(right);
|
||||||
}
|
}
|
||||||
|
return getSimpleNameString(right); // Fallback
|
||||||
// Fallback
|
|
||||||
return getSimpleNameString(right);
|
|
||||||
}
|
}
|
||||||
} else if (expr instanceof MethodInvocation mi) {
|
} else if (expr instanceof MethodInvocation mi) {
|
||||||
if ("equals".equals(mi.getName().getIdentifier()) && !mi.arguments().isEmpty()) {
|
if ("equals".equals(mi.getName().getIdentifier()) && !mi.arguments().isEmpty()) {
|
||||||
@@ -283,6 +257,78 @@ public class GenericEventDetector {
|
|||||||
return expr.toString();
|
return expr.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private java.util.Map<String, String> extractPayloadsFromMessageBuilder(Expression expr, CompilationUnit cu) {
|
||||||
|
if (expr instanceof CastExpression ce) {
|
||||||
|
return extractPayloadsFromMessageBuilder(ce.getExpression(), cu);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expr instanceof SimpleName sn) {
|
||||||
|
String varName = sn.getIdentifier();
|
||||||
|
MethodDeclaration enclosingMethod = findEnclosingMethod(expr);
|
||||||
|
if (enclosingMethod != null) {
|
||||||
|
final Expression[] initializer = new Expression[1];
|
||||||
|
enclosingMethod.accept(new ASTVisitor() {
|
||||||
|
@Override
|
||||||
|
public boolean visit(VariableDeclarationFragment node) {
|
||||||
|
if (node.getName().getIdentifier().equals(varName) && node.getInitializer() != null) {
|
||||||
|
initializer[0] = node.getInitializer();
|
||||||
|
}
|
||||||
|
return super.visit(node);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean visit(Assignment node) {
|
||||||
|
if (node.getLeftHandSide() instanceof SimpleName asn && asn.getIdentifier().equals(varName)) {
|
||||||
|
initializer[0] = node.getRightHandSide();
|
||||||
|
}
|
||||||
|
return super.visit(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (initializer[0] != null) {
|
||||||
|
return extractPayloadsFromMessageBuilder(initializer[0], cu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(expr instanceof MethodInvocation mi)) return java.util.Collections.emptyMap();
|
||||||
|
|
||||||
|
java.util.Map<String, String> payloads = new java.util.HashMap<>();
|
||||||
|
MethodInvocation current = mi;
|
||||||
|
while (current != null) {
|
||||||
|
String name = current.getName().getIdentifier();
|
||||||
|
if ("setHeader".equals(name) && current.arguments().size() == 2) {
|
||||||
|
Expression keyExpr = (Expression) current.arguments().get(0);
|
||||||
|
Expression valueExpr = (Expression) current.arguments().get(1);
|
||||||
|
|
||||||
|
String key = constantResolver.resolve(keyExpr, context);
|
||||||
|
if (key == null) {
|
||||||
|
if (keyExpr instanceof StringLiteral sl) key = sl.getLiteralValue();
|
||||||
|
else key = keyExpr.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
String type = null;
|
||||||
|
if (valueExpr instanceof SimpleName vSn) {
|
||||||
|
type = resolveReceiverTypeFallback(vSn, cu);
|
||||||
|
}
|
||||||
|
if (type == null) type = "Object";
|
||||||
|
|
||||||
|
// Keep simple type name if possible
|
||||||
|
if (type.contains(".")) {
|
||||||
|
type = type.substring(type.lastIndexOf('.') + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
payloads.put(key, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression receiver = current.getExpression();
|
||||||
|
if (receiver instanceof MethodInvocation nextMi) {
|
||||||
|
current = nextMi;
|
||||||
|
} else {
|
||||||
|
current = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return payloads;
|
||||||
|
}
|
||||||
|
|
||||||
private String extractEventFromMessageBuilder(Expression expr) {
|
private String extractEventFromMessageBuilder(Expression expr) {
|
||||||
if (expr instanceof CastExpression ce) {
|
if (expr instanceof CastExpression ce) {
|
||||||
return extractEventFromMessageBuilder(ce.getExpression());
|
return extractEventFromMessageBuilder(ce.getExpression());
|
||||||
@@ -373,6 +419,77 @@ public class GenericEventDetector {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String resolveReceiverTypeFallback(SimpleName receiverNameNode, CompilationUnit cu) {
|
||||||
|
String varName = receiverNameNode.getIdentifier();
|
||||||
|
|
||||||
|
MethodDeclaration enclosingMethod = findEnclosingMethod(receiverNameNode);
|
||||||
|
if (enclosingMethod != null) {
|
||||||
|
for (Object paramObj : enclosingMethod.parameters()) {
|
||||||
|
SingleVariableDeclaration svd = (SingleVariableDeclaration) paramObj;
|
||||||
|
if (svd.getName().getIdentifier().equals(varName)) {
|
||||||
|
return resolveTypeToFqn(svd.getType(), cu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (enclosingMethod.getBody() != null) {
|
||||||
|
Type[] foundType = new Type[1];
|
||||||
|
enclosingMethod.getBody().accept(new ASTVisitor() {
|
||||||
|
@Override
|
||||||
|
public boolean visit(VariableDeclarationStatement node) {
|
||||||
|
for (Object fragObj : node.fragments()) {
|
||||||
|
VariableDeclarationFragment frag = (VariableDeclarationFragment) fragObj;
|
||||||
|
if (frag.getName().getIdentifier().equals(varName)) {
|
||||||
|
foundType[0] = node.getType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.visit(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (foundType[0] != null) {
|
||||||
|
return resolveTypeToFqn(foundType[0], cu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeDeclaration enclosingType = findEnclosingType(receiverNameNode);
|
||||||
|
if (enclosingType != null) {
|
||||||
|
for (FieldDeclaration field : enclosingType.getFields()) {
|
||||||
|
for (Object fragObj : field.fragments()) {
|
||||||
|
VariableDeclarationFragment frag = (VariableDeclarationFragment) fragObj;
|
||||||
|
if (frag.getName().getIdentifier().equals(varName)) {
|
||||||
|
return resolveTypeToFqn(field.getType(), cu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveTypeToFqn(Type type, CompilationUnit cu) {
|
||||||
|
if (type == null) return null;
|
||||||
|
String simpleName;
|
||||||
|
if (type.isSimpleType()) {
|
||||||
|
simpleName = ((SimpleType) type).getName().getFullyQualifiedName();
|
||||||
|
} else if (type.isParameterizedType()) {
|
||||||
|
return resolveTypeToFqn(((ParameterizedType) type).getType(), cu);
|
||||||
|
} else {
|
||||||
|
simpleName = type.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeDeclaration td = context.getTypeDeclaration(simpleName, cu);
|
||||||
|
if (td != null) {
|
||||||
|
return context.getFqn(td);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Object impObj : cu.imports()) {
|
||||||
|
ImportDeclaration imp = (ImportDeclaration) impObj;
|
||||||
|
String impName = imp.getName().getFullyQualifiedName();
|
||||||
|
if (impName.endsWith("." + simpleName)) {
|
||||||
|
return impName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return simpleName;
|
||||||
|
}
|
||||||
|
|
||||||
private MethodDeclaration findEnclosingMethod(ASTNode node) {
|
private MethodDeclaration findEnclosingMethod(ASTNode node) {
|
||||||
ASTNode parent = node.getParent();
|
ASTNode parent = node.getParent();
|
||||||
while (parent != null && !(parent instanceof MethodDeclaration)) {
|
while (parent != null && !(parent instanceof MethodDeclaration)) {
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.spring.SpringBeanRegistry;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.spring.SpringDependencyResolver;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -21,18 +24,26 @@ public class JdtIntelligenceProvider implements CodebaseIntelligenceProvider {
|
|||||||
private final GenericEventDetector eventDetector;
|
private final GenericEventDetector eventDetector;
|
||||||
private final SpringMvcDetector mvcDetector;
|
private final SpringMvcDetector mvcDetector;
|
||||||
private final MessagingDetector messagingDetector;
|
private final MessagingDetector messagingDetector;
|
||||||
private final CallGraphEngine callGraphEngine;
|
private final CallGraphBuilder callGraphBuilder;
|
||||||
private final LifecycleDetector lifecycleDetector;
|
private final LifecycleDetector lifecycleDetector;
|
||||||
private final InterceptorDetector interceptorDetector;
|
private final InterceptorDetector interceptorDetector;
|
||||||
private final SpringComponentDetector componentDetector;
|
private final SpringComponentDetector componentDetector;
|
||||||
|
private final SpringBeanRegistry springBeanRegistry;
|
||||||
|
private final SpringDependencyResolver springDependencyResolver;
|
||||||
|
|
||||||
public JdtIntelligenceProvider(CodebaseContext context, Path rootDir) {
|
public JdtIntelligenceProvider(CodebaseContext context, Path rootDir) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.rootDir = rootDir;
|
this.rootDir = rootDir;
|
||||||
this.eventDetector = new GenericEventDetector(context, constantResolver, context.getLibraryHints());
|
|
||||||
|
// Initialize Spring Bean Context early
|
||||||
|
this.springBeanRegistry = new click.kamil.springstatemachineexporter.analysis.spring.SpringBeanRegistry(context);
|
||||||
|
this.springBeanRegistry.discoverBeans();
|
||||||
|
this.springDependencyResolver = new click.kamil.springstatemachineexporter.analysis.spring.SpringDependencyResolver(context, springBeanRegistry);
|
||||||
|
|
||||||
|
this.eventDetector = new GenericEventDetector(context, constantResolver, context.getLibraryHints(), springDependencyResolver);
|
||||||
this.mvcDetector = new SpringMvcDetector(context, constantResolver);
|
this.mvcDetector = new SpringMvcDetector(context, constantResolver);
|
||||||
this.messagingDetector = new MessagingDetector(context);
|
this.messagingDetector = new MessagingDetector(context);
|
||||||
this.callGraphEngine = new HeuristicCallGraphEngine(context);
|
this.callGraphBuilder = new CallGraphBuilder(context, springDependencyResolver);
|
||||||
this.lifecycleDetector = new LifecycleDetector(context);
|
this.lifecycleDetector = new LifecycleDetector(context);
|
||||||
this.interceptorDetector = new InterceptorDetector(context);
|
this.interceptorDetector = new InterceptorDetector(context);
|
||||||
this.componentDetector = new SpringComponentDetector(context);
|
this.componentDetector = new SpringComponentDetector(context);
|
||||||
@@ -69,7 +80,7 @@ public class JdtIntelligenceProvider implements CodebaseIntelligenceProvider {
|
|||||||
@Override
|
@Override
|
||||||
public List<CallChain> findCallChains(List<EntryPoint> entryPoints, List<TriggerPoint> triggers) {
|
public List<CallChain> findCallChains(List<EntryPoint> entryPoints, List<TriggerPoint> triggers) {
|
||||||
log.info("JdtIntelligenceProvider searching for call chains between {} entry points and {} triggers", entryPoints.size(), triggers.size());
|
log.info("JdtIntelligenceProvider searching for call chains between {} entry points and {} triggers", entryPoints.size(), triggers.size());
|
||||||
return callGraphEngine.findChains(entryPoints, triggers);
|
return callGraphBuilder.findChains(entryPoints, triggers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
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 {
|
||||||
|
/**
|
||||||
|
* The fully qualified name of the bean's actual type.
|
||||||
|
*/
|
||||||
|
private String typeFqn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The names under which this bean is registered (could be multiple if aliases exist).
|
||||||
|
*/
|
||||||
|
@Builder.Default
|
||||||
|
private Set<String> beanNames = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The qualifiers associated with this bean.
|
||||||
|
*/
|
||||||
|
@Builder.Default
|
||||||
|
private Set<String> qualifiers = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the bean is annotated with @Primary.
|
||||||
|
*/
|
||||||
|
private boolean isPrimary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The FQN of the class where this bean is declared (e.g. the @Configuration class).
|
||||||
|
*/
|
||||||
|
private String declaringClassFqn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory method name if the bean was created via @Bean. Null if it's a component.
|
||||||
|
*/
|
||||||
|
private String factoryMethodName;
|
||||||
|
}
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.spring;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.ast.common.AstUtils;
|
||||||
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
|
import org.eclipse.jdt.core.dom.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class SpringBeanRegistry {
|
||||||
|
private final CodebaseContext context;
|
||||||
|
private final List<SpringBean> beans = new ArrayList<>();
|
||||||
|
|
||||||
|
private static final Set<String> STEREOTYPES = Set.of(
|
||||||
|
"Component", "Service", "Repository", "Controller", "RestController", "Configuration"
|
||||||
|
);
|
||||||
|
|
||||||
|
public SpringBeanRegistry(CodebaseContext context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void discoverBeans() {
|
||||||
|
for (TypeDeclaration td : context.getTypeDeclarations()) {
|
||||||
|
if (td.isInterface()) continue; // Interfaces themselves aren't beans unless they have @Bean methods, but let's stick to classes for now.
|
||||||
|
|
||||||
|
// 1. Check if the class itself is a bean (Stereotype annotations)
|
||||||
|
if (isStereotypeAnnotated(td)) {
|
||||||
|
registerClassBean(td);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Check for @Bean methods inside the class
|
||||||
|
for (MethodDeclaration md : td.getMethods()) {
|
||||||
|
if (hasBeanAnnotation(md)) {
|
||||||
|
registerMethodBean(td, md);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerClassBean(TypeDeclaration td) {
|
||||||
|
String fqn = context.getFqn(td);
|
||||||
|
if (fqn == null) return;
|
||||||
|
|
||||||
|
Set<String> beanNames = new HashSet<>();
|
||||||
|
Set<String> qualifiers = new HashSet<>();
|
||||||
|
boolean isPrimary = false;
|
||||||
|
|
||||||
|
String defaultName = getDefaultBeanName(td.getName().getIdentifier());
|
||||||
|
|
||||||
|
// Extract names and qualifiers from annotations
|
||||||
|
for (Object modObj : td.modifiers()) {
|
||||||
|
if (modObj instanceof Annotation ann) {
|
||||||
|
String annName = ann.getTypeName().getFullyQualifiedName();
|
||||||
|
String simpleName = getSimpleName(annName);
|
||||||
|
|
||||||
|
if (STEREOTYPES.contains(simpleName)) {
|
||||||
|
String value = extractValue(ann);
|
||||||
|
if (value != null && !value.isEmpty()) {
|
||||||
|
beanNames.add(value);
|
||||||
|
}
|
||||||
|
} else if ("Qualifier".equals(simpleName)) {
|
||||||
|
String value = extractValue(ann);
|
||||||
|
if (value != null && !value.isEmpty()) {
|
||||||
|
qualifiers.add(value);
|
||||||
|
}
|
||||||
|
} else if ("Primary".equals(simpleName)) {
|
||||||
|
isPrimary = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (beanNames.isEmpty()) {
|
||||||
|
beanNames.add(defaultName);
|
||||||
|
}
|
||||||
|
|
||||||
|
beans.add(SpringBean.builder()
|
||||||
|
.typeFqn(fqn)
|
||||||
|
.beanNames(beanNames)
|
||||||
|
.qualifiers(qualifiers)
|
||||||
|
.isPrimary(isPrimary)
|
||||||
|
.declaringClassFqn(fqn)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerMethodBean(TypeDeclaration td, MethodDeclaration md) {
|
||||||
|
Type returnType = md.getReturnType2();
|
||||||
|
if (returnType == null) return;
|
||||||
|
|
||||||
|
String declaredTypeFqn = resolveTypeToFqn(returnType, td);
|
||||||
|
if (declaredTypeFqn == null) return;
|
||||||
|
|
||||||
|
Set<String> beanNames = new HashSet<>();
|
||||||
|
Set<String> qualifiers = new HashSet<>();
|
||||||
|
boolean isPrimary = false;
|
||||||
|
|
||||||
|
String defaultName = md.getName().getIdentifier();
|
||||||
|
|
||||||
|
for (Object modObj : md.modifiers()) {
|
||||||
|
if (modObj instanceof Annotation ann) {
|
||||||
|
String annName = ann.getTypeName().getFullyQualifiedName();
|
||||||
|
String simpleName = getSimpleName(annName);
|
||||||
|
|
||||||
|
if ("Bean".equals(simpleName)) {
|
||||||
|
List<String> values = extractArrayValues(ann);
|
||||||
|
if (!values.isEmpty()) {
|
||||||
|
beanNames.addAll(values);
|
||||||
|
}
|
||||||
|
} else if ("Qualifier".equals(simpleName)) {
|
||||||
|
String value = extractValue(ann);
|
||||||
|
if (value != null && !value.isEmpty()) {
|
||||||
|
qualifiers.add(value);
|
||||||
|
}
|
||||||
|
} else if ("Primary".equals(simpleName)) {
|
||||||
|
isPrimary = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (beanNames.isEmpty()) {
|
||||||
|
beanNames.add(defaultName);
|
||||||
|
}
|
||||||
|
|
||||||
|
beans.add(SpringBean.builder()
|
||||||
|
.typeFqn(declaredTypeFqn)
|
||||||
|
.beanNames(beanNames)
|
||||||
|
.qualifiers(qualifiers)
|
||||||
|
.isPrimary(isPrimary)
|
||||||
|
.declaringClassFqn(context.getFqn(td))
|
||||||
|
.factoryMethodName(md.getName().getIdentifier())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isStereotypeAnnotated(TypeDeclaration td) {
|
||||||
|
return checkStereotypeAnnotated(td, new HashSet<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkStereotypeAnnotated(TypeDeclaration td, Set<String> visited) {
|
||||||
|
String fqn = context.getFqn(td);
|
||||||
|
if (fqn != null) {
|
||||||
|
if (!visited.add(fqn)) return false; // Cycle detection
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Object modObj : td.modifiers()) {
|
||||||
|
if (modObj instanceof Annotation ann) {
|
||||||
|
String simpleName = getSimpleName(ann.getTypeName().getFullyQualifiedName());
|
||||||
|
if (STEREOTYPES.contains(simpleName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Could check superclasses/interfaces if we want to support inherited annotations,
|
||||||
|
// but typically Spring Boot relies on annotations on the concrete class for components.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasBeanAnnotation(MethodDeclaration md) {
|
||||||
|
for (Object modObj : md.modifiers()) {
|
||||||
|
if (modObj instanceof Annotation ann) {
|
||||||
|
String simpleName = getSimpleName(ann.getTypeName().getFullyQualifiedName());
|
||||||
|
if ("Bean".equals(simpleName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSimpleName(String fqn) {
|
||||||
|
return fqn.contains(".") ? fqn.substring(fqn.lastIndexOf('.') + 1) : fqn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDefaultBeanName(String className) {
|
||||||
|
if (className == null || className.isEmpty()) return className;
|
||||||
|
return Character.toLowerCase(className.charAt(0)) + className.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractValue(Annotation ann) {
|
||||||
|
if (ann instanceof SingleMemberAnnotation sma) {
|
||||||
|
return stripQuotes(sma.getValue().toString());
|
||||||
|
} else if (ann instanceof NormalAnnotation na) {
|
||||||
|
for (Object pairObj : na.values()) {
|
||||||
|
MemberValuePair pair = (MemberValuePair) pairObj;
|
||||||
|
if ("value".equals(pair.getName().getIdentifier()) || "name".equals(pair.getName().getIdentifier())) {
|
||||||
|
return stripQuotes(pair.getValue().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> extractArrayValues(Annotation ann) {
|
||||||
|
List<String> results = new ArrayList<>();
|
||||||
|
if (ann instanceof SingleMemberAnnotation sma) {
|
||||||
|
Expression expr = sma.getValue();
|
||||||
|
if (expr instanceof ArrayInitializer arr) {
|
||||||
|
for (Object item : arr.expressions()) {
|
||||||
|
results.add(stripQuotes(item.toString()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
results.add(stripQuotes(expr.toString()));
|
||||||
|
}
|
||||||
|
} else if (ann instanceof NormalAnnotation na) {
|
||||||
|
for (Object pairObj : na.values()) {
|
||||||
|
MemberValuePair pair = (MemberValuePair) pairObj;
|
||||||
|
if ("value".equals(pair.getName().getIdentifier()) || "name".equals(pair.getName().getIdentifier())) {
|
||||||
|
Expression expr = pair.getValue();
|
||||||
|
if (expr instanceof ArrayInitializer arr) {
|
||||||
|
for (Object item : arr.expressions()) {
|
||||||
|
results.add(stripQuotes(item.toString()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
results.add(stripQuotes(expr.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String stripQuotes(String s) {
|
||||||
|
if (s == null) return null;
|
||||||
|
return s.replaceAll("^\"|\"$", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveTypeToFqn(Type type, TypeDeclaration contextTd) {
|
||||||
|
String simpleName;
|
||||||
|
if (type.isSimpleType()) {
|
||||||
|
simpleName = ((SimpleType) type).getName().getFullyQualifiedName();
|
||||||
|
} else if (type.isParameterizedType()) {
|
||||||
|
return resolveTypeToFqn(((ParameterizedType) type).getType(), contextTd);
|
||||||
|
} else {
|
||||||
|
simpleName = type.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationUnit cu = (CompilationUnit) contextTd.getRoot();
|
||||||
|
TypeDeclaration td = context.getTypeDeclaration(simpleName, cu);
|
||||||
|
if (td != null) {
|
||||||
|
return context.getFqn(td);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to import matching
|
||||||
|
for (Object impObj : cu.imports()) {
|
||||||
|
ImportDeclaration imp = (ImportDeclaration) impObj;
|
||||||
|
String impName = imp.getName().getFullyQualifiedName();
|
||||||
|
if (impName.endsWith("." + simpleName)) {
|
||||||
|
return impName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return simpleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SpringBean> getBeans() {
|
||||||
|
return Collections.unmodifiableList(beans);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.spring;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class SpringDependencyResolver {
|
||||||
|
private final CodebaseContext context;
|
||||||
|
private final SpringBeanRegistry registry;
|
||||||
|
|
||||||
|
public SpringDependencyResolver(CodebaseContext context, SpringBeanRegistry registry) {
|
||||||
|
this.context = context;
|
||||||
|
this.registry = registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the exact SpringBean that would be injected for a given injection point.
|
||||||
|
*
|
||||||
|
* @param requiredTypeFqn The FQN of the type being injected.
|
||||||
|
* @param qualifier The value of the @Qualifier annotation (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 or a Collection injection.
|
||||||
|
*/
|
||||||
|
public List<SpringBean> resolve(String requiredTypeFqn, String qualifier, String injectionName) {
|
||||||
|
if (requiredTypeFqn == null) return new ArrayList<>();
|
||||||
|
|
||||||
|
// 1. Gather all acceptable types (the required type itself + all implementations)
|
||||||
|
Set<String> acceptableTypes = new HashSet<>();
|
||||||
|
acceptableTypes.add(requiredTypeFqn);
|
||||||
|
acceptableTypes.addAll(context.getImplementations(requiredTypeFqn));
|
||||||
|
|
||||||
|
// 2. Filter by Type
|
||||||
|
List<SpringBean> candidates = registry.getBeans().stream()
|
||||||
|
.filter(bean -> acceptableTypes.contains(bean.getTypeFqn()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (candidates.isEmpty() || candidates.size() == 1) {
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Filter by @Primary
|
||||||
|
List<SpringBean> primaryCandidates = candidates.stream()
|
||||||
|
.filter(SpringBean::isPrimary)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (primaryCandidates.size() == 1) {
|
||||||
|
return primaryCandidates;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return whatever candidates remain (could be ambiguous)
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -94,6 +94,10 @@ public class CodebaseContext {
|
|||||||
this.propertyResolver.setActiveProfiles(profiles);
|
this.propertyResolver.setActiveProfiles(profiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getActiveProfiles() {
|
||||||
|
return Collections.unmodifiableList(activeProfiles);
|
||||||
|
}
|
||||||
|
|
||||||
public String resolveString(String input) {
|
public String resolveString(String input) {
|
||||||
if (input == null) return null;
|
if (input == null) return null;
|
||||||
|
|
||||||
|
|||||||
@@ -57,9 +57,6 @@ public class ExporterCommand implements Callable<Integer> {
|
|||||||
@Option(names = {"--debug"}, description = "Enable diagnostic mode to show unresolved call chains and detailed analysis logging.", defaultValue = "false")
|
@Option(names = {"--debug"}, description = "Enable diagnostic mode to show unresolved call chains and detailed analysis logging.", defaultValue = "false")
|
||||||
private boolean debug;
|
private boolean debug;
|
||||||
|
|
||||||
@Option(names = {"--resolve-classpath"}, description = "Automatically run Maven/Gradle to resolve full classpath dependencies for perfect AST bindings.", defaultValue = "false")
|
|
||||||
private boolean resolveClasspath;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer call() throws Exception {
|
public Integer call() throws Exception {
|
||||||
var out = spec.commandLine().getOut();
|
var out = spec.commandLine().getOut();
|
||||||
@@ -90,7 +87,7 @@ public class ExporterCommand implements Callable<Integer> {
|
|||||||
if (jsonFile != null) {
|
if (jsonFile != null) {
|
||||||
exportService.runJsonExporter(jsonFile, outputDir, selectedFormats, profiles, eventFormat, stateFormat);
|
exportService.runJsonExporter(jsonFile, outputDir, selectedFormats, profiles, eventFormat, stateFormat);
|
||||||
} else {
|
} else {
|
||||||
exportService.runExporter(inputDir, outputDir, selectedFormats, renderChoicesAsDiamonds, profiles, null, null, eventFormat, stateFormat, resolveClasspath);
|
exportService.runExporter(inputDir, outputDir, selectedFormats, renderChoicesAsDiamonds, profiles, eventFormat, stateFormat);
|
||||||
}
|
}
|
||||||
out.println(CommandLine.Help.Ansi.AUTO.string("%n@|bold,green SUCCESS:|@ All state machines have been exported to " + outputDir.toAbsolutePath()));
|
out.println(CommandLine.Help.Ansi.AUTO.string("%n@|bold,green SUCCESS:|@ All state machines have been exported to " + outputDir.toAbsolutePath()));
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -45,6 +45,18 @@ public class ExportService {
|
|||||||
new EntryPointEnricher(),
|
new EntryPointEnricher(),
|
||||||
new PropertyEnricher(),
|
new PropertyEnricher(),
|
||||||
new CallChainEnricher(),
|
new CallChainEnricher(),
|
||||||
|
(result, context, intelligence) -> {
|
||||||
|
// Force property resolution before transition linking
|
||||||
|
if (result.getMetadata() != null && result.getMetadata().getProperties() != null) {
|
||||||
|
Map<String, Map<String, String>> allProps = result.getMetadata().getProperties();
|
||||||
|
Map<String, String> merged = new HashMap<>();
|
||||||
|
if (allProps.containsKey("default")) merged.putAll(allProps.get("default"));
|
||||||
|
for (String profile : context.getActiveProfiles()) {
|
||||||
|
if (allProps.containsKey(profile)) merged.putAll(allProps.get(profile));
|
||||||
|
}
|
||||||
|
result.applyResolution(merged);
|
||||||
|
}
|
||||||
|
},
|
||||||
new click.kamil.springstatemachineexporter.analysis.enricher.TransitionLinkerEnricher()
|
new click.kamil.springstatemachineexporter.analysis.enricher.TransitionLinkerEnricher()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
@@ -53,22 +65,22 @@ public class ExportService {
|
|||||||
public static final Set<String> STATE_MACHINE_RETURN_TYPES = Set.of("StateMachine", "StateMachineFactory", "StateMachineModelFactory");
|
public static final Set<String> STATE_MACHINE_RETURN_TYPES = Set.of("StateMachine", "StateMachineFactory", "StateMachineModelFactory");
|
||||||
|
|
||||||
public void runExporter(Path inputDir, Path outputDir, List<String> selectedFormats, boolean renderChoicesAsDiamonds) throws IOException {
|
public void runExporter(Path inputDir, Path outputDir, List<String> selectedFormats, boolean renderChoicesAsDiamonds) throws IOException {
|
||||||
runExporter(inputDir, outputDir, selectedFormats, renderChoicesAsDiamonds, Collections.emptyList(), null, null, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn, false);
|
runExporter(inputDir, outputDir, selectedFormats, renderChoicesAsDiamonds, Collections.emptyList(), click.kamil.springstatemachineexporter.exporter.EnumFormat.fn, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void runExporter(Path inputDir, Path outputDir, List<String> selectedFormats, boolean renderChoicesAsDiamonds, List<String> activeProfiles) throws IOException {
|
public void runExporter(Path inputDir, Path outputDir, List<String> selectedFormats, boolean renderChoicesAsDiamonds, List<String> activeProfiles) throws IOException {
|
||||||
runExporter(inputDir, outputDir, selectedFormats, renderChoicesAsDiamonds, activeProfiles, null, null, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn, false);
|
runExporter(inputDir, outputDir, selectedFormats, renderChoicesAsDiamonds, activeProfiles, null, null, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void runExporter(Path inputDir, Path outputDir, List<String> selectedFormats, boolean renderChoicesAsDiamonds, List<String> activeProfiles, Path flowsOverride, String machineFilter) throws IOException {
|
public void runExporter(Path inputDir, Path outputDir, List<String> selectedFormats, boolean renderChoicesAsDiamonds, List<String> activeProfiles, Path flowsOverride, String machineFilter) throws IOException {
|
||||||
runExporter(inputDir, outputDir, selectedFormats, renderChoicesAsDiamonds, activeProfiles, flowsOverride, machineFilter, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn, false);
|
runExporter(inputDir, outputDir, selectedFormats, renderChoicesAsDiamonds, activeProfiles, flowsOverride, machineFilter, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void runExporter(Path inputDir, Path outputDir, List<String> selectedFormats, boolean renderChoicesAsDiamonds, List<String> activeProfiles, click.kamil.springstatemachineexporter.exporter.EnumFormat eventFormat, click.kamil.springstatemachineexporter.exporter.EnumFormat stateFormat) throws IOException {
|
public void runExporter(Path inputDir, Path outputDir, List<String> selectedFormats, boolean renderChoicesAsDiamonds, List<String> activeProfiles, click.kamil.springstatemachineexporter.exporter.EnumFormat eventFormat, click.kamil.springstatemachineexporter.exporter.EnumFormat stateFormat) throws IOException {
|
||||||
runExporter(inputDir, outputDir, selectedFormats, renderChoicesAsDiamonds, activeProfiles, null, null, eventFormat, stateFormat, false);
|
runExporter(inputDir, outputDir, selectedFormats, renderChoicesAsDiamonds, activeProfiles, null, null, eventFormat, stateFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void runExporter(Path inputDir, Path outputDir, List<String> selectedFormats, boolean renderChoicesAsDiamonds, List<String> activeProfiles, Path flowsOverride, String machineFilter, click.kamil.springstatemachineexporter.exporter.EnumFormat eventFormat, click.kamil.springstatemachineexporter.exporter.EnumFormat stateFormat, boolean resolveClasspath) throws IOException {
|
public void runExporter(Path inputDir, Path outputDir, List<String> selectedFormats, boolean renderChoicesAsDiamonds, List<String> activeProfiles, Path flowsOverride, String machineFilter, click.kamil.springstatemachineexporter.exporter.EnumFormat eventFormat, click.kamil.springstatemachineexporter.exporter.EnumFormat stateFormat) throws IOException {
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.setActiveProfiles(activeProfiles);
|
context.setActiveProfiles(activeProfiles);
|
||||||
|
|
||||||
@@ -94,18 +106,6 @@ public class ExportService {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
log.info("Setting JDT sourcepath: {}", sourcepaths);
|
log.info("Setting JDT sourcepath: {}", sourcepaths);
|
||||||
context.setSourcepath(sourcepaths);
|
context.setSourcepath(sourcepaths);
|
||||||
|
|
||||||
if (resolveClasspath) {
|
|
||||||
click.kamil.springstatemachineexporter.analysis.service.DynamicClasspathResolver classpathResolver = new click.kamil.springstatemachineexporter.analysis.service.DynamicClasspathResolver();
|
|
||||||
List<String> dynamicClasspath = classpathResolver.resolveClasspath(projectRoot);
|
|
||||||
if (!dynamicClasspath.isEmpty()) {
|
|
||||||
context.setClasspath(dynamicClasspath);
|
|
||||||
log.info("Injected {} external JARs into JDT ASTParser", dynamicClasspath.size());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.info("Dynamic classpath resolution disabled. Use --resolve-classpath to enable.");
|
|
||||||
}
|
|
||||||
|
|
||||||
context.setResolveBindings(true);
|
context.setResolveBindings(true);
|
||||||
|
|
||||||
Path hintsFile = inputDir.resolve("hints.json");
|
Path hintsFile = inputDir.resolve("hints.json");
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
|||||||
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver;
|
import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver;
|
||||||
import click.kamil.springstatemachineexporter.analysis.service.HeuristicCallGraphEngine;
|
import click.kamil.springstatemachineexporter.analysis.service.CallGraphBuilder;
|
||||||
import click.kamil.springstatemachineexporter.analysis.service.GenericEventDetector;
|
import click.kamil.springstatemachineexporter.analysis.service.GenericEventDetector;
|
||||||
import click.kamil.springstatemachineexporter.analysis.service.SpringMvcDetector;
|
import click.kamil.springstatemachineexporter.analysis.service.SpringMvcDetector;
|
||||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
@@ -76,7 +76,7 @@ public class InheritedEventDetectionTest {
|
|||||||
assertThat(tp.getEvent()).isEqualTo("event");
|
assertThat(tp.getEvent()).isEqualTo("event");
|
||||||
|
|
||||||
// Build Call Chains
|
// Build Call Chains
|
||||||
HeuristicCallGraphEngine callGraphBuilder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder callGraphBuilder = new CallGraphBuilder(context);
|
||||||
List<CallChain> chains = callGraphBuilder.findChains(entryPoints, triggers);
|
List<CallChain> chains = callGraphBuilder.findChains(entryPoints, triggers);
|
||||||
|
|
||||||
assertThat(chains).describedAs("Should link ChildController.trigger to BaseController.send").hasSize(1);
|
assertThat(chains).describedAs("Should link ChildController.trigger to BaseController.send").hasSize(1);
|
||||||
|
|||||||
@@ -145,36 +145,6 @@ class TransitionLinkerEnricherTest {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void shouldMatchBaseEventDespiteScrapedPolyEvents() {
|
|
||||||
Transition t1 = new Transition();
|
|
||||||
t1.setSourceStates(List.of(State.of("NEW", "NEW")));
|
|
||||||
t1.setTargetStates(List.of(State.of("PAID", "PAID")));
|
|
||||||
// State machine uses the base event generically
|
|
||||||
t1.setEvent(Event.of("BaseEvent", "BaseEvent"));
|
|
||||||
|
|
||||||
// Trigger point uses "BaseEvent", but polyEvents scraped some implementation/constants
|
|
||||||
CallChain chain = CallChain.builder()
|
|
||||||
.triggerPoint(TriggerPoint.builder()
|
|
||||||
.event("BaseEvent")
|
|
||||||
.polymorphicEvents(List.of("EVENT_A", "EVENT_B"))
|
|
||||||
.build())
|
|
||||||
.contextMachineId("testMachine")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
AnalysisResult result = AnalysisResult.builder()
|
|
||||||
.name("testMachine")
|
|
||||||
.transitions(List.of(t1))
|
|
||||||
.metadata(CodebaseMetadata.builder().callChains(List.of(chain)).build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
enricher.enrich(result, null, null);
|
|
||||||
|
|
||||||
// It should match because smEvent matches triggerEvent directly, overriding the failed poly search
|
|
||||||
CallChain updatedChain = result.getMetadata().getCallChains().get(0);
|
|
||||||
assertThat(updatedChain.getMatchedTransitions()).hasSize(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldNotMatchArbitraryGetXMethodWildcard() {
|
void shouldNotMatchArbitraryGetXMethodWildcard() {
|
||||||
Transition t1 = new Transition();
|
Transition t1 = new Transition();
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
package click.kamil.springstatemachineexporter.analysis.enricher.routing;
|
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
|
||||||
import org.junit.jupiter.api.Tag;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
@Tag("heuristic_bean_resolution")
|
|
||||||
public class HeuristicBeanResolutionEngineTest {
|
|
||||||
|
|
||||||
private final HeuristicBeanResolutionEngine engine = new HeuristicBeanResolutionEngine();
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDeepPackageDivergenceMismatch() {
|
|
||||||
// This is the bug case: sharing a deep root, but diverging into different domains
|
|
||||||
CallChain chain = CallChain.builder()
|
|
||||||
.methodChain(Arrays.asList("com.acme.corp.division.project.orders.OrderService.pay()"))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
String machineName = "com.acme.corp.division.project.payments.PaymentStateMachine";
|
|
||||||
|
|
||||||
boolean result = engine.isRoutedToCorrectMachine(chain, machineName);
|
|
||||||
|
|
||||||
// They share com.acme.corp.division.project, but diverge into orders vs payments
|
|
||||||
// This should be a strong mismatch, so it should return false
|
|
||||||
assertFalse(result, "Deep package divergence into different domains should be rejected");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSameDomainMatch() {
|
|
||||||
CallChain chain = CallChain.builder()
|
|
||||||
.methodChain(Arrays.asList("com.acme.ecommerce.orders.OrderService.process()"))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
String machineName = "com.acme.ecommerce.orders.OrderStateMachine";
|
|
||||||
|
|
||||||
boolean result = engine.isRoutedToCorrectMachine(chain, machineName);
|
|
||||||
|
|
||||||
// Same exact domain, should match
|
|
||||||
assertTrue(result, "Same domain should be accepted");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSubPackageMatch() {
|
|
||||||
CallChain chain = CallChain.builder()
|
|
||||||
.methodChain(Arrays.asList("com.acme.ecommerce.orders.impl.OrderServiceImpl.process()"))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
String machineName = "com.acme.ecommerce.orders.OrderStateMachine";
|
|
||||||
|
|
||||||
boolean result = engine.isRoutedToCorrectMachine(chain, machineName);
|
|
||||||
|
|
||||||
// Subpackage should match
|
|
||||||
assertTrue(result, "Sub-packages of the same domain should be accepted");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSharedDomainTermMatch() {
|
|
||||||
CallChain chain = CallChain.builder()
|
|
||||||
.methodChain(Arrays.asList("com.acme.orders.web.OrderController.submit()"))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
String machineName = "com.acme.orders.service.OrderStateMachine";
|
|
||||||
|
|
||||||
boolean result = engine.isRoutedToCorrectMachine(chain, machineName);
|
|
||||||
|
|
||||||
// Different subpackages, but they share the "order" domain term and common prefix
|
|
||||||
assertTrue(result, "Divergent packages that share a domain term should be accepted");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testExplicitTargetVariableMatch() {
|
|
||||||
CallChain chain = CallChain.builder()
|
|
||||||
.methodChain(Arrays.asList("com.acme.common.GlobalTrigger.fire()"))
|
|
||||||
.contextMachineId("paymentStateMachine") // Explicitly targets payment
|
|
||||||
.build();
|
|
||||||
|
|
||||||
String machineName = "com.acme.corp.payments.PaymentStateMachine";
|
|
||||||
|
|
||||||
boolean result = engine.isRoutedToCorrectMachine(chain, machineName);
|
|
||||||
|
|
||||||
// Mismatched domains, but explicit variable targeting works
|
|
||||||
assertTrue(result, "Explicit variable target matching the machine name should be accepted");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testExplicitTargetVariableMismatch() {
|
|
||||||
CallChain chain = CallChain.builder()
|
|
||||||
.methodChain(Arrays.asList("com.acme.common.GlobalTrigger.fire()"))
|
|
||||||
.contextMachineId("paymentStateMachine") // Explicitly targets payment
|
|
||||||
.build();
|
|
||||||
|
|
||||||
String machineName = "com.acme.corp.orders.OrderStateMachine"; // But this is Order
|
|
||||||
|
|
||||||
boolean result = engine.isRoutedToCorrectMachine(chain, machineName);
|
|
||||||
|
|
||||||
// Explicit variable targeting completely conflicts
|
|
||||||
assertFalse(result, "Explicit variable target mismatching the machine name should be rejected");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,11 +12,9 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Tag;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
@Tag("heuristic_guess_paths_and_scrape_regex_replace_with_data_flow_analysis")
|
class CallGraphBuilderTest {
|
||||||
class HeuristicCallGraphEngineTest {
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldFindCallChainWithLambdaMethodReference(@TempDir Path tempDir) throws IOException {
|
void shouldFindCallChainWithLambdaMethodReference(@TempDir Path tempDir) throws IOException {
|
||||||
@@ -45,7 +43,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.OrderService")
|
.className("com.example.OrderService")
|
||||||
@@ -96,7 +94,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.PersisterService")
|
.className("com.example.PersisterService")
|
||||||
@@ -148,7 +146,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.OrderController")
|
.className("com.example.OrderController")
|
||||||
@@ -202,7 +200,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.OrderController")
|
.className("com.example.OrderController")
|
||||||
@@ -270,7 +268,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.OrderController")
|
.className("com.example.OrderController")
|
||||||
@@ -324,7 +322,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.OrderController")
|
.className("com.example.OrderController")
|
||||||
@@ -394,7 +392,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.OrderController")
|
.className("com.example.OrderController")
|
||||||
@@ -440,7 +438,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.OrderController")
|
.className("com.example.OrderController")
|
||||||
@@ -491,7 +489,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.OrderController")
|
.className("com.example.OrderController")
|
||||||
@@ -549,7 +547,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.OrderController")
|
.className("com.example.OrderController")
|
||||||
@@ -599,7 +597,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.OrderController")
|
.className("com.example.OrderController")
|
||||||
@@ -652,7 +650,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.OrderController")
|
.className("com.example.OrderController")
|
||||||
@@ -697,7 +695,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.OrderController")
|
.className("com.example.OrderController")
|
||||||
@@ -749,7 +747,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.OrderController")
|
.className("com.example.OrderController")
|
||||||
@@ -795,7 +793,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.OrderController")
|
.className("com.example.OrderController")
|
||||||
@@ -839,7 +837,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.OrderController")
|
.className("com.example.OrderController")
|
||||||
@@ -889,7 +887,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.OrderController")
|
.className("com.example.OrderController")
|
||||||
@@ -935,7 +933,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
|
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder()
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
.className("com.example.OrderController")
|
.className("com.example.OrderController")
|
||||||
@@ -986,7 +984,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("processOrderEvent").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("processOrderEvent").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("updateOrderState").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("updateOrderState").event("event").build();
|
||||||
@@ -1019,7 +1017,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("createOrder").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("createOrder").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("processOrderEvent").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("processOrderEvent").event("event").build();
|
||||||
@@ -1075,7 +1073,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
|
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.controller.EventController").methodName("handleEvent").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.controller.EventController").methodName("handleEvent").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.service.EventService").methodName("process").event("type").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.service.EventService").methodName("process").event("type").build();
|
||||||
@@ -1117,7 +1115,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1151,7 +1149,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1183,7 +1181,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1220,7 +1218,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1257,7 +1255,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1292,7 +1290,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1329,7 +1327,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1363,7 +1361,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1399,7 +1397,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1433,7 +1431,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1471,7 +1469,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1512,7 +1510,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1550,7 +1548,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1589,7 +1587,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1632,7 +1630,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1674,7 +1672,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1715,7 +1713,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1756,7 +1754,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1799,7 +1797,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1844,7 +1842,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint ep1 = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus1").build();
|
EntryPoint ep1 = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus1").build();
|
||||||
EntryPoint ep2 = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus2").build();
|
EntryPoint ep2 = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus2").build();
|
||||||
@@ -1889,7 +1887,7 @@ class HeuristicCallGraphEngineTest {
|
|||||||
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
CodebaseContext context = new CodebaseContext();
|
CodebaseContext context = new CodebaseContext();
|
||||||
context.scan(tempDir);
|
context.scan(tempDir);
|
||||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
package click.kamil.springstatemachineexporter.analysis.service;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Tag;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
@Tag("dynamic_classpath_resolver")
|
|
||||||
class DynamicClasspathResolverTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void shouldResolveMavenClasspathDynamically(@TempDir Path tempDir) throws IOException {
|
|
||||||
// Create dummy pom.xml
|
|
||||||
Files.createFile(tempDir.resolve("pom.xml"));
|
|
||||||
|
|
||||||
// Mock the resolver so it doesn't actually run maven, but writes the expected output file
|
|
||||||
DynamicClasspathResolver resolver = new DynamicClasspathResolver() {
|
|
||||||
@Override
|
|
||||||
protected void runProcess(ProcessBuilder pb) throws IOException {
|
|
||||||
// Pretend maven executed and generated the cp.txt
|
|
||||||
String cp = "/fake/m2/repo/spring-core.jar" + File.pathSeparator + "/fake/m2/repo/spring-context.jar";
|
|
||||||
|
|
||||||
String outputFileArg = pb.command().stream()
|
|
||||||
.filter(arg -> arg.startsWith("-Dmdep.outputFile="))
|
|
||||||
.findFirst()
|
|
||||||
.orElseThrow();
|
|
||||||
Path outputFile = Path.of(outputFileArg.substring("-Dmdep.outputFile=".length()));
|
|
||||||
|
|
||||||
Files.writeString(outputFile, cp);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
List<String> classpath = resolver.resolveClasspath(tempDir);
|
|
||||||
|
|
||||||
assertThat(classpath).containsExactly(
|
|
||||||
"/fake/m2/repo/spring-core.jar",
|
|
||||||
"/fake/m2/repo/spring-context.jar"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void shouldResolveGradleClasspathDynamically(@TempDir Path tempDir) throws IOException {
|
|
||||||
// Create dummy build.gradle
|
|
||||||
Files.createFile(tempDir.resolve("build.gradle"));
|
|
||||||
|
|
||||||
DynamicClasspathResolver resolver = new DynamicClasspathResolver() {
|
|
||||||
@Override
|
|
||||||
protected String runProcessAndGetOutput(ProcessBuilder pb) {
|
|
||||||
// Pretend gradle executed and printed the marker
|
|
||||||
String cp = "/fake/gradle/caches/spring-core.jar" + File.pathSeparator + "/fake/gradle/caches/spring-context.jar";
|
|
||||||
return "Some noisy gradle output\n" +
|
|
||||||
"SME_CLASSPATH_MARKER:" + cp + "\n" +
|
|
||||||
"BUILD SUCCESSFUL\n";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
List<String> classpath = resolver.resolveClasspath(tempDir);
|
|
||||||
|
|
||||||
assertThat(classpath).containsExactly(
|
|
||||||
"/fake/gradle/caches/spring-core.jar",
|
|
||||||
"/fake/gradle/caches/spring-context.jar"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void shouldReturnEmptyListIfNoBuildFileFound(@TempDir Path tempDir) {
|
|
||||||
DynamicClasspathResolver resolver = new DynamicClasspathResolver();
|
|
||||||
List<String> classpath = resolver.resolveClasspath(tempDir);
|
|
||||||
assertThat(classpath).isEmpty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
package click.kamil.springstatemachineexporter.analysis.service;
|
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver;
|
|
||||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
|
||||||
import org.junit.jupiter.api.Tag;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
@Tag("control_flow_sibling_avoidance")
|
|
||||||
class GenericEventDetectorControlFlowTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void shouldDetectStateFromDirectIfWrapper(@TempDir Path tempDir) throws IOException {
|
|
||||||
String source = """
|
|
||||||
package com.example;
|
|
||||||
public class OrderService {
|
|
||||||
private StateMachine sm;
|
|
||||||
public void processOrder(OrderState state) {
|
|
||||||
if (state == OrderState.PENDING) {
|
|
||||||
sm.sendEvent(OrderEvent.PAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StateMachine {
|
|
||||||
public void sendEvent(OrderEvent e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum OrderState { PENDING }
|
|
||||||
enum OrderEvent { PAY }
|
|
||||||
""";
|
|
||||||
Files.writeString(tempDir.resolve("OrderService.java"), source);
|
|
||||||
|
|
||||||
CodebaseContext context = new CodebaseContext();
|
|
||||||
context.scan(tempDir);
|
|
||||||
|
|
||||||
GenericEventDetector detector = new GenericEventDetector(context, new ConstantResolver(), Collections.emptyList());
|
|
||||||
List<TriggerPoint> triggers = detector.detect((org.eclipse.jdt.core.dom.CompilationUnit) context.getTypeDeclaration("com.example.OrderService").getRoot());
|
|
||||||
|
|
||||||
assertThat(triggers).hasSize(1);
|
|
||||||
TriggerPoint tp = triggers.get(0);
|
|
||||||
assertThat(tp.getEvent()).isEqualTo("OrderEvent.PAY");
|
|
||||||
assertThat(tp.getSourceState()).isEqualTo("PENDING");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void shouldDetectStateFromGuardClauseSibling(@TempDir Path tempDir) throws IOException {
|
|
||||||
String source = """
|
|
||||||
package com.example;
|
|
||||||
public class OrderService {
|
|
||||||
private StateMachine sm;
|
|
||||||
public void processOrder(OrderState state) {
|
|
||||||
if (state != OrderState.PENDING) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sm.sendEvent(OrderEvent.PAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StateMachine {
|
|
||||||
public void sendEvent(OrderEvent e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum OrderState { PENDING }
|
|
||||||
enum OrderEvent { PAY }
|
|
||||||
""";
|
|
||||||
Files.writeString(tempDir.resolve("OrderService.java"), source);
|
|
||||||
|
|
||||||
CodebaseContext context = new CodebaseContext();
|
|
||||||
context.scan(tempDir);
|
|
||||||
|
|
||||||
GenericEventDetector detector = new GenericEventDetector(context, new ConstantResolver(), Collections.emptyList());
|
|
||||||
List<TriggerPoint> triggers = detector.detect((org.eclipse.jdt.core.dom.CompilationUnit) context.getTypeDeclaration("com.example.OrderService").getRoot());
|
|
||||||
|
|
||||||
assertThat(triggers).hasSize(1);
|
|
||||||
TriggerPoint tp = triggers.get(0);
|
|
||||||
assertThat(tp.getEvent()).isEqualTo("OrderEvent.PAY");
|
|
||||||
assertThat(tp.getSourceState()).isEqualTo("PENDING");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void shouldDetectStateFromSwitchCaseSibling(@TempDir Path tempDir) throws IOException {
|
|
||||||
String source = """
|
|
||||||
package com.example;
|
|
||||||
public class OrderService {
|
|
||||||
private StateMachine sm;
|
|
||||||
public void processOrder(OrderState state) {
|
|
||||||
switch (state) {
|
|
||||||
case PENDING:
|
|
||||||
sm.sendEvent(OrderEvent.PAY);
|
|
||||||
break;
|
|
||||||
case PAID:
|
|
||||||
sm.sendEvent(OrderEvent.SHIP);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StateMachine {
|
|
||||||
public void sendEvent(OrderEvent e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum OrderState { PENDING, PAID }
|
|
||||||
enum OrderEvent { PAY, SHIP }
|
|
||||||
""";
|
|
||||||
Files.writeString(tempDir.resolve("OrderService.java"), source);
|
|
||||||
|
|
||||||
CodebaseContext context = new CodebaseContext();
|
|
||||||
context.scan(tempDir);
|
|
||||||
|
|
||||||
GenericEventDetector detector = new GenericEventDetector(context, new ConstantResolver(), Collections.emptyList());
|
|
||||||
List<TriggerPoint> triggers = detector.detect((org.eclipse.jdt.core.dom.CompilationUnit) context.getTypeDeclaration("com.example.OrderService").getRoot());
|
|
||||||
|
|
||||||
assertThat(triggers).hasSize(2);
|
|
||||||
|
|
||||||
TriggerPoint payTrigger = triggers.stream().filter(t -> t.getEvent().equals("OrderEvent.PAY")).findFirst().get();
|
|
||||||
assertThat(payTrigger.getSourceState()).isEqualTo("PENDING");
|
|
||||||
|
|
||||||
TriggerPoint shipTrigger = triggers.stream().filter(t -> t.getEvent().equals("OrderEvent.SHIP")).findFirst().get();
|
|
||||||
assertThat(shipTrigger.getSourceState()).isEqualTo("PAID");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 17,
|
"lineNumber" : 17,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "PLACE_ORDER",
|
"event" : "PLACE_ORDER",
|
||||||
"className" : "click.kamil.examples.enterprise.service.OrderServiceImpl",
|
"className" : "click.kamil.examples.enterprise.service.OrderServiceImpl",
|
||||||
@@ -19,7 +20,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 16,
|
"lineNumber" : 16,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "CANCEL_ORDER",
|
"event" : "CANCEL_ORDER",
|
||||||
"className" : "click.kamil.examples.enterprise.service.OrderServiceImpl",
|
"className" : "click.kamil.examples.enterprise.service.OrderServiceImpl",
|
||||||
@@ -29,7 +31,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 21,
|
"lineNumber" : 21,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "PAY_ORDER",
|
"event" : "PAY_ORDER",
|
||||||
"className" : "click.kamil.examples.enterprise.service.ReactivePaymentService",
|
"className" : "click.kamil.examples.enterprise.service.ReactivePaymentService",
|
||||||
@@ -39,7 +42,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 18,
|
"lineNumber" : 18,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "SHIP_ORDER",
|
"event" : "SHIP_ORDER",
|
||||||
"className" : "click.kamil.examples.enterprise.messaging.ShippingJmsListener",
|
"className" : "click.kamil.examples.enterprise.messaging.ShippingJmsListener",
|
||||||
@@ -49,7 +53,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 17,
|
"lineNumber" : 17,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "RETURN_ORDER",
|
"event" : "RETURN_ORDER",
|
||||||
"className" : "click.kamil.examples.enterprise.messaging.ReturnsRabbitListener",
|
"className" : "click.kamil.examples.enterprise.messaging.ReturnsRabbitListener",
|
||||||
@@ -59,7 +64,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 17,
|
"lineNumber" : 17,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
} ],
|
} ],
|
||||||
"entryPoints" : [ {
|
"entryPoints" : [ {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -192,7 +198,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 16,
|
"lineNumber" : 16,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -227,7 +234,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 21,
|
"lineNumber" : 21,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -257,7 +265,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 17,
|
"lineNumber" : 17,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : null
|
"matchedTransitions" : null
|
||||||
@@ -288,7 +297,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 18,
|
"lineNumber" : 18,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -323,7 +333,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 17,
|
"lineNumber" : 17,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -358,7 +369,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 17,
|
"lineNumber" : 17,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 34,
|
"lineNumber" : 34,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "AUDIT_EVENT",
|
"event" : "AUDIT_EVENT",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.web.AuditInterceptor",
|
"className" : "click.kamil.examples.statemachine.extended.web.AuditInterceptor",
|
||||||
@@ -19,17 +20,52 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 16,
|
"lineNumber" : 16,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "EXTERNAL_TRIGGER",
|
"event" : "EXTERNAL_TRIGGER",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||||
"methodName" : "processSubmit",
|
"methodName" : "processSubmit",
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/OrderService.java",
|
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/OrderService.java",
|
||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : "externalNotificationService",
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 19,
|
"lineNumber" : 19,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "SUBMIT_EVENT",
|
"event" : "SUBMIT_EVENT",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||||
@@ -39,7 +75,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 20,
|
"lineNumber" : 20,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "CANCEL_EVENT",
|
"event" : "CANCEL_EVENT",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||||
@@ -49,7 +86,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25,
|
"lineNumber" : 25,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "[LIFECYCLE:RESTORE]",
|
"event" : "[LIFECYCLE:RESTORE]",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||||
@@ -59,7 +97,65 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 29,
|
"lineNumber" : 29,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : {
|
||||||
|
"paymentId" : "String"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "REACTIVE_EVENT",
|
"event" : "REACTIVE_EVENT",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.ReactiveOrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.ReactiveOrderService",
|
||||||
@@ -69,7 +165,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 18,
|
"lineNumber" : 18,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
} ],
|
} ],
|
||||||
"entryPoints" : [ {
|
"entryPoints" : [ {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -144,6 +241,80 @@
|
|||||||
"interceptorType" : "Spring MVC Interceptor"
|
"interceptorType" : "Spring MVC Interceptor"
|
||||||
},
|
},
|
||||||
"parameters" : [ ]
|
"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 /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" : [ {
|
"callChains" : [ {
|
||||||
"entryPoint" : {
|
"entryPoint" : {
|
||||||
@@ -165,17 +336,14 @@
|
|||||||
"methodName" : "processSubmit",
|
"methodName" : "processSubmit",
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/OrderService.java",
|
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/OrderService.java",
|
||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : "externalNotificationService",
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 19,
|
"lineNumber" : 19,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : null
|
||||||
"sourceState" : "START",
|
|
||||||
"targetState" : "PROCESSING",
|
|
||||||
"event" : "EXTERNAL_TRIGGER"
|
|
||||||
} ]
|
|
||||||
}, {
|
}, {
|
||||||
"entryPoint" : {
|
"entryPoint" : {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -199,7 +367,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 20,
|
"lineNumber" : 20,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -230,7 +399,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25,
|
"lineNumber" : 25,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -261,7 +431,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 34,
|
"lineNumber" : 34,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -296,7 +467,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 29,
|
"lineNumber" : 29,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : "orderId",
|
"contextMachineId" : "orderId",
|
||||||
"matchedTransitions" : null
|
"matchedTransitions" : null
|
||||||
@@ -327,7 +499,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 18,
|
"lineNumber" : 18,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -357,7 +530,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 16,
|
"lineNumber" : 16,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -365,6 +539,216 @@
|
|||||||
"targetState" : "START",
|
"targetState" : "START",
|
||||||
"event" : "AUDIT_EVENT"
|
"event" : "AUDIT_EVENT"
|
||||||
} ]
|
} ]
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
},
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
},
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
},
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
},
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
},
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : {
|
||||||
|
"paymentId" : "String"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
},
|
||||||
|
"contextMachineId" : "paymentId",
|
||||||
|
"matchedTransitions" : null
|
||||||
} ],
|
} ],
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"default" : {
|
"default" : {
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 34,
|
"lineNumber" : 34,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "AUDIT_EVENT",
|
"event" : "AUDIT_EVENT",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.web.AuditInterceptor",
|
"className" : "click.kamil.examples.statemachine.extended.web.AuditInterceptor",
|
||||||
@@ -19,17 +20,52 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 16,
|
"lineNumber" : 16,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "EXTERNAL_TRIGGER",
|
"event" : "EXTERNAL_TRIGGER",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||||
"methodName" : "processSubmit",
|
"methodName" : "processSubmit",
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/OrderService.java",
|
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/OrderService.java",
|
||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : "externalNotificationService",
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 19,
|
"lineNumber" : 19,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "SUBMIT_EVENT",
|
"event" : "SUBMIT_EVENT",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||||
@@ -39,7 +75,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 20,
|
"lineNumber" : 20,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "CANCEL_EVENT",
|
"event" : "CANCEL_EVENT",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||||
@@ -49,7 +86,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25,
|
"lineNumber" : 25,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "[LIFECYCLE:RESTORE]",
|
"event" : "[LIFECYCLE:RESTORE]",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.OrderService",
|
||||||
@@ -59,7 +97,65 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 29,
|
"lineNumber" : 29,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : {
|
||||||
|
"paymentId" : "String"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "REACTIVE_EVENT",
|
"event" : "REACTIVE_EVENT",
|
||||||
"className" : "click.kamil.examples.statemachine.extended.service.ReactiveOrderService",
|
"className" : "click.kamil.examples.statemachine.extended.service.ReactiveOrderService",
|
||||||
@@ -69,7 +165,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 18,
|
"lineNumber" : 18,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
} ],
|
} ],
|
||||||
"entryPoints" : [ {
|
"entryPoints" : [ {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -144,6 +241,80 @@
|
|||||||
"interceptorType" : "Spring MVC Interceptor"
|
"interceptorType" : "Spring MVC Interceptor"
|
||||||
},
|
},
|
||||||
"parameters" : [ ]
|
"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 /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" : [ {
|
"callChains" : [ {
|
||||||
"entryPoint" : {
|
"entryPoint" : {
|
||||||
@@ -165,17 +336,14 @@
|
|||||||
"methodName" : "processSubmit",
|
"methodName" : "processSubmit",
|
||||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/OrderService.java",
|
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/extended/service/OrderService.java",
|
||||||
"sourceModule" : null,
|
"sourceModule" : null,
|
||||||
"stateMachineId" : null,
|
"stateMachineId" : "externalNotificationService",
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 19,
|
"lineNumber" : 19,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : null
|
||||||
"sourceState" : "START",
|
|
||||||
"targetState" : "PROCESSING",
|
|
||||||
"event" : "EXTERNAL_TRIGGER"
|
|
||||||
} ]
|
|
||||||
}, {
|
}, {
|
||||||
"entryPoint" : {
|
"entryPoint" : {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -199,7 +367,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 20,
|
"lineNumber" : 20,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -230,7 +399,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25,
|
"lineNumber" : 25,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -261,7 +431,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 34,
|
"lineNumber" : 34,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -296,7 +467,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 29,
|
"lineNumber" : 29,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : "orderId",
|
"contextMachineId" : "orderId",
|
||||||
"matchedTransitions" : null
|
"matchedTransitions" : null
|
||||||
@@ -327,7 +499,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 18,
|
"lineNumber" : 18,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -357,7 +530,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 16,
|
"lineNumber" : 16,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -365,6 +539,216 @@
|
|||||||
"targetState" : "START",
|
"targetState" : "START",
|
||||||
"event" : "AUDIT_EVENT"
|
"event" : "AUDIT_EVENT"
|
||||||
} ]
|
} ]
|
||||||
|
}, {
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
},
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
},
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
},
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
},
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
},
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : {
|
||||||
|
"paymentId" : "String"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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,
|
||||||
|
"payloadTypes" : { }
|
||||||
|
},
|
||||||
|
"contextMachineId" : "paymentId",
|
||||||
|
"matchedTransitions" : null
|
||||||
} ],
|
} ],
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"default" : {
|
"default" : {
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 15,
|
"lineNumber" : 15,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
} ],
|
} ],
|
||||||
"entryPoints" : [ {
|
"entryPoints" : [ {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -57,7 +58,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 15,
|
"lineNumber" : 15,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 40,
|
"lineNumber" : 40,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "ORDER_EVENT",
|
"event" : "ORDER_EVENT",
|
||||||
"className" : "click.kamil.maven.core.MavenOrderStateMachine.OrderService",
|
"className" : "click.kamil.maven.core.MavenOrderStateMachine.OrderService",
|
||||||
@@ -19,7 +20,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 52,
|
"lineNumber" : 52,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
} ],
|
} ],
|
||||||
"entryPoints" : [ {
|
"entryPoints" : [ {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -106,7 +108,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 40,
|
"lineNumber" : 40,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 18,
|
"lineNumber" : 18,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
} ],
|
} ],
|
||||||
"entryPoints" : [ {
|
"entryPoints" : [ {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -69,7 +70,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 18,
|
"lineNumber" : 18,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25,
|
"lineNumber" : 25,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "eventProvider",
|
"event" : "eventProvider",
|
||||||
"className" : "click.kamil.service.StateMachineServiceImpl",
|
"className" : "click.kamil.service.StateMachineServiceImpl",
|
||||||
@@ -19,7 +20,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 50,
|
"lineNumber" : 50,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
}, {
|
}, {
|
||||||
"event" : "customMessage",
|
"event" : "customMessage",
|
||||||
"className" : "click.kamil.service.StateMachineServiceImpl",
|
"className" : "click.kamil.service.StateMachineServiceImpl",
|
||||||
@@ -29,7 +31,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 78,
|
"lineNumber" : 78,
|
||||||
"polymorphicEvents" : null
|
"polymorphicEvents" : null,
|
||||||
|
"payloadTypes" : { }
|
||||||
} ],
|
} ],
|
||||||
"entryPoints" : [ {
|
"entryPoints" : [ {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -144,7 +147,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25,
|
"lineNumber" : 25,
|
||||||
"polymorphicEvents" : [ ]
|
"polymorphicEvents" : [ ],
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -175,7 +179,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25,
|
"lineNumber" : 25,
|
||||||
"polymorphicEvents" : [ ]
|
"polymorphicEvents" : [ ],
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -206,7 +211,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25,
|
"lineNumber" : 25,
|
||||||
"polymorphicEvents" : [ ]
|
"polymorphicEvents" : [ ],
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -241,7 +247,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25,
|
"lineNumber" : 25,
|
||||||
"polymorphicEvents" : [ ]
|
"polymorphicEvents" : [ ],
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -276,7 +283,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 78,
|
"lineNumber" : 78,
|
||||||
"polymorphicEvents" : [ ]
|
"polymorphicEvents" : [ ],
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -311,7 +319,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 25,
|
"lineNumber" : 25,
|
||||||
"polymorphicEvents" : [ ]
|
"polymorphicEvents" : [ ],
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
@@ -346,7 +355,8 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 50,
|
"lineNumber" : 50,
|
||||||
"polymorphicEvents" : [ ]
|
"polymorphicEvents" : [ ],
|
||||||
|
"payloadTypes" : { }
|
||||||
},
|
},
|
||||||
"contextMachineId" : null,
|
"contextMachineId" : null,
|
||||||
"matchedTransitions" : [ {
|
"matchedTransitions" : [ {
|
||||||
|
|||||||
@@ -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,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,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,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,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();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user