Expand generic REST path variables into concrete command keys, filter entry points and call chains per machine, and tighten ambiguous routing and switch constraints so layered dispatchers link fewer false positives. Co-authored-by: Cursor <cursoragent@cursor.com>
Spring State Machine Explorer
Static analysis tool to extract and visualize Spring State Machines with deep code traceability.
Core Modules
state_machine_exporter: Java AST analyzer. Scans source code, resolves local Maven/Gradle dependencies, and extracts logic (Lambdas, Actions, Guards) into JSON/PUML.state_machine_exporter_html: Generates an interactive HTML portal with SVG diagrams and rich tooltips.
Usage
Analysis is source-first: the exporter reads project source and build metadata (pom.xml, settings.gradle) statically. It does not run Maven or Gradle against the project being analyzed.
1. Extract Metadata (JSON)
./gradlew :state_machine_exporter:run --args="-i ./my-project -o ./out -f json"
Common options:
| Flag | Description |
|---|---|
-i, --input |
Project directory to scan (defaults to .) |
-j, --json |
Re-render from an existing JSON export instead of scanning source |
-o, --output |
Output directory (default ./out) |
-f, --format |
Comma-separated formats: json, dot, puml, scxml |
-p, --profiles |
Spring profiles for property placeholder resolution |
--event, --state |
Enum formatting: fn (default), fqn, sn |
--debug |
Verbose analysis logging and unresolved chain diagnostics |
--no-diamonds |
Render choice/junction states as regular nodes (default: diamonds) |
--include-generated-sources |
Also scan target/ / build/ generated .java trees (default: on) |
--no-inline-accessors |
Disable accessor indexing/inlining (enabled by default) |
2. Generate Interactive Portal (HTML)
./gradlew :state_machine_exporter_html:run --args="-i ./my-project -o ./out_html"
HTML-specific options:
| Flag | Description |
|---|---|
-f, --flows |
Custom flows.json path |
-m, --machine |
Export only machines whose name contains this filter |
--open |
Open the generated portal in the default browser |
--no-metadata-pane |
Hide the left metadata pane (entry points, flows) |
--no-diamonds |
Render choice pseudostates as normal nodes |
--debug |
Same diagnostic mode as the core exporter |
--include-generated-sources |
Same generated-source scanning as the core exporter |
-p, --profiles |
Spring profiles for property resolution |
Business Flows
Define sequence of events in src/main/resources/flows.json:
[
{
"name": "Order Success",
"description": "Happy path for order placement",
"steps": ["PAY", "CHECK_AVAILABILITY->PENDING", "SHIP"]
}
]
JSON Structure
metadata.entryPoints: REST, WebFlux, and JMS entry points.metadata.callChains: Trace from API call to machine trigger (sendEvent).transitions.actions[].internalLogic: Extracted source code/lambdas.metadata.properties: Dictionary of all detected Spring profiles.
Static analysis: accessor inlining
During scan, the exporter builds an accessor index of trivial getters, setters, record components, and constant-returning methods. At analysis time, AccessorResolver uses this index first (O(1) lookup), then falls back to constructor/field tracing and method-body evaluation.
This improves resolution of DTO/event chains such as request.getPayload().getType() without running Maven or Gradle on the target project.
# Enabled by default; disable for debugging or A/B comparison
./gradlew :state_machine_exporter:run --args="-i ./my-project -o ./out --no-inline-accessors"
Pipeline components live under analysis/pipeline/ (AccessorResolver, ResolutionBudget, MethodInvocationUnwrapper, FieldInitializerFinder, LibraryUnwrapRegistry).