Files
spring-state-machine-renderer/plan-extended-anaylis/implementation_details.md

103 lines
3.8 KiB
Markdown

# Implementation Details: Trigger Detection with JDT
## 1. Finding `sendEvent` Calls
We can use an `ASTVisitor` to find all `MethodInvocation` nodes.
```java
public class SendEventVisitor extends ASTVisitor {
@Override
public boolean visit(MethodInvocation node) {
if ("sendEvent".equals(node.getName().getIdentifier())) {
// Found a call!
// 1. Extract event (first argument)
// 2. Identify the enclosing method and class
}
return super.visit(node);
}
}
```
### Challenges in Event Extraction
Events can be:
- Enum constants: `Events.SUBMIT`
- Strings: `"SUBMIT"`
- Variables: `sm.sendEvent(eventFromPayload)` (Hard to resolve statically)
We should reuse `CodebaseContext.resolveState` logic (which is basically resolving an expression to a value/fqn).
## 2. Identifying Enclosing Context
Once a `sendEvent` is found, we can traverse up the AST to find the `MethodDeclaration` and `TypeDeclaration`.
```java
ASTNode parent = node.getParent();
while (parent != null && !(parent instanceof MethodDeclaration)) {
parent = parent.getParent();
}
// parent is now the MethodDeclaration
```
### 2. Extracting Annotations
From `MethodDeclaration`, we can check for mappings:
- `@PostMapping`, `@GetMapping`, etc.
- `@KafkaListener`: Extract `topics`, `groupId`.
- `@RabbitListener`: Extract `queues`, `bindings` (Exchange/RoutingKey).
From `TypeDeclaration`, we can check for:
- `@RestController`, `@Controller`
- `@RequestMapping` at class level (to get base path)
## 3. Specialized WebFlux Analysis
### Annotation-based WebFlux
This is largely identical to Spring MVC. The challenge is if the `sendEvent` is wrapped in a reactive operator.
```java
public Mono<Void> submit(Order order) {
return service.save(order)
.doOnNext(o -> stateMachine.sendEvent(Events.SUBMIT))
.then();
}
```
**Static Strategy**: We need to look inside `LambdaExpression` nodes passed to reactive operators (`doOnNext`, `flatMap`, `map`, `subscribe`). The `ASTVisitor` should traverse into these lambdas.
### Functional WebFlux (RouterFunctions)
Functional endpoints are often defined as Beans returning `RouterFunction`.
```java
@Bean
public RouterFunction<ServerResponse> route(OrderHandler handler) {
return RouterFunctions.route(POST("/orders"), handler::submitOrder);
}
```
**Static Strategy**:
1. Find methods returning `RouterFunction`.
2. Analyze the `MethodInvocation` chain (`route`, `andRoute`, `nest`).
3. Extract the URI pattern and the `HandlerFunction` reference.
4. If the handler is a method reference (`handler::submitOrder`), link it to the corresponding `MethodDeclaration`.
## 4. Specialized RabbitMQ Analysis
`@RabbitListener` can be complex:
```java
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "orderQueue", durable = "true"),
exchange = @Exchange(value = "orderExchange"),
key = "order.created"
))
public void onOrder(Order order) { ... }
```
**Static Strategy**:
1. Find `@RabbitListener`.
2. If it has `bindings`, drill down into `@QueueBinding`, `@Queue`, `@Exchange` to extract the topology.
3. If it only has `queues`, resolve the queue name (might be a SpEL expression or property placeholder, which we can try to resolve or just keep as-is).
## 5. Indirect Flow Detection (The "Service Link")
If the project has multiple state machines, we need to know which one is being targeted.
Usually, this is done via:
- Autowiring by type: `StateMachine<States, Events> sm;`
- Autowiring by name: `@Qualifier("mySm") StateMachine sm;`
We can look at the fields of the class where `sendEvent` is called.
## 6. Output Enhancement
The `Exporter` should be modified to:
- In DOT: Add nodes for Endpoints/Listeners and link them to the Events/Transitions.
- In SCXML: Add metadata to transitions.