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);
}
Related
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.
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;
}
I have created an Osgi service. I want to create a new instance of my service each time the service request comes.
Code look likes this -
#Component(immediate=true)
#Service(serviceFactory = true)
#Property(name = EventConstants.EVENT_TOPIC, value = {DEPLOY, UNDEPLOY })
public class XyzHandler implements EventHandler {
private Consumer consumer;
public static setConsumer(Consumer consumer) {
this.consumer = consumer;
}
#Override
public void handleEvent(final Event event) {
consumer.notify();
}
}
public class Consumer {
private DataSourceCache cache;
public void notify() {
updateCache(cache);
System.out.println("cache updated");
}
public void updateCache(DataSourceCache cache) {
cache = null;
}
}
In my Consumer class, I want to access the service instance of XyzHandler & set the attribute consumer. Also I would like to have a new service instance of XyzHandler created every time for each request.
I found few articles where it is mentioned that using osgi declarative service annotations this can be achieved.
OSGi how to run mutliple instances of one service
But I want to achieve this without using DS 1.3.
How can I do this without using annotations or how can it be done using DS 1.2?
To me this looks like a case of having asked a question based on what you think the answer is rather than describing what you're trying to achieve. If we take a few steps back then a more elegant solution exists.
In general injecting objects into stateful services is a bad pattern in OSGi. It forces you to be really careful about the lifecycle, and risks memory leaks. From the example code it appears as though what you really want is for your Consumer to get notified when an event occurs on an Event Admin topic. The easiest way to do this would be to remove the XyzHandler from the equation and make the Consumer an Event Handler like this:
#Component(property= { EventConstants.EVENT_TOPIC + "=" + DEPLOY,
EventConstants.EVENT_TOPIC + "=" + UNDEPLOY})
public class Consumer implements EventHandler {
private DataSourceCache cache;
#Override
public void handleEvent(final Event event) {
notify();
}
public void notify() {
updateCache(cache);
System.out.println("cache updated");
}
public void updateCache(DataSourceCache cache) {
cache = null;
}
}
If you really don't want to make your Consumer an EventHandler then it would still be easier to register the Consumer as a service and use the whiteboard pattern to get it picked up by a single XyzHandler:
#Component(service=Consumer.class)
public class Consumer {
private DataSourceCache cache;
public void notify() {
updateCache(cache);
System.out.println("cache updated");
}
public void updateCache(DataSourceCache cache) {
cache = null;
}
}
#Component(property= { EventConstants.EVENT_TOPIC + "=" + DEPLOY,
EventConstants.EVENT_TOPIC + "=" + UNDEPLOY})
public class XyzHandler implements EventHandler {
// Use a thread safe list for dynamic references!
private List<Consumer> consumers = new CopyOnWriteArrayList<>();
#Reference(cardinality=MULTIPLE, policy=DYNAMIC)
void addConsumer(Consumer consumer) {
consumers.add(consumer);
}
void removeConsumer(Consumer consumer) {
consumers.remove(consumer);
}
#Override
public void handleEvent(final Event event) {
consumers.forEach(this::notify);
}
private void notify(Consumer consumer) {
try {
consumer.notify();
} catch (Exception e) {
// TODO log this?
}
}
}
Using the whiteboard pattern in this way avoids you needing to track which XyzHandler needs to be created/destroyed when a bundle is started or stopped, and will keep your code much cleaner.
It sounds like your service needs to be a prototype scope service. This was introduced in Core R6. DS 1.3, from Compendium R6, includes support for components to be prototype scope services.
But DS 1.2 predates Core R6 and thus has no knowledge or support for prototype scope services.
Is there any way to get the number and some identification information of already created entities of particular Prototype-bean in Spring application?
Addition. In our project we have more then 400 prototype-beans and I would like to trace the state what beans were created during execution and the number of entities of each type.
I have found a way to see the actual picture about created prototype-beans.
I use free VisualVM memory profiler.
In the Sampler tab you can see all instances of created classes including singleton and prototype beans.
You'll see the names of your own packages and classes. In this case:
prototype is a package with my prototype-beans.
singleton is a package with my singleton-beans.
newclasses is a package with classes that I created by new operator.
Also after the garbage collector will clean up the memory you will see the result here.
you can do it by Publish and Listen Application Events.
create you own event.
when prototype bean was created send event from it.
create count ApplicationListener , and listen to income creation event.
here is example
Spring – Publish and Listen Application Events
Spring does not manage the complete lifecycle of a prototype bean: the container instantiates, configures, decorates and otherwise assembles a prototype object, hands it to the client and then has no further knowledge of that prototype instance.
Simple variant :
public class PrototypeCreationEvent extends ApplicationEvent {
private String beanName;
public PrototypeCreationEvent(Object source , String beanName) {
super(source);
this.beanName = beanName;
}
public String getBeanName(){
return beanName;
}
}
public class PrototypeCreationListener implements ApplicationListener<PrototypeCreationEvent> {
private ConcurrentMap<String,AtomicInteger> prototypeCreationStatistic = new ConcurrentHashMap<>();
//or from guava AtomicLongMap prototypeCreationStatistic = AtomicLongMap.create();
#Override
public void onApplicationEvent(PrototypeCreationEvent event) {
prototypeCreationStatistic.computeIfAbsent(event.getBeanName() , k->new AtomicInteger(0)).incrementAndGet();
System.out.println(event);
}
public ConcurrentMap<String,AtomicInteger> getPrototypeCreationStatistic(){
return prototypeCreationStatistic;
}
}
public abstract class PrototypeCreationPublisher implements BeanNameAware , ApplicationEventPublisherAware ,InitializingBean {
private String beanName;
private ApplicationEventPublisher applicationEventPublisher;
#Override
public void setBeanName(String name) {
this.beanName = name;
}
#Override
public void afterPropertiesSet() throws Exception {
System.out.println();
}
#Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
#PostConstruct //or use interface InitializingBean
public void sendEventAfterCreation() throws Exception {
applicationEventPublisher.publishEvent(new PrototypeCreationEvent(this , beanName));
}
}
#Component(value = BeanDefinition.SCOPE_PROTOTYPE)
public class PrototypeA extends PrototypeCreationPublisher{
}
#Component(value = BeanDefinition.SCOPE_PROTOTYPE)
public class PrototypeB extends PrototypeCreationPublisher{
}
example :
PrototypeA prototypeA1 = context.getBean(PrototypeA.class);
PrototypeA prototypeA2 = context.getBean(PrototypeA.class);
PrototypeA prototypeA3 = context.getBean(PrototypeA.class);
PrototypeB prototypeB1 = context.getBean(PrototypeB.class);
PrototypeCreationListener statistic = context.getBean(PrototypeCreationListener.class);
statistic.getPrototypeCreationStatistic().entrySet().forEach(s->{
System.out.println(s.getKey() + " count = "+s.getValue());
});
result :
PrototypeB count = 1
PrototypeA count = 3
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.