enrichment schema 2
This commit is contained in:
@@ -39,3 +39,25 @@
|
||||
- [ ] Add support for `@Value` tracking in fields and constructors.
|
||||
- [ ] Implement constant resolution for cross-class references in annotations.
|
||||
- [ ] Integrate value propagation into the call graph analysis (linking injected values to `sendEvent` arguments).
|
||||
|
||||
## Phase 8: Advanced String Expression Resolution
|
||||
- [ ] Implement `ValueResolver` to handle string concatenations and variable references.
|
||||
- [ ] Support cross-class constant lookup using JDT.
|
||||
- [ ] Update all Enrichers (MVC, Rabbit, JMS) to use `ValueResolver` for metadata extraction.
|
||||
- [ ] Verify with complex path patterns in the integration test suite.
|
||||
|
||||
## Phase 9: Instance Identification & Persistence Mapping
|
||||
- [ ] Implement `InstanceIdentifier` to track State Machine IDs (via `@Qualifier`, bean names, or factories).
|
||||
- [ ] Detect persistence restoration logic (`persister.restore`).
|
||||
- [ ] Update all Enrichers to include `stateMachineId` and `isRestoredFromPersistence` in metadata.
|
||||
- [ ] Verify multi-SM identification in the integration test suite.
|
||||
|
||||
## Phase 10: Incoming Payload Analysis
|
||||
- [ ] Implement payload type extraction for REST and Message entry points.
|
||||
- [ ] Detect usage of payload fields in `sendEvent` or `restore` calls.
|
||||
- [ ] Add `payloadType` to `EntryPoint` and `TriggerPoint` models.
|
||||
|
||||
## Phase 11: Interceptor & Filter Mapping
|
||||
- [ ] Implement detection of `HandlerInterceptor` and `Filter` implementations.
|
||||
- [ ] Map interceptors to endpoints using `WebMvcConfigurer` analysis.
|
||||
- [ ] Provide "Interceptor Context" metadata for each mapped endpoint.
|
||||
|
||||
42
plan-extended-anaylis/advanced_context.md
Normal file
42
plan-extended-anaylis/advanced_context.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Advanced Context Extraction: Payloads and Interceptors
|
||||
|
||||
## 1. Incoming Payload Analysis
|
||||
To understand *what* data drives the state machine, we need to capture metadata about the incoming parameters at entry points.
|
||||
|
||||
### REST Endpoints
|
||||
For methods annotated with `@PostMapping`, `@PutMapping`, etc.:
|
||||
1. **Extract Parameters**: Scan method parameters for `@RequestBody`, `@PathVariable`, or `@RequestParam`.
|
||||
2. **Type Extraction**: Capture the FQN of the parameter type (e.g., `com.example.OrderRequest`).
|
||||
3. **Data Linkage**: If a field from this payload is used in a `sendEvent(payload.getEvent())` call, mark the transition as "Dynamic based on Payload".
|
||||
|
||||
### Message Listeners
|
||||
For `@RabbitListener` or `@JmsListener`:
|
||||
1. Identify the parameter receiving the message body.
|
||||
2. Extract its type and any `@Header` or `@Payload` annotations.
|
||||
|
||||
## 2. Interceptor and Filter Analysis
|
||||
Interceptors often perform cross-cutting logic (authentication, context injection) that affects how state machines are driven.
|
||||
|
||||
### Discovery Strategy
|
||||
1. **Find Interceptors**: Scan for classes implementing `HandlerInterceptor` or `WebRequestInterceptor`.
|
||||
2. **Registration Mapping**: Look for `WebMvcConfigurer.addInterceptors()` calls to identify which URL patterns each interceptor applies to.
|
||||
3. **Context Injection**: Analyze if the interceptor modifies the request (e.g., `request.setAttribute("userContext", ...)`) or populates a `ThreadLocal` (like `SecurityContextHolder`).
|
||||
|
||||
### Linking to State Machines
|
||||
If a controller uses a value from a known Interceptor-injected context to decide which event to send or which state machine to restore, we can link the Interceptor's logic to the State Machine flow.
|
||||
|
||||
## 3. Implementation in `EnrichmentService`
|
||||
We will introduce a `ContextEnricher` that:
|
||||
1. Builds a map of **Active Interceptors** per path.
|
||||
2. Augments `EntryPoint` metadata with **Payload Type** information.
|
||||
|
||||
## 4. Modeling Updates
|
||||
Update `EntryPoint` and `TriggerPoint` to include `payloadType` and `interceptors`.
|
||||
|
||||
```java
|
||||
public record EntryPoint(
|
||||
// ... existing fields ...
|
||||
String payloadType,
|
||||
List<String> activeInterceptors
|
||||
) {}
|
||||
```
|
||||
@@ -39,6 +39,13 @@ The State Machine Exporter now becomes a "Consumer" of intelligence.
|
||||
- **Maintainability**: Adding support for a new framework (e.g., Micronaut) only requires updating the `IntelligenceProvider`, not touching the core state machine logic.
|
||||
- **Performance**: We can run the SM Parser and the Intelligence Scanner in parallel since they are now independent.
|
||||
|
||||
## 5. Monorepo and Multi-Module Support
|
||||
Applications are often split into multiple modules (e.g., `core`, `api`, `workers`).
|
||||
- **Workspace Scanning**: The analyzer should treat the entire monorepo as a single codebase context.
|
||||
- **Source Tracking**: Each metadata item (`TriggerPoint`, `EntryPoint`) includes a `sourceModule` identifier to show exactly where it was found.
|
||||
- **Cross-Module Resolution**: Properties defined in one module's `application.yml` and used in another should be resolved globally.
|
||||
- **Internal Dependency Following**: If the analyzer finds a call to a method in another module, it should continue the call-chain analysis into that module's source.
|
||||
|
||||
## Challenges
|
||||
- **Multiple State Machines**: How to know which `StateMachine` instance is being used?
|
||||
- Initial heuristic: If there's only one, assume it's that one.
|
||||
|
||||
73
plan-extended-anaylis/instance_identification.md
Normal file
73
plan-extended-anaylis/instance_identification.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# State Machine Instance Identification & Persistence Mapping
|
||||
|
||||
## 1. The Multi-SM Problem
|
||||
In large applications, multiple State Machines often coexist. A "Trigger Point" (like a REST Controller) must be linked to the correct State Machine definition.
|
||||
|
||||
**Common Identification Patterns**:
|
||||
- **Unique Types**: `StateMachine<OrderState, OrderEvent>` vs `StateMachine<UserState, UserEvent>`.
|
||||
- **Bean Qualifiers**: `@Qualifier("orderStateMachine")`.
|
||||
- **Factory IDs**: `factory.getStateMachine("order-123")`.
|
||||
- **Persistence Restoration**: `persister.restore(sm, orderId)`.
|
||||
|
||||
## 2. Analysis Strategy: "Instance Tracking"
|
||||
|
||||
### 1. Definition Discovery (Existing)
|
||||
Identify all configurations (classes with `@EnableStateMachineFactory` or `@EnableStateMachine`). Store their **Bean Names** and **Generic Types**.
|
||||
|
||||
### 2. Dependency Analysis
|
||||
When a class (e.g., `OrderController`) uses a state machine:
|
||||
1. **Identify the Field/Parameter**: Look for `StateMachine<S, E>`.
|
||||
2. **Resolve Generic Types**: Match `S` and `E` against known SM definitions.
|
||||
3. **Resolve Qualifiers**: Check for `@Qualifier` or variable names that match a SM bean name.
|
||||
|
||||
### 3. Loading & Persistence Analysis
|
||||
A dedicated "Loading Detector" will look for persistence logic.
|
||||
|
||||
**Pattern: Persister Restore**
|
||||
```java
|
||||
persister.restore(stateMachine, id);
|
||||
stateMachine.sendEvent(E);
|
||||
```
|
||||
**Static Strategy**:
|
||||
- Find calls to `Persister.restore(sm, ...)` or `PersistStateChangeListener`.
|
||||
- Link the variable `sm` to the restoration event.
|
||||
- Mark the `TriggerPoint` as "Restored from Persistence".
|
||||
|
||||
**Pattern: Factory Creation**
|
||||
```java
|
||||
StateMachine sm = factory.getStateMachine(smId);
|
||||
```
|
||||
**Static Strategy**:
|
||||
- Find `factory.getStateMachine(...)`.
|
||||
- If the argument is a literal (e.g., `"order"`), map it to the SM definition named "order".
|
||||
|
||||
## 3. Implementation: `InstanceIdentifier`
|
||||
We will introduce an `InstanceIdentifier` that works alongside the `ValueResolver`.
|
||||
|
||||
```java
|
||||
public class InstanceIdentifier {
|
||||
public StateMachineReference identify(VariableDeclaration var, CodebaseContext context) {
|
||||
// 1. Check type generics
|
||||
// 2. Check @Qualifier
|
||||
// 3. Trace back to factory or persister calls
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4. Modeling in `AnalysisResult`
|
||||
The `TriggerPoint` will be enhanced with a `stateMachineId` or `configFqn` field.
|
||||
|
||||
```java
|
||||
public record TriggerPoint(
|
||||
String className,
|
||||
String methodName,
|
||||
String event,
|
||||
String stateMachineId, // Links back to the specific SM
|
||||
boolean isRestoredFromPersistence,
|
||||
Map<String, String> metadata
|
||||
) {}
|
||||
```
|
||||
|
||||
## 5. Challenges
|
||||
- **Generic Controllers**: A single base controller that handles multiple SMs via generics. We might need to report "Multiple Potential SMs".
|
||||
- **Dynamic Factory IDs**: `factory.getStateMachine(payload.getType())`. Hard to resolve statically without data flow analysis.
|
||||
55
plan-extended-anaylis/polyglot_support.md
Normal file
55
plan-extended-anaylis/polyglot_support.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Polyglot Analysis: Supporting Mixed Java and Kotlin
|
||||
|
||||
## 1. The Need for Language Abstraction
|
||||
Professional JVM codebases are increasingly "Mixed Mode" (Java + Kotlin). Tying our analysis logic directly to JDT (Java AST) creates a bottleneck for Kotlin support.
|
||||
|
||||
## 2. The "Driver" Architecture
|
||||
We decouple the **Intelligence Logic** (e.g., "Find all REST controllers") from the **AST Parser** (e.g., "Walk the JDT tree").
|
||||
|
||||
### 1. Language Driver Interface
|
||||
```java
|
||||
public interface LanguageDriver {
|
||||
boolean supports(Path filePath);
|
||||
|
||||
// Unified Extraction Methods
|
||||
List<RawClassMetadata> extractClasses(Path filePath);
|
||||
List<RawMethodMetadata> extractMethods(RawClassMetadata cls);
|
||||
List<RawInvocationMetadata> findInvocations(RawMethodMetadata method, String targetMethodName);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Unified Metadata Model (Intermediate Representation)
|
||||
We move to a "Generic JVM Model" before performing the final state machine mapping.
|
||||
|
||||
```java
|
||||
public record RawClassMetadata(
|
||||
String fqn,
|
||||
String language, // "java", "kotlin"
|
||||
List<String> annotations,
|
||||
String superclass,
|
||||
List<String> interfaces
|
||||
) {}
|
||||
|
||||
public record RawMethodMetadata(
|
||||
String name,
|
||||
List<String> annotations,
|
||||
String returnType,
|
||||
List<String> parameterTypes
|
||||
) {}
|
||||
```
|
||||
|
||||
## 3. Polyglot Analysis Flow
|
||||
1. **File Discovery**: Find all `.java` and `.kt` files.
|
||||
2. **Driver Assignment**: Delegate each file to the appropriate `LanguageDriver`.
|
||||
3. **Cross-Language Resolution**: The `CodebaseContext` stores metadata for ALL classes.
|
||||
- A Java controller calling a Kotlin service is resolved by looking up the Kotlin class's unified metadata.
|
||||
4. **Enricher Execution**: Enrichers (like `SpringMvcEnricher`) now operate on the **Unified Metadata Model**, making them language-agnostic!
|
||||
|
||||
## 4. Why this is "Future Proof":
|
||||
- **Enrichers are written once**: The logic to find `@PostMapping` only needs to know how to query the `RawClassMetadata` annotations, not how to traverse JDT vs. Kotlin PSI.
|
||||
- **Easy Expansion**: To support Scala, Groovy, or even a newer Java version, you just add a new `LanguageDriver`.
|
||||
|
||||
## 5. Refactoring Plan
|
||||
1. **Define Intermediate Models**: Create `RawClassMetadata` and `RawMethodMetadata`.
|
||||
2. **Extract JDT Logic**: Move the current JDT-specific code into `JavaLanguageDriver`.
|
||||
3. **Kotlin PoC (Phase 12)**: Implement a basic `KotlinLanguageDriver` using Regex or a lightweight parser (like Tree-Sitter) to prove the abstraction works.
|
||||
@@ -38,21 +38,30 @@ public class AppProps {
|
||||
2. Index their fields with the prefix (e.g., `app.messaging.queueName`).
|
||||
3. When these beans are injected into a service, link the service's usage to the resolved property value.
|
||||
|
||||
## 3. Property Resolver (The "Config Scanner")
|
||||
A component dedicated to building a global map of available properties.
|
||||
## 3. Profile-Aware Property Resolver
|
||||
A component dedicated to building a multi-dimensional map of available properties.
|
||||
|
||||
1. **File Scanning**: Parse `application.properties`, `application.yml`, and `bootstrap.yml`.
|
||||
2. **Profile Support**: Handle `application-{profile}.yml` if a profile is active.
|
||||
3. **Property Map**: Create a `Map<String, String>` of all key-value pairs.
|
||||
1. **File Scanning**: Parse `application.properties`, `application.yml`, and `bootstrap.yml`.
|
||||
2. **Profile Identification**:
|
||||
- From filenames: `application-{profile}.properties`.
|
||||
- From YAML documents: `spring.config.activate.on-profile` (Spring Boot 2.4+).
|
||||
3. **The Multi-Profile Map**:
|
||||
- Instead of one map, we store: `Map<ProfileName, Map<Key, Value>>`.
|
||||
- The "default" profile is the base.
|
||||
|
||||
## 4. Value Propagation (Data Flow)
|
||||
Once a value is resolved or its placeholder is identified, we track its usage.
|
||||
## 4. Smart Value Propagation
|
||||
When a trigger uses a property, we want to know it's profile-dependent.
|
||||
|
||||
**Example Trace**:
|
||||
1. `application.yml` -> `app.queue: "orders"`
|
||||
2. `OrderService` -> `@Value("${app.queue}") String q;` -> `this.queue = q;`
|
||||
3. `OrderService.send()` -> `sm.sendEvent(this.queue);`
|
||||
4. **Resolution**: The "Event" for this trigger is "orders".
|
||||
1. `application.yml` -> `app.queue: "orders"`
|
||||
2. `application-prod.yml` -> `app.queue: "orders-prod"`
|
||||
3. `TriggerPoint` stores:
|
||||
- `placeholder: "${app.queue}"`
|
||||
- `defaultResolvedValue: "orders"`
|
||||
4. **Rendering Strategy**: The renderer can show "orders" by default, but provide a "Profile Toggle" to switch to "prod" and see the labels update to "orders-prod".
|
||||
|
||||
## 5. Metadata Retention
|
||||
We should never "squash" profiles during analysis. The `CodebaseMetadata` will carry the full profile matrix to the exporter.
|
||||
|
||||
## 5. SpEL (Spring Expression Language) Lite
|
||||
Full SpEL support is hard for static analysis, but we can support "Common Patterns":
|
||||
|
||||
53
plan-extended-anaylis/string_resolution.md
Normal file
53
plan-extended-anaylis/string_resolution.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Advanced String and Variable Resolution Strategy
|
||||
|
||||
## 1. The Problem
|
||||
Annotations often use non-literal values for metadata:
|
||||
- **Constants**: `@PostMapping(ApiConstants.SUBMIT_PATH)`
|
||||
- **Concatenation**: `@RequestMapping(BASE + "/orders")`
|
||||
- **Inherited Variables**: Use of fields defined in base classes.
|
||||
|
||||
Simple `StringLiteral` extraction fails in these cases. We need a recursive **Expression Evaluator**.
|
||||
|
||||
## 2. Evaluation Logic
|
||||
|
||||
### Constant Resolution
|
||||
If an expression is a `SimpleName` or `QualifiedName`:
|
||||
1. Use `CodebaseContext.getTypeDeclaration()` to find the class where the variable is defined.
|
||||
2. Search for the `VariableDeclarationFragment`.
|
||||
3. If it has an initializer that is a literal or another evaluatable expression, resolve it.
|
||||
|
||||
### Concatenation (Infix Expressions)
|
||||
If an expression is an `InfixExpression` with the `+` operator:
|
||||
1. Recursively resolve the left operand.
|
||||
2. Recursively resolve the right operand.
|
||||
3. Concatenate the results if both are strings.
|
||||
|
||||
### Spring Expression Language (SpEL) in Annotations
|
||||
For values like `@Value("#{systemProperties['path.base'] + '/api'}")`:
|
||||
1. Identify the SpEL string.
|
||||
2. Use a "Lite" SpEL parser to extract keys.
|
||||
3. Match against the `profiles` map in `CodebaseMetadata`.
|
||||
|
||||
## 3. Implementation: `ValueResolver`
|
||||
We will introduce a central `ValueResolver` utility.
|
||||
|
||||
```java
|
||||
public class ValueResolver {
|
||||
public String resolveString(Expression expr, CodebaseContext context) {
|
||||
if (expr instanceof StringLiteral sl) return sl.getLiteralValue();
|
||||
if (expr instanceof InfixExpression ie) return resolveInfix(ie, context);
|
||||
if (expr instanceof Name name) return resolveVariable(name, context);
|
||||
// ... fallback to toString() or empty
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4. Challenges
|
||||
- **Circular Dependencies**: A + B, where B = A + C. We need a "visited" set to prevent infinite recursion.
|
||||
- **Runtime-only values**: Some values cannot be resolved statically (e.g., values from a DB). In these cases, we should return the placeholder or a "Runtime Value" marker.
|
||||
- **Method Calls**: `getPath() + "/orders"`. Resolving method return values is complex; initially, we will only support simple getters returning literals.
|
||||
|
||||
## 5. Integration
|
||||
1. Update `AstUtils` or create `ValueResolver`.
|
||||
2. Refactor `SpringMvcEnricher`, `RabbitMqEnricher`, and `JmsEnricher` to use the resolver for all path/queue/destination fields.
|
||||
3. Update `integration_test_state_machine` with complex path examples.
|
||||
Reference in New Issue
Block a user