Custom RabbitMessageListner Throws Wrapped Exception - spring-amqp

I have custom MessageListener from the custom lib
import org.springframework.amqp.core.*
public class Subscriber {
private SimpleMessageListenerContainer simpleMessageListenerContainer;
private ConnectionFactory rabbitConnectionFactory;
public final class ScruberMessageListner implements MessageListener {
#Override
public void onMessage(Message message) {
// TODO Auto-generated method stub
}
}
}
When I create a Subscriber, the custom lib will care of starting the Container and managing listener and many more added functionality.
#Configuration
public class BrokerSubscriberConfiguration {
#Bean
public Subscriber treatmentEventSubscriber(EventConsumer treatmentEventConsumer) {
return Subscriber.createInitialisedSubscriber(treatmentEventConsumer, configurations,
null, true);
}
}
#SpringBootApplication
#ComponentScan(basePackages = "com.fin.bc")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application loads the bring up message listener but it throws exception upon receiving the message.
LogMessage :
SimpleConnection#2a76b80a [delegate=amqp://admin#10.15.190.17:5672/hydra.services, localPort= 50786], acknowledgeMode=AUTO local queue size=0
2020-05-08 15:22:16,251 DEBUG org.springframework.amqp.rabbit.listener.BlockingQueueConsumer [SimpleAsyncTaskExecutor-1] Received message: (Body:'[B#4037c15c(byte[4])' MessageProperties [headers={}, contentLength=0, receivedDeliveryMode=NON_PERSISTENT, redelivered=false, receivedExchange=, receivedRoutingKey=Hydra.clone.q2, deliveryTag=1, consumerTag=amq.ctag-50OU2ydRYIghmrPKncVtUw, consumerQueue=Hydra.clone.q2])
2020-05-08 15:22:16,369 WARN org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler [SimpleAsyncTaskExecutor-1] Execution of Rabbit message listener failed.
org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener threw exception
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:1651)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1603)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1489)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1466)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1461)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1410)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:870)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:854)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1600(SimpleMessageListenerContainer.java:78)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1137)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1043)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalArgumentException: Detected a Non-hex character at 1 or 2 position
at org.springframework.security.crypto.codec.Hex.decode(Hex.java:62)
at org.springframework.security.crypto.encrypt.HexEncodingTextEncryptor.decrypt(HexEncodingTextEncryptor.java:40)
at com.discover.cpp.mba.util.Encryptor.decrypt(Encryptor.java:32)
at com.discover.cpp.mba.Subscriber$SubscriberMessageListener.onMessage(Subscriber.java:521)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1600)
... 10 common frames omitted
2020-05-08 15:22:16,372 DEBUG org.springframework.amqp.rabbit.listener.ContainerUtils [SimpleAsyncTaskExecutor-1] Rejecting messages (requeue=true)
Does Rabbit Listener use decoding technique while processing message ?

This has nothing to do with the Spring AMQP framework. It's your code that is (indirectly) throwing the exception.
at org.springframework.security.crypto.encrypt.HexEncodingTextEncryptor.decrypt(HexEncodingTextEncryptor.java:40)
at com.discover.cpp.mba.util.Encryptor.decrypt(Encryptor.java:32)
at com.discover.cpp.mba.Subscriber$SubscriberMessageListener.onMessage(Subscriber.java:521)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1600)
It looks like you are calling some spring security code from your listener.
It appears the inbound message does not have the expected format.
I am guessing Hex.decode is expecting a character [a-f,0-9] and the data contains something else.

Related

Grails 4 service not being injected inside Grails Data Service

This is about a Grails service injected into a Data Service. The problem is that the injected service is null at runtime. Here is an example.
class MessagingService {
def sendEmail(String message) {
...
}
}
interface IFlowService {
...
}
#Service(Flow)
abstract class FlowService implements IFlowService {
MessagingService messagingService
void sendFoo() {
messagingService.sendEmail(message)
}
}
FlowService and MessagingService both reside under grails-app/services.
When FlowService calls sendEmail there is an NPE because messagingService is null.
MessagingService is hand-written and is not associated with a domain.
This project uses Grails 4.0.10 and the issue occurred several times. When the usual Gails magic (i.e. injection) didn't work I solved the first one or two issues with kludges, you know, just to avoid getting stuck.
Now it seems to me the issue is quite predictable, it happens every time I write a service not associated with a domain. Did I miss something in the documentation? What is the appropriate way to handle this?
Kludge: To get around the issue I include a method sayHi in the problematic service. It just logs a debug message. I invoke sayHi from BootStrap to check that it works. It does, surprisingly. Then I add code in BootStrap to assign the service to the supposedly injected property in the service. [Shudder]
I tried to reproduce the same-
interface IFlowService {
}
#Service(Flow)
abstract class FlowService implements IFlowService {
MessagingService messagingService
void hello() {
println "hello"
messagingService.hi() // <- NPE
}
}
class MessagingService {
void hi() {
println "hi"
}
}
This seems to be a bug to be in Grails. But you can easily solve this (probably as a workaround) by just adding #Autowired in the service-
import org.springframework.beans.factory.annotation.Autowired
#Service(Flow)
abstract class FlowService implements IFlowService {
#Autowired
MessagingService messagingService
void hello() {
println "hello"
messagingService.hi() // <- No NPE
}
}
It prints-

Integrated Swagger and previously working code breaks: cannot deserialize from Object value (no delegate- or property-based Creator

I successfully integrated swagger to several spring boot services.
Had to allow the endpoints to bypass authentication by adding in respective #EnableWebSecurity class that extends WebSecurityConfigurerAdapter (this had worked for other services fine) :
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Order(1)
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
...
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.antMatcher("/**")
...
.antMatchers("/actuator/**").permitAll()
.antMatchers("/v2/api-docs", "/configuration/**", "/webjars/**","/swagger*/**") // ADDED THIS for swagger
.permitAll() // ADDED THIS for swagger
.antMatchers("/challenge").permitAll()
.antMatchers("/token").permitAll() // ENDPOINT with complaint now, that was previously ok.
.anyRequest()
.authenticated()
.and()
.cors();
}
...
}
For a specific one, however, once I added the relevant swagger code & dependencies, it seems to have broken and complains on what was working initially.
this is endpoint with the complaint :
#PostMapping("/token")
public ResponseDto token(#Valid #RequestBody TokenRequest request) {
try {
return service.generateJwtFromCode(request.getId(), request.getCode());
}
...
catch (Exception exception) {..
}
}
nested exception is on no constructor found for this class:
#AllArgsConstructor
public class TokenRequest {
#NotEmpty
#JsonProperty
private final String id;
#NotEmpty
#Getter
private final String code;
public UUID getId() {
return UUID.fromString(id);
}
}
Could not resolve parameter [0] in responseDTO Controller.token(Service.TokenRequest): Type definition error: [simple type, class TokenRequest]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `service.TokenRequest` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (PushbackInputStream); line: 1, column: 2]
o.s.web.servlet.DispatcherServlet : Failed to complete request: org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class service.TokenRequest]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `service.TokenRequest` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (PushbackInputStream); line: 1, column: 2]
o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in …
Not sure what it has to do with swagger integration. If i remove swagger integration code, it works fine with the same code, and doesn't complain about the type-conversion failure.
For resolving this, I also took someone's suggestion of
upgrading dependency for com.fasterxml.jackson.core
and rebuilding the code. But still no success.
compileOnly 'com.fasterxml.jackson.core:jackson-databind:2.11.2'
Things I have tried but didn't resolve:
Added a default/empty constructor
(for most others with similar problem it worked by that, for me it complaint thereafter on
error: variable id might not have been initialized
}
Added this to the tokenRequest class:
#Value
#AllArgsConstructor(onConstructor = #__(#JsonCreator(mode = JsonCreator.Mode.PROPERTIES))
Had a different error:
c.j.MappingJackson2HttpMessageConverter : Failed to evaluate Jackson deserialization for type [[simple type, class TokenRequest]]
InvalidDefinitionException: Invalid type definition for type `TokenRequest`: More than one argument (#0 and #1) left as delegating for Creator [constructor for TokenRequest, annotations: {interface com.fasterxml.jackson.annotation.JsonCreator=#com.fasterxml.jackson.annotation.JsonCreator(mode=DELEGATING)}]: only one allowed
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:62)...
Solution was to add a default constructor AND also remove final variables.
#AllArgsConstructor
public class TokenRequest {
#NotEmpty
#JsonProperty
private String id; // code fixed issue
#NotEmpty
#Getter
private String code; // code fixed issue
public TokenRequest(){} // code fixed issue
public UUID getId() {
return UUID.fromString(id);
}
}

Crack implementation for custom object in quickFixj

I created a custom object in FIX44.xml .I want to call onMessage handler in receiver for same custom object using crack implementation but adding crack in fromApp of reciever does not call onMessage .Here is the code
#Override
public void fromApp(Message arg0, SessionID arg1) throws FieldNotFound, IncorrectDataFormat,
IncorrectTagValue, UnsupportedMessageType {
crack(arg0, arg1); // should calls onMessage(..,..) of custom object
}
public void onMessage(MyCustomObject message, SessionID sessionID)
throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue {
//do something
}
Hello isha and welcome to Stack Overflow! I'm suspecting you are not having your class implement the MessageCracker interface (you did not include the whole class definition).
The correct use of this interface, and how to define the methods to crack your messages is described in the QuickFIX/J Receiving Messages documentation.
To quote the relevant parts, which includes an example:
Define your application like this:
import quickfix.Application;
import quickfix.MessageCracker;
public class MyApplication
extends MessageCracker implements quickfix.Application
QuickFIX/J version 1.16 or newer supports a new version of
MessageCracker that dynamically discovers message handling method
using either a method naming convention (similar to earlier versions
of QuickFIX/J) or by tagging handlers methods with the #Handler
annotation.
Examples...
import quickfix.Application;
import quickfix.MessageCracker;
public class MyApplication extends MessageCracker implements quickfix.Application
{
public void fromApp(Message message, SessionID sessionID)
throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue {
crack(message, sessionID);
}
// Using annotation
#Handler
public void myEmailHandler(quickfix.fix50.Email email, SessionID sessionID) {
// handler implementation
}
// By convention (notice different version of FIX. It's an error to have two handlers for the same message)
// Convention is "onMessage" method with message object as first argument and SessionID as second argument
public void onMessage(quickfix.fix44.Email email, SessionID sessionID) {
// handler implementation
}
}

How do we hook into before/After message processing using #RabbitListener

Problem: I am migrating from MessageListener interface impl to #RabbitListener. I had logic like this where I was doing "pre" and "post" message processing on a MessageListener that was inherited by several classes
example:
public AbstractMessageListener implements MessageListener {
#Override
public void onMessage(Message message) {
//do some pre message processing
process(Message message);
// do some post message processing
}
protected abstract void process(Message message);
}
Question: Is there a way I can achieve something similar using #RabbitListener annotation Where I can inherit pre/post message processing logic without having to re-implement or call the pre/post message processing inside each child #RabbitListener annotation and all the while maintaining a customizable method signatures for the child #RabbitListener? Or is this being too greedy?
Example desired result:
public class SomeRabbitListenerClass {
#RabbitListener( id = "listener.mypojo",queues = "${rabbitmq.some.queue}")
public void listen(#Valid MyPojo myPojo) {
//...
}
}
public class SomeOtherRabbitListenerClass {
#RabbitListener(id = "listener.orders",queues ="${rabbitmq.some.other.queue}")
public void listen(Order order, #Header("order_type") String orderType) {
//...
}
}
with both these #RabbitListener(s) utilizing the same inherited pre/post message processing
I see there is a 'containerFactory' argument in the #RabbitListener annotation but i'm already declaring one in the config... and i'm really sure how to achieve the inheritance I desire with a custom containerFactory.
Updated Answer: This is what I ended up doing.
Advice defintion:
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.amqp.core.Message;
/**
* AOP Around advice wrapper. Every time a message comes in we can do
* pre/post processing by using this advice by implementing the before/after methods.
* #author sjacobs
*
*/
public class RabbitListenerAroundAdvice implements MethodInterceptor {
/**
* place the "AroundAdvice" around each new message being processed.
*/
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Message message = (Message) invocation.getArguments()[1];
before(message)
Object result = invocation.proceed();
after(message);
return result;
}
declare beans: In your rabbitmq config declare the advice as a Spring bean and pass it to the rabbitListenerContainerFactory#setAdviceChain(...)
//...
#Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory( cachingConnectionFactory() );
factory.setTaskExecutor(threadPoolTaskExecutor());
factory.setMessageConverter(jackson2JsonMessageConverter());
factory.setAdviceChain(rabbitListenerAroundAdvice());
return factory;
}
#Bean
public RabbitListenerAroundAdvice rabbitListenerAroundAdvice() {
return new RabbitListenerAroundAdvice();
}
// ...
Correction
You can use the advice chain in the SimpleRabbitListenerContainerFactory to apply an around advice to listeners created for #RabbitListener; the two arguments are the Channel and Message.
If you only need to take action before calling the listener, you can add MessagePostProcessor(s) to the container afterReceivePostProcessors.
The inheritance isn't possible here because annotation processing on the POJO methods and MessageListener implementation are fully different stories.
Using MessageListener you fully have control around the target behavior and the container.
With the annotations you deal only with the POJO, framework-free code. The particular MessageListener is created on the background. And that one fully based on the annotated method.
I'd say we can achieve your requirement using Spring AOP Framework.
See the recent question and its answers on the matter: How to write an integration test for #RabbitListener annotation?

How to write an integration test for #RabbitListener annotation?

My question is really a follow up question to
RabbitMQ Integration Test and Threading
There it states to wrap "your listeners" and pass in a CountDownLatch and eventually all the threads will merge. This answer works if we were manually creating and injecting the message listener but for #RabbitListener annotations... i'm not sure how to pass in a CountDownLatch. The framework is auto magically creating the message listener behind the scenes.
Are there any other approaches?
With the help of #Gary Russell I was able to get an answer and used the following solution.
Conclusion: I must admit i'm indifferent about this solution (feels like a hack) but this is the only thing I could get to work and once you get over the initial one time setup and actually understand the 'work flow' it is not so painful. Basically comes down to defining ( 2 ) #Beans and adding them to your Integration Test config.
Example solution posted below with explanations. Please feel free to suggest improvements to this solution.
1. Define a ProxyListenerBPP that during spring initialization will listen for a specified clazz (i.e our test class that contains #RabbitListener) and
inject our custom CountDownLatchListenerInterceptor advice defined in the next step.
import org.aopalliance.aop.Advice;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
/**
* Implements BeanPostProcessor bean... during spring initialization we will
* listen for a specified clazz
* (i.e our #RabbitListener annotated class) and
* inject our custom CountDownLatchListenerInterceptor advice
* #author sjacobs
*
*/
public class ProxyListenerBPP implements BeanPostProcessor, BeanFactoryAware, Ordered, PriorityOrdered{
private BeanFactory beanFactory;
private Class<?> clazz;
public static final String ADVICE_BEAN_NAME = "wasCalled";
public ProxyListenerBPP(Class<?> clazz) {
this.clazz = clazz;
}
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (clazz.isAssignableFrom(bean.getClass())) {
ProxyFactoryBean pfb = new ProxyFactoryBean();
pfb.setProxyTargetClass(true); // CGLIB, false for JDK proxy (interface needed)
pfb.setTarget(bean);
pfb.addAdvice(this.beanFactory.getBean(ADVICE_BEAN_NAME, Advice.class));
return pfb.getObject();
}
else {
return bean;
}
}
#Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 1000; // Just before #RabbitListener post processor
}
2. Create the MethodInterceptor advice impl that will hold the reference to the CountDownLatch. The CountDownLatch needs to be referenced in both in the Integration test thread and inside the async worker thread in the #RabbitListener. So we can later release back to the Integration Test thread as soon as the #RabbitListener async thread has completed execution. No need for polling.
import java.util.concurrent.CountDownLatch;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* AOP MethodInterceptor that maps a <b>Single</b> CountDownLatch to one method and invokes
* CountDownLatch.countDown() after the method has completed execution. The motivation behind this
* is for integration testing purposes of Spring RabbitMq Async Worker threads to be able to merge
* the Integration Test thread after an Async 'worker' thread completed its task.
* #author sjacobs
*
*/
public class CountDownLatchListenerInterceptor implements MethodInterceptor {
private CountDownLatch countDownLatch = new CountDownLatch(1);
private final String methodNameToInvokeCDL ;
public CountDownLatchListenerInterceptor(String methodName) {
this.methodNameToInvokeCDL = methodName;
}
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
String methodName = invocation.getMethod().getName();
if (this.methodNameToInvokeCDL.equals(methodName) ) {
//invoke async work
Object result = invocation.proceed();
//returns us back to the 'awaiting' thread inside the integration test
this.countDownLatch.countDown();
//"reset" CountDownLatch for next #Test (if testing for more async worker)
this.countDownLatch = new CountDownLatch(1);
return result;
} else
return invocation.proceed();
}
public CountDownLatch getCountDownLatch() {
return countDownLatch;
}
}
3. Next add to your Integration Test Config the following #Bean(s)
public class SomeClassThatHasRabbitListenerAnnotationsITConfig extends BaseIntegrationTestConfig {
// pass into the constructor the test Clazz that contains the #RabbitListener annotation into the constructor
#Bean
public static ProxyListenerBPP listenerProxier() { // note static
return new ProxyListenerBPP(SomeClassThatHasRabbitListenerAnnotations.class);
}
// pass the method name that will be invoked by the async thread in SomeClassThatHasRabbitListenerAnnotations.Class
// I.E the method name annotated with #RabbitListener or #RabbitHandler
// in our example 'listen' is the method name inside SomeClassThatHasRabbitListenerAnnotations.Class
#Bean(name=ProxyListenerBPP.ADVICE_BEAN_NAME)
public static Advice wasCalled() {
String methodName = "listen";
return new CountDownLatchListenerInterceptor( methodName );
}
// this is the #RabbitListener bean we are testing
#Bean
public SomeClassThatHasRabbitListenerAnnotations rabbitListener() {
return new SomeClassThatHasRabbitListenerAnnotations();
}
}
4. Finally, in the integration #Test call... after sending a message via rabbitTemplate to trigger the async thread... now call the CountDownLatch#await(...) method obtained from the interceptor and make sure to pass in a TimeUnit args so it can timeout in case of long running process or something goes wrong. Once the async the Integration Test thread is notified (awakened) and now we can finally begin to actually test/validate/verify the results of the async work.
#ContextConfiguration(classes={ SomeClassThatHasRabbitListenerAnnotationsITConfig.class } )
public class SomeClassThatHasRabbitListenerAnnotationsIT extends BaseIntegrationTest{
#Inject
private CountDownLatchListenerInterceptor interceptor;
#Inject
private RabbitTemplate rabbitTemplate;
#Test
public void shouldReturnBackAfterAsyncThreadIsFinished() throws Exception {
MyObject payload = new MyObject();
rabbitTemplate.convertAndSend("some.defined.work.queue", payload);
CountDownLatch cdl = interceptor.getCountDownLatch();
// wait for async thread to finish
cdl.await(10, TimeUnit.SECONDS); // IMPORTANT: set timeout args.
//Begin the actual testing of the results of the async work
// check the database?
// download a msg from another queue?
// verify email was sent...
// etc...
}
It's a bit more tricky with #RabbitListener but the simplest way is to advise the listener.
With the custom listener container factory just have your test case add the advice to the factory.
The advice would be a MethodInterceptor; the invocation will have 2 arguments; the channel and the (unconverted) Message. The advice has to be injected before the container(s) are created.
Alternatively, get a reference to the container using the registry and add the advice later (but you'll have to call initialize() to force the new advice to be applied).
An alternative would be a simple BeanPostProcessor to proxy your listener class before it is injected into the container. That way, you will see the method argumen(s) after any conversion; you will also be able to verify any result returned by the listener (for request/reply scenarios).
If you are not familiar with these techniques, I can try to find some time to spin up a quick example for you.
EDIT
I issued a pull request to add an example to EnableRabbitIntegrationTests. This adds a listener bean with 2 annotated listener methods, a BeanPostProcessor that proxies the listener bean before it is injected into a listener container. An Advice is added to the proxy which counts latches down when the expected messages are received.

Resources