transition enricher rabbit
This commit is contained in:
@@ -400,6 +400,15 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 50
|
||||
}, {
|
||||
"event" : "customMessage",
|
||||
"className" : "click.kamil.service.StateMachineServiceImpl",
|
||||
"methodName" : "sendCustomMessage",
|
||||
"sourceFile" : "service/src/main/java/click/kamil/service/StateMachineServiceImpl.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 69
|
||||
} ],
|
||||
"entryPoints" : [ {
|
||||
"type" : "REST",
|
||||
@@ -434,6 +443,21 @@
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "RABBIT",
|
||||
"name" : "RABBIT: order.custom.transition.queue",
|
||||
"className" : "click.kamil.web.RabbitOrderListener",
|
||||
"methodName" : "handleCustomTransition",
|
||||
"sourceFile" : "web/src/main/java/click/kamil/web/RabbitOrderListener.java",
|
||||
"metadata" : {
|
||||
"protocol" : "RABBIT",
|
||||
"destination" : "order.custom.transition.queue"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "payload",
|
||||
"type" : "String",
|
||||
"annotations" : [ ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "JMS",
|
||||
"name" : "JMS: order.process.queue",
|
||||
@@ -559,6 +583,40 @@
|
||||
"targetState" : "OrderState.CANCELLED",
|
||||
"event" : "OrderEvent.CANCEL"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "RABBIT",
|
||||
"name" : "RABBIT: order.custom.transition.queue",
|
||||
"className" : "click.kamil.web.RabbitOrderListener",
|
||||
"methodName" : "handleCustomTransition",
|
||||
"sourceFile" : "web/src/main/java/click/kamil/web/RabbitOrderListener.java",
|
||||
"metadata" : {
|
||||
"protocol" : "RABBIT",
|
||||
"destination" : "order.custom.transition.queue"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "payload",
|
||||
"type" : "String",
|
||||
"annotations" : [ ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.web.RabbitOrderListener.handleCustomTransition", "click.kamil.service.StateMachineServiceImpl.sendCustomMessage" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "OrderEvent.PROCESS",
|
||||
"className" : "click.kamil.service.StateMachineServiceImpl",
|
||||
"methodName" : "sendCustomMessage",
|
||||
"sourceFile" : "service/src/main/java/click/kamil/service/StateMachineServiceImpl.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 69
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "OrderState.NEW",
|
||||
"targetState" : "OrderState.PROCESSING",
|
||||
"event" : "OrderEvent.PROCESS"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "JMS",
|
||||
|
||||
@@ -18,6 +18,15 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 50
|
||||
}, {
|
||||
"event" : "customMessage",
|
||||
"className" : "click.kamil.service.StateMachineServiceImpl",
|
||||
"methodName" : "sendCustomMessage",
|
||||
"sourceFile" : "service/src/main/java/click/kamil/service/StateMachineServiceImpl.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 69
|
||||
} ],
|
||||
"entryPoints" : [ {
|
||||
"type" : "REST",
|
||||
@@ -52,6 +61,21 @@
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "RABBIT",
|
||||
"name" : "RABBIT: order.custom.transition.queue",
|
||||
"className" : "click.kamil.web.RabbitOrderListener",
|
||||
"methodName" : "handleCustomTransition",
|
||||
"sourceFile" : "web/src/main/java/click/kamil/web/RabbitOrderListener.java",
|
||||
"metadata" : {
|
||||
"protocol" : "RABBIT",
|
||||
"destination" : "order.custom.transition.queue"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "payload",
|
||||
"type" : "String",
|
||||
"annotations" : [ ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "JMS",
|
||||
"name" : "JMS: order.process.queue",
|
||||
@@ -177,6 +201,40 @@
|
||||
"targetState" : "OrderState.CANCELLED",
|
||||
"event" : "OrderEvent.CANCEL"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "RABBIT",
|
||||
"name" : "RABBIT: order.custom.transition.queue",
|
||||
"className" : "click.kamil.web.RabbitOrderListener",
|
||||
"methodName" : "handleCustomTransition",
|
||||
"sourceFile" : "web/src/main/java/click/kamil/web/RabbitOrderListener.java",
|
||||
"metadata" : {
|
||||
"protocol" : "RABBIT",
|
||||
"destination" : "order.custom.transition.queue"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "payload",
|
||||
"type" : "String",
|
||||
"annotations" : [ ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.web.RabbitOrderListener.handleCustomTransition", "click.kamil.service.StateMachineServiceImpl.sendCustomMessage" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "OrderEvent.PROCESS",
|
||||
"className" : "click.kamil.service.StateMachineServiceImpl",
|
||||
"methodName" : "sendCustomMessage",
|
||||
"sourceFile" : "service/src/main/java/click/kamil/service/StateMachineServiceImpl.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 69
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "OrderState.NEW",
|
||||
"targetState" : "OrderState.PROCESSING",
|
||||
"event" : "OrderEvent.PROCESS"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "JMS",
|
||||
|
||||
@@ -216,6 +216,14 @@ public class CallGraphBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
// Extract from constructor args (e.g., new CustomMessage(OrderEvent.PROCESS, ...))
|
||||
expr = traceVariable(expr);
|
||||
if (expr instanceof ClassInstanceCreation cic) {
|
||||
if (!cic.arguments().isEmpty()) {
|
||||
expr = (Expression) cic.arguments().get(0);
|
||||
}
|
||||
}
|
||||
|
||||
String val = constantResolver.resolve(expr, context);
|
||||
if (val == null) {
|
||||
val = expr.toString(); // Fallback to raw string (handles Enums, 'new Class()', etc)
|
||||
@@ -317,4 +325,41 @@ public class CallGraphBuilder {
|
||||
}
|
||||
return (TypeDeclaration) parent;
|
||||
}
|
||||
private Expression traceVariable(Expression expr) {
|
||||
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 traceVariable(initializer[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
private MethodDeclaration findEnclosingMethod(ASTNode node) {
|
||||
ASTNode parent = node.getParent();
|
||||
while (parent != null && !(parent instanceof MethodDeclaration)) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
return (MethodDeclaration) parent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,6 +193,10 @@ public class GenericEventDetector {
|
||||
}
|
||||
|
||||
private String extractEventFromMessageBuilder(Expression expr) {
|
||||
if (expr instanceof CastExpression ce) {
|
||||
return extractEventFromMessageBuilder(ce.getExpression());
|
||||
}
|
||||
|
||||
if (expr instanceof SimpleName sn) {
|
||||
String varName = sn.getIdentifier();
|
||||
MethodDeclaration enclosingMethod = findEnclosingMethod(expr);
|
||||
@@ -218,6 +222,15 @@ public class GenericEventDetector {
|
||||
if (initializer[0] != null) {
|
||||
return extractEventFromMessageBuilder(initializer[0]); // recursive
|
||||
}
|
||||
|
||||
// If it has NO initializer, it might be a method parameter!
|
||||
for (Object paramObj : enclosingMethod.parameters()) {
|
||||
if (paramObj instanceof SingleVariableDeclaration svd) {
|
||||
if (svd.getName().getIdentifier().equals(varName)) {
|
||||
return varName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package click.kamil.domain;
|
||||
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class CustomStateMachineMessage<T, P> extends GenericMessage<T> {
|
||||
|
||||
private final P customPayload;
|
||||
|
||||
public CustomStateMachineMessage(T payload, P customPayload) {
|
||||
super(payload);
|
||||
this.customPayload = customPayload;
|
||||
}
|
||||
|
||||
public CustomStateMachineMessage(T payload, Map<String, Object> headers, P customPayload) {
|
||||
super(payload, headers);
|
||||
this.customPayload = customPayload;
|
||||
}
|
||||
|
||||
public P getCustomPayload() {
|
||||
return customPayload;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,9 @@ public interface StateMachineService {
|
||||
// Quirky pattern 2: Generic Varargs
|
||||
<T extends IEvent> void sendEventsVarargs(T... events);
|
||||
|
||||
// Child class of Spring's GenericMessage with a generic payload
|
||||
<T extends click.kamil.domain.OrderEvent, P> void sendCustomMessage(click.kamil.domain.CustomStateMachineMessage<T, P> customMessage);
|
||||
|
||||
// Quirky pattern 3: Multiple generic bounds (Intersection Types)
|
||||
<T extends IEvent & java.io.Serializable & Cloneable> T processQuirkyEvent(T event);
|
||||
}
|
||||
|
||||
@@ -54,12 +54,21 @@ public class StateMachineServiceImpl implements StateMachineService {
|
||||
@Override
|
||||
@SafeVarargs
|
||||
public final <T extends IEvent> void sendEventsVarargs(T... events) {
|
||||
// Quirky pattern 2: Iterating over generic varargs
|
||||
for (T event : events) {
|
||||
sendMessage(event);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends OrderEvent, P> void sendCustomMessage(click.kamil.domain.CustomStateMachineMessage<T, P> customMessage) {
|
||||
// Because CustomStateMachineMessage extends GenericMessage<T> and implements Message<T>,
|
||||
// we can pass it directly to the state machine as long as T resolves to OrderEvent!
|
||||
|
||||
// We cast it to Message<OrderEvent> to satisfy Mono's strict type requirements
|
||||
org.springframework.messaging.Message<OrderEvent> msg = (org.springframework.messaging.Message<OrderEvent>) customMessage;
|
||||
stateMachine.sendEvent(Mono.just(msg)).subscribe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IEvent & java.io.Serializable & Cloneable> T processQuirkyEvent(T event) {
|
||||
// Quirky pattern 3: Multiple bounds, returning the same generic type
|
||||
|
||||
@@ -24,5 +24,9 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-activemq</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package click.kamil.web;
|
||||
|
||||
import click.kamil.domain.CustomStateMachineMessage;
|
||||
import click.kamil.domain.OrderEvent;
|
||||
import click.kamil.service.StateMachineService;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class RabbitOrderListener {
|
||||
|
||||
private final StateMachineService stateMachineService;
|
||||
|
||||
// Constructor Injection
|
||||
public RabbitOrderListener(StateMachineService stateMachineService) {
|
||||
this.stateMachineService = stateMachineService;
|
||||
}
|
||||
|
||||
// Listens to RabbitMQ, constructs the custom class, triggers a state transition
|
||||
@RabbitListener(queues = "order.custom.transition.queue")
|
||||
public void handleCustomTransition(String payload) {
|
||||
System.out.println("Received RabbitMQ message: " + payload);
|
||||
|
||||
// Construct our custom event containing the generic payload and the enum state
|
||||
CustomStateMachineMessage<OrderEvent, String> customMessage =
|
||||
new CustomStateMachineMessage<>(OrderEvent.PROCESS, "My Rabbit Custom Payload: " + payload);
|
||||
|
||||
// Push the custom event into the state machine to perform the transition
|
||||
stateMachineService.sendCustomMessage(customMessage);
|
||||
System.out.println("Triggered PROCESS transition via custom message over RabbitMQ");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user