transition enricher rabbit
This commit is contained in:
@@ -400,6 +400,15 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 50
|
"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" : [ {
|
"entryPoints" : [ {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -434,6 +443,21 @@
|
|||||||
"verb" : "POST"
|
"verb" : "POST"
|
||||||
},
|
},
|
||||||
"parameters" : [ ]
|
"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",
|
"type" : "JMS",
|
||||||
"name" : "JMS: order.process.queue",
|
"name" : "JMS: order.process.queue",
|
||||||
@@ -559,6 +583,40 @@
|
|||||||
"targetState" : "OrderState.CANCELLED",
|
"targetState" : "OrderState.CANCELLED",
|
||||||
"event" : "OrderEvent.CANCEL"
|
"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" : {
|
"entryPoint" : {
|
||||||
"type" : "JMS",
|
"type" : "JMS",
|
||||||
|
|||||||
@@ -18,6 +18,15 @@
|
|||||||
"stateMachineId" : null,
|
"stateMachineId" : null,
|
||||||
"sourceState" : null,
|
"sourceState" : null,
|
||||||
"lineNumber" : 50
|
"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" : [ {
|
"entryPoints" : [ {
|
||||||
"type" : "REST",
|
"type" : "REST",
|
||||||
@@ -52,6 +61,21 @@
|
|||||||
"verb" : "POST"
|
"verb" : "POST"
|
||||||
},
|
},
|
||||||
"parameters" : [ ]
|
"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",
|
"type" : "JMS",
|
||||||
"name" : "JMS: order.process.queue",
|
"name" : "JMS: order.process.queue",
|
||||||
@@ -177,6 +201,40 @@
|
|||||||
"targetState" : "OrderState.CANCELLED",
|
"targetState" : "OrderState.CANCELLED",
|
||||||
"event" : "OrderEvent.CANCEL"
|
"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" : {
|
"entryPoint" : {
|
||||||
"type" : "JMS",
|
"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);
|
String val = constantResolver.resolve(expr, context);
|
||||||
if (val == null) {
|
if (val == null) {
|
||||||
val = expr.toString(); // Fallback to raw string (handles Enums, 'new Class()', etc)
|
val = expr.toString(); // Fallback to raw string (handles Enums, 'new Class()', etc)
|
||||||
@@ -317,4 +325,41 @@ public class CallGraphBuilder {
|
|||||||
}
|
}
|
||||||
return (TypeDeclaration) parent;
|
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) {
|
private String extractEventFromMessageBuilder(Expression expr) {
|
||||||
|
if (expr instanceof CastExpression ce) {
|
||||||
|
return extractEventFromMessageBuilder(ce.getExpression());
|
||||||
|
}
|
||||||
|
|
||||||
if (expr instanceof SimpleName sn) {
|
if (expr instanceof SimpleName sn) {
|
||||||
String varName = sn.getIdentifier();
|
String varName = sn.getIdentifier();
|
||||||
MethodDeclaration enclosingMethod = findEnclosingMethod(expr);
|
MethodDeclaration enclosingMethod = findEnclosingMethod(expr);
|
||||||
@@ -218,6 +222,15 @@ public class GenericEventDetector {
|
|||||||
if (initializer[0] != null) {
|
if (initializer[0] != null) {
|
||||||
return extractEventFromMessageBuilder(initializer[0]); // recursive
|
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
|
// Quirky pattern 2: Generic Varargs
|
||||||
<T extends IEvent> void sendEventsVarargs(T... events);
|
<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)
|
// Quirky pattern 3: Multiple generic bounds (Intersection Types)
|
||||||
<T extends IEvent & java.io.Serializable & Cloneable> T processQuirkyEvent(T event);
|
<T extends IEvent & java.io.Serializable & Cloneable> T processQuirkyEvent(T event);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,12 +54,21 @@ public class StateMachineServiceImpl implements StateMachineService {
|
|||||||
@Override
|
@Override
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public final <T extends IEvent> void sendEventsVarargs(T... events) {
|
public final <T extends IEvent> void sendEventsVarargs(T... events) {
|
||||||
// Quirky pattern 2: Iterating over generic varargs
|
|
||||||
for (T event : events) {
|
for (T event : events) {
|
||||||
sendMessage(event);
|
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
|
@Override
|
||||||
public <T extends IEvent & java.io.Serializable & Cloneable> T processQuirkyEvent(T event) {
|
public <T extends IEvent & java.io.Serializable & Cloneable> T processQuirkyEvent(T event) {
|
||||||
// Quirky pattern 3: Multiple bounds, returning the same generic type
|
// Quirky pattern 3: Multiple bounds, returning the same generic type
|
||||||
|
|||||||
@@ -24,5 +24,9 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-activemq</artifactId>
|
<artifactId>spring-boot-starter-activemq</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</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