Java EE - CDI Event Observes - events

Why when I execute the event, it calls two methods that listen with different annotations?
Example:
#Inject
#EventTest
private Event<String> event;
event.fire("Hello!!!");
public void listener1(#Observes #EventTest String msg) {
System.out.println("listener1: " + msg);
}
public void listener2(#Observes #OtherEventTest String msg) {
System.out.println("listener2: " + msg);
}
==================================
the event is executed by calling the two listener methods, I don't understand why.

For this particular case, I was missing the following in the annotations:
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.PARAMETER, ElementType.FIELD })
Now it works correctly.

Related

Spring Cloud Stream - Consume Data on Demand manually?

Using Spring Data Stream, How can I start reading from a queue and stop reading on demand?
I want to so something like this:
#EnableBinding(Sink.class)
public class SomeConsumer {
#StreamListener(target = Sink.INPUT)
public void receiveMsg(Message<String> message)
{
logger.info(" received new message [" + message.toString() + "] ");
}
public static void startReceiving()
{
//How to implement this logic?
}
public static void stopReceiving()
{
//How to implement this logic?
}
}
It can't be done in a static method; autowire the BindingsEndpoint and use the changeState() method.
See my answer to this question.

Logging not working in web application using Spring AOP

using Spring AOP, I'm trying to put logging in my web application for an object called corelation like below :-
LoggingCorrelationEnrichingAspect.java:-
#Aspect
#Component
public class LoggingCorrelationEnrichingAspect {
private static final Logger logger = getLogger(LoggingCorrelationEnrichingAspect.class);
#Around("#annotation(Correlated)")
public Object wrapWithCorrelationContext(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
logger.info("Entering "+ proceedingJoinPoint.getSignature().getName() +" with Correlation Id:: "
+ ((Map)proceedingJoinPoint.getArgs()[0]).get(CommerceConnectorConstants.HttpHeaders.CORRELATION_ID).get());
return ((Mono<?>) proceedingJoinPoint.proceed());
}
}
Correlated.java:-
#Inherited
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface Correlated {}
In my main REST Controller operation, using #Correlated annotation, I'm trying to log this corellation like below :-
#Correlated
#GetMapping(path = "/products}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Mono<ProductBeanResponse> getProducts(
#RequestHeader(name = Test.HttpHeaders.TENANT_ID, required = true) UUID tId,
#RequestHeader(name = Test.HttpHeaders.CORRELATION_ID, required = true) UUID correlationId
----
---
}
However, when I test my service using PostMan tool and see the applicaiton logs, the corelation id is never logged :-
logger.info("Entering "+ proceedingJoinPoint.getSignature().getName() +" with Correlation Id:: "
+ ((Map)proceedingJoinPoint.getArgs()[0]).get(CommerceConnectorConstants.HttpHeaders.CORRELATION_ID).get());
Please advise is this a configuration issue in Spring AOP.
Thanks
This can get working in either of below two ways
Provide fully qualified name of Correlated in the pointcut definition as #Around("#annotation(com.x.y.z.Correlated)")
Update the Aspect method signature to include the Correlated as second argument
#Around("#annotation(correlated)")
public Object wrapWithCorrelationContext(ProceedingJoinPoint proceedingJoinPoint, Correlated correlated ) throws Throwable {
logger.info("Entering "+ proceedingJoinPoint.getSignature().getName() +" with Correlation Id:: "
+ ((Map)proceedingJoinPoint.getArgs()[0]).get(CommerceConnectorConstants.HttpHeaders.CORRELATION_ID).get());
return ((Mono<?>) proceedingJoinPoint.proceed());
}
Let know in comments if anything else is required.
P.S.: Also as pointed out by M. Deinum make sure to remove object cast.

Not able to to filter messages received using condition attribute in Spring Cloud Stream #StreamListener annotation

I am trying to create a event based system for communicating between services using Apache Kafka as Messaging system and Spring Cloud Stream Kafka.
I have written my Receiver class methods as below,
#StreamListener(target = Sink.INPUT, condition = "headers['eventType']=='EmployeeCreatedEvent'")
public void handleEmployeeCreatedEvent(#Payload String payload) {
logger.info("Received EmployeeCreatedEvent: " + payload);
}
This method is specifically to catch for messages or events related to EmployeeCreatedEvent.
#StreamListener(target = Sink.INPUT, condition = "headers['eventType']=='EmployeeTransferredEvent'")
public void handleEmployeeTransferredEvent(#Payload String payload) {
logger.info("Received EmployeeTransferredEvent: " + payload);
}
This method is specifically to catch for messages or events related to EmployeeTransferredEvent.
#StreamListener(target = Sink.INPUT)
public void handleDefaultEvent(#Payload String payload) {
logger.info("Received payload: " + payload);
}
This is the default method.
When I run the application, I am not able to see the methods annoated with condition attribute being called. I only see the handleDefaultEvent method being called.
I am sending a message to this Receiver Application from the Sending/Source App using the below CustomMessageSource class as below,
#Component
#EnableBinding(Source.class)
public class CustomMessageSource {
#Autowired
private Source source;
public void sendMessage(String payload,String eventType) {
Message<String> myMessage = MessageBuilder.withPayload(payload)
.setHeader("eventType", eventType)
.build();
source.output().send(myMessage);
}
}
I am calling the method from my controller in Source App as below,
customMessageSource.sendMessage("Hello","EmployeeCreatedEvent");
The customMessageSource instance is autowired as below,
#Autowired
CustomMessageSource customMessageSource;
Basicaly, I would like to filter messages received by the Sink/Receiver application and handle them accordingly.
For this I have used the #StreamListener annotation with condition attribute to simulate the behaviour of handling different events.
I am using Spring Cloud Stream Chelsea.SR2 version.
Can someone help me in resolving this issue.
It seems like the headers are not propagated. Make sure you include the custom headers in spring.cloud.stream.kafka.binder.headers http://docs.spring.io/autorepo/docs/spring-cloud-stream-docs/Chelsea.SR2/reference/htmlsingle/#_kafka_binder_properties .

Spring #Async error handling with database access

I'm able to intercept Async exceptions using the following class.
I need to register exceptions in a database,is there a way to use autowiring in this class ? It seems not to support it.
(Tried #Controller and #Service, does not work)
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Autowired
private IDBEventService dbEventService;
#Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
System.out.println("Exception message - " + throwable.getMessage());
System.out.println("Method name - " + method.getName());
for (Object param : obj) {
System.out.println("Parameter value - " + param);
}
dbEventService.recordEvent("Something happened");
}
}
A standard way to achieve what you are trying to do would be to log the exception and configure a database appender for the logger.

Is transactional #observes working for fired events on JBoss AS 7?

In order to use events only listened if a transaction succeeds or fails, I'm following the given doc about transactional observers :
http://docs.jboss.org/weld/reference/1.1.0.Final/en-US/html_single/#d0e4075
... but cannot manage to make my code work on JBoss AS7.
Here's my EJB:
#LocalBean
#Stateful
#TransactionAttribute(TransactionAttributeType.NEVER)
public class MyController
{
#Inject
private transient Event<MyEvent> myEventLauncher;
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void save()
{
myEventLauncher.fire(new MyEvent());
}
#AfterCompletion
protected void afterSave(boolean isCommitted)
{
// do stuff
}
}
And here my basic listener:
public class MyHandler
{
protected void listenMyEvent(#Observes(during=TransactionPhase.AFTER_SUCCESS) MyEvent event)
{
// do stuff
}
protected void listenMyEvent2(#Observes(during=TransactionPhase.AFTER_FAILURE) MyEvent event)
{
// do stuff
}
}
I can say I'm in a transaction when the event is fired, because the afterSave method of the EJB is called. Alas, the methods listenMyEvent and listenMyEvent2 are always called both, like if I was not in a transactional context.
I tried the same code on GlassFish 3 and it perfectly works, so I guess there is a problem with JBoss AS 7, but I cannot find any bug report about it.
Well, as my current tests made me think that transactional observers are not working in JBoss AS 7, I managed to do a workaround I gave here for people who are interested.
First, we need qualifier annotations: Immediate, AfterFailure and AfterSuccess.
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD, ElementType.PARAMETER })
public #interface AfterFailure
{}
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD, ElementType.PARAMETER })
public #interface AfterSuccess
{}
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD, ElementType.PARAMETER })
public #interface Immediate
{}
Also, three basic AnnotationLiteral to create in runtime instances of this three annotations.
Then, we need a encapsulator for our true events, that I named SpecialEvent.
public class SpecialEvent
{
private Object event; // the real event you want
public SpecialEvent(Object event)
{
super();
this.event = event;
}
public Object getEvent()
{
return event;
}
}
And at last, an observer for this special event and an interceptor for classes where you want to fire this kind of events (full explanation below).
#RequestScoped
public class SpecialEventObserver
{
#Inject
private Event<Object> anyEventFirer; // firer for real events
private List<Object> events; // queued events
public SpecialEventObserver()
{
events = new ArrayList<Object>();
}
// remove all queued events
public void reset()
{
this.events.clear();
}
public void fireAfterFailureEvents() throws Exception
{
this.fireAllEventsOnce(new AfterFailureLiteral());
}
public void fireAfterSuccessEvents() throws Exception
{
this.fireAllEventsOnce(new AfterSuccessLiteral());
}
protected void listenSpecialEvent(#Observes SpecialEvent specialEvent)
{
Object event = specialEvent.getEvent();
this.events.add(event);
this.fireEvent(event, new ImmediateLiteral());
}
protected void fireAllEventsOnce(Annotation qualifier) throws Exception
{
try
{
for (Object event : this.events)
{
this.fireEvent(event, qualifier);
}
}
catch (Exception e)
{
throw e;
}
finally
{
this.events.clear();
}
}
protected void fireEvent(Object event, Annotation qualifier)
{
Event eventFirer = anyEventFirer.select(event.getClass(), qualifier);
eventFirer.fire(event);
}
}
#Interceptor
#LocalInterception
public class MyInterceptor implements Serializable
{
#Inject
private SpecialEventObserver specialEventObserver;
#AroundInvoke
public Object intercept(InvocationContext ic) throws Exception
{
specialEventObserver.reset();
try
{
// call the real method
Object proceedResult = ic.proceed();
// real method succeeded, fire successful events
specialEventObserver.fireAfterSuccessEvents();
return proceedResult;
}
catch (Exception e)
{
// real method failed, fire failed events
specialEventObserver.fireAfterFailureEvents();
throw e;
}
}
}
The mechanism is quite simple:
When you want to fire an event, fire a SpecialEvent that hold the true event.
The SpecialEventObserver will catch any SpecialEvent and will immediately fire your own event with an Immediate qualifier. It will also queue the events for the after completion part.
At the end of your own method call (ic.proceed in the interceptor), MyInterceptor will ask the SpecialEventObserver either to fire again all events with a AfterFailure qualifier or a AfterSuccess qualifier, depending of the success of your method.
In place of #Observes(during=...), your own observers have to observe events with the right qualifier, like #Observes #Immediate, #Observes #AfterFailure or #Observes #AfterSuccess.
The behavior is not exactly the one that provides the native #Observes(during=...). The after completion part is not based on the transaction state, but on your own method call success:
In JaveEE6, transactional observers on after success or after failure phases must be immediately called if you're not in a transaction, like a IN_PROGRESS would do.
In this workaround, observers on after success or after failure phases will always be called at the end of the method, and only if it succeeded or failed.
This works with version 7.1.0.Final which is supposedly (-> with Jboss you never know) fully Java EE compliant. Also your bean is not thread-safe as it uses list instead of a concurrent queue.
Your observer methods need REQUIRES_NEW, as stated here :
http://www.seamframework.org/Documentation/WhyIsThereNoActiveTransactionInMySFSBTransactionalObserver

Resources