Event handlers in a separate class Axon 3.0.3 - java

I've got a fairly simple CQRS setup here using Axon & Spring.
This is the configuration class.
#AnnotationDriven
#Configuration
public class AxonConfig {
#Bean
public EventStore eventStore() {
...
}
#Bean
public CommandBus commandBus() {
return new SimpleCommandBus();
}
#Bean
public EventBus eventBus() {
return new SimpleEventBus();
}
}
This is my Aggregate...
#Aggregate
public class ThingAggregate {
#AggregateIdentifier
private String id;
public ThingAggregate() {
}
public ThingAggregate(String id) {
this.id = id;
}
#CommandHandler
public handle(CreateThingCommand cmd) {
apply(new ThingCreatedEvent('1234', cmd.getThing()));
}
#EventSourcingHandler
public void on(ThingCreatedEvent event) {
// this is called!
}
}
This is my EventHandler in a separate .java file...
#Component
public class ThingEventHandler {
private ThingRepository repository;
#Autowired
public ThingEventHandler(ThingRepository thingRepository) {
this.repository = conditionRepository;
}
#EventHandler
public void handleThingCreatedEvent(ThingCreatedEvent event) {
// this is only called if I publish directly to the EventBus
// apply within the Aggregate does not call it!
repository.save(event.getThing());
}
}
I'm using the CommandGateway to send the original creation command. My CommandHandler in the Aggregate receives the command fine, but when I call apply within my Aggregate, passing a new Event, my EventHandler in the external class, does not get called. Only EventHandlers directly inside the Aggregate class are called.
If I try and publish an Event directly to the EventBus, my external EventHandler is called.
Any idea why my EventHandler in an external java class is not being called when I call apply within the Aggregate?

In Axon 3, the Event Store is a replacement for the Event Bus. It is basically a specialized implementation that doesn't only forward events to subscribed, but also stores them.
In your configuration, you have both an Event Bus and an Event Store. The Aggregate's events are probably published to the Event Store. Since you receive events in your handler when publishing directly to the Event Bus, your handlers are subscribed there.
The solution: remove the Event Bus from your configuration and use the Event Store exclusively.

Related

Sending Spring domain events using a custom ApplicationEventPublisher

I'm trying to implement an application using Spring Boot/Spring Data, following DDD architecture guidelines. I have an Aggregate Root which publish domain events using the method AbstractAggregateRoot::registerEvent() . Furthermore, I need to intercept those events for Logging/Tracing purposes so I decided to make an experiment:
First, implement a custom ApplicationEvent Publisher
public class CustomEventPublisher implements ApplicationEventPublisher {
private final ApplicationEventPublisher publisher;
private final Logger logger = getLogger(CustomEventPublisher.class);
public CustomEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
#Override
public void publishEvent(ApplicationEvent event) {
logger.info("sending an event...");
publisher.publishEvent(event);
}
//.....
}
And then registering as bean
#Configuration
public class CustomEventPublisherConfig {
#Bean
#Primary
public ApplicationEventPublisher getCustomEventPublisher(ApplicationEventPublisher publisher , RabbitTemplate rabbitTemplate) {
return new CustomEventPublisher(publisher, rabbitTemplate);
}
}
this works fine once I explicitly publish events from some sample object with an injected ApplicationEventPublisher
public void pub() {
publisher.publishEvent(new Event(this , 1));
}
#EventListener
public void sub(Event e) {
this.value = e.getValue();
}
and I got the "sending an event..." log entry
then I've tried to define the aggregate root
#Entity
public class AggregateRoot extends AbstractAggregateRoot {
#Id
#GeneratedValue
private Long id;
private int value = 0;
public AggregateRoot setValue(int value) {
registerEvent(new Event(this , value));
return this;
}
}
and
public void pub() {
repository.save(new AggregateRoot().setValue(1));
}
Test pass again but I can clearly see that Spring Data is not using the CustomEventPublisher. I've tried to understand if there is some way to intercept repository.save() call and override the default behaviour, this approach could work even if needs to reinvent the wheel (I don't think that the domain event publishing code is so complicated though) but the only thing I've found is about Spring Data REST that is out of my scope
Any suggestion to overcome this problem?
Thanks in advance
As far as I know, Spring does not provide a way to replace the publisher used by EventPublishingRepositoryProxyPostProcessor. And it seems to me that you have chosen not quite the right path to get what you want to achieve, so I am not answering your direct question, but your requirements described in the beginning.
I would advise you to register a listener using #EventListener and handle your event there:
#EventListener
public void handleEvent(Event event) {
System.out.println(event);
}
Or you can use #TransactionalEventListener to bind the listener to a transaction phase:
#TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void handleEvent(Event event) {
System.out.println(event);
}

How can I ensure non concurrent execution for all derived commands/events spanned by an Axon Saga handler?

I have a Saga that creates a command which creates the Item aggregate.
I need to execute some other logic after the aggregates gets created so I've got a handler to deal with the ItemCreatedEvent.
It seems the ItemEventsHandler logic is not executed under the Saga instance locking. So I get another event associated to the Saga executing concurrently with the handler.
Is there a way to enforce the handler to be part of the same saga instance handling/execution?
Am I missing something?
Using axon 4.3.3 with spring boot.
#Saga
public class OrderSaga {
#Inject
private transient CommandGateway commandGateway;
#StartSaga
#SagaEventHandler(associationProperty = "executionId")
public void handle(OrderCreatedEvent event) {
event.items.forEach(item -> {
// Associate to Saga
SagaLifecycle.associateWith("itemId", item.id);
commandGateway.sendAndWait(new CreateItemCommand(event.groupId, event.schemaId, item));
});
}
}
#Aggregate
public class ItemAggregate {
#AggregateIdentifier
private String id;
private String groupId;
...
#CommandHandler
public ItemAggregate on(CreateItemCommand command) {
AggregateLifecycle.apply(new ItemCreatedEvent(
command.id,
command.groupId,
...));
}
#EventSourcingHandler
protected void on(ItemCreatedEvent event) {
this.id = event.id;
this.groupId = event.groupId;
...
}
}
#Service
public class ItemEventsHandler {
#EventHandler
protected void on(ItemCreatedEvent event) {
// This needs to be executed under the Saga locking
}
}
It turned out that the order wasn't relevant at the time ItemEventsHandler is handling the event.
The potential concurrency proven to be impossible as the second message is only produced after the first one has already been fully processed.
I've also found that in case of needing this non concurrent execution I could loop back the events to a Saga handler.

Why does my Spring #EventListener show different transactional behavior on event submission than when being called directly?

When using the #EventListener functionality with Spring Data's repositories the behavior is different than when calling the same code procedural.
My persistent objects publish events using the following base class:
public abstract class Aggregate {
#Transient
private transient final Set<Object> events = new LinkedHashSet<>();
protected <T> T registerEvent(T event) {
this.events.add(event);
return event;
}
#DomainEvents
Collection<Object> events() {
return Collections.unmodifiableSet(events);
}
#AfterDomainEventPublication
void clearEvents() {
this.events.clear();
}
}
My event listening class is implemented as follows:
class Service {
#EventListener
public void listener(SomeEvent event) {
someOtherRepository.save(someOtherPersistentObject);
someOtherCode();
}
}
When the listener is triggered and someOtherRepository's save(…) method fails a rollback will be issued. But someOtherCode() is executed regardless of the rollback.
But when I remove all #EventListening functionality and call the listener(…) method directly after the point where the originating repository is responsible for firing the event. Then I get a different behavior. Then someOtherCode() is never executed and the someOtherRepository.save(…) method fails immediately.
The original service responsible for publishing the event looks like this
public OriginatingService {
#Transactional
public void someMethod() {
originatingRepoDifferentFromSomeOtherRepo.save(something);
Why is this happening and is there a way to force the same behavior onto my event listening implementation?
Because writes to the database may be delayed until transaction commit i.e. when the transactional method returns.
Update as below to explicitly trigger an immediate flush:
#EventListener
public void listener(SomeEvent event) {
someOtherRepository.saveAndFlush(someOtherPersistentObject);
someOtherCode();
}

Calling Spring controller method without going to internet

tldr: Is there a way to make an internal request (using the method's path) without going to the internet?
--
Why do I need it? I have a project which receives many events. The decision of who will handle each event is made by a Controller. So I have something similar to this:
#RestController
#RequestMapping("/events")
public class EventHandlerAPI {
#Autowired
private EventAHandler eventAhandler;
#Autowired
private EventBHandler eventBhandler;
#PostMapping("/a")
public void handleEventA(#RequestBody EventA event) {
eventAhandler.handle(id, event);
}
#PostMapping("/b")
public void handleEventB(#RequestBody EventB event) {
eventBhandler.handle(id, event);
}
}
We recently added support to receive events through a Queue service. It sends to us the payload and the event class. Our decision is to let both interfaces working (rest and queue). The solution to avoid code duplication was to keep the Controller choosing which handler will take care of the event. The code nowadays is similar to this:
#Configuration
public class EventHandlerQueueConsumer {
#Autowired
private EventHandlerAPI eventHandlerAPI;
private Map<Class, EventHandler> eventHandlers;
#PostConstruct
public void init() {
/* start listen queue */
declareEventHandlers();
}
private void declareEventHandlers() {
eventHandlers = new HashMap<>();
eventHandlers.put(EventAHandler.class, (EventHandler<EventAHandler>) eventHandlerAPI::handleEventA);
eventHandlers.put(EventBHandler.class, (EventHandler<EventBHandler>) eventHandlerAPI::handleEventB);
}
private void onEventReceived(AbstractEvent event) {
EventHandler eventHandler = eventHandlers.get(event.getClass());
eventHandler.handle(event);
}
private interface EventHandler<T extends AbstractEvent> {
void handle(T event);
}
}
This code works, but it doesn't let the controller choose who will handle the event (our intention). The decision is actually being made by the map.
What I would like to do was to invoke the controller method through it's request mapping without going to the internet. Something like this:
#Configuration
public class EventHandlerQueueConsumer {
// MADE UP CLASS TO SHOW WHAT I WANT
#Autowired
private ControllerInkover controllerInvoker;
#PostConstruct
public void init() { /* start listen queue */ }
private void onEventReceived(AbstractEvent event) {
controllerInvoker.post(event.getPath(), new Object[] { event });
}
}
This way is much cleaner and let all the decisions be made by the controller.
I've researched a lot and didn't found a way to implement it. Debugging spring, I found how he routes the request after the DispatcherServlet, but all the spring internals uses HttpServletRequest and HttpServletResponse :(
Is there a way to make an internal request (using the method's path) without going to the internet?
They are classes of the same application
Then it should easy enough.
1) You can call your own API on http(s)://localhost:{port}/api/{path} using RestTemplate utility class. This is preferred way, since you'll follow standard MVC pattern. Something like:
restTemplate.exchange(uri, HttpMethod.POST, httpEntity, ResponseClass.class);
2) If you don't want to invoke network connection at all, then you can either use Spring's internal to find the mapping/method map or use some reflection to build custom
map upon controller's startup. Then you can pass your event/object to the method from the map in a way shown in your mock-up class. Something like:
#RequestMapping("foo")
public void fooMethod() {
System.out.println("mapping = " + getMapping("fooMethod")); // you can get all methods/mapping in #PostContruct initialization phase
}
private String getMapping(String methodName) {
Method methods[] = this.getClass().getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName() == methodName) {
String mapping[] = methods[i].getAnnotation(RequestMapping.class).value();
if (mapping.length > 0) {
return mapping[mapping.length - 1];
}
}
}
return null;
}

Is there a way to create a global listener to receive messages from a Java websocket?

I'm working with AngularJS, Ionic and Java as backend. I'm using websocket to create a realtime chat.
I want to set up a listener to receive all the messages anywhere in my app to handler it and keep it without nodejs.
Is there a way to do it that?
I don't think there is a well prepared way to meet such requirements in Java WebSocket API. (Although, it would be possible with JMX without a little bit of effort.) However you can simply do that by writing a parent endpoint class, having every endpoint class extend it like the following:
// Assumes you use some dependency injection framework
#Component
// Your own POJO to be notified of every socket events
public class WebSocketListener {
public void onOpen(String id) {
// Your logic here
}
public void onMessage(String id, Object message) {
// Your logic here
}
public void onClose(String id) {
// Your logic here
}
}
// A parent class to notify your own global listener
// Extend it if you want to listen socket's event through some specific path
public abstract class NotifiableEndpoint {
#Inject
private WebSocketListener listener;
#OnOpen
public void onOpen(Session session) {
listener.onOpen(session.getId());
}
#OnMessage
public void onMessage(Session session, StringOrByteBuffer data) {
listener.onOpen(session.getId(), data);
}
#OnClose
public void OnClose(Session session) {
listener.onOpen(session.getId());
}
}
// A concrete class to write your business logic
#ServerEndpoint("/hello")
public class YourEndpoint extends NotifiableEndpoint {
// Your logic
}
Not to use inheritance, reflection through ServerEndpointConfig.Configurator.getEndpointInstance might work.

Categories

Resources