So I need to create an integration test for my kafkalistener method, where the test expects ListenerExecutionFailedException is actually thrown because the message was failed during consumption due to another service being inactive.
Below is the test code, where I use embeddedkafkabroker for producer and consumer:
#Test(expected = ListenerExecutionFailedException.class)
public void shouldThrowException() {
RecordHeaders recordHeaders = new RecordHeaders();
recordHeaders.add(new RecordHeader("messageType", "bootstrap".getBytes()));
recordHeaders.add(new RecordHeader("userId", "123".getBytes()));
recordHeaders.add(new RecordHeader("applicationId", "1234".getBytes()));
recordHeaders.add(new RecordHeader("correlationId", UUID.randomUUID().toString().getBytes()));
ProducerRecord<String, String> producerRecord = new ProducerRecord<>(
"TEST_TOPIC",
1,
null,
"message",
"",
recordHeaders);
producer.send(producerRecord);
consumer.subscribe(Collections.singleton("TEST_TOPIC"));
consumer.poll(Duration.ofSeconds(2));
}
What I'm wondering is the exception was considered as not thrown and the test fails even though I know the message is indeed received by the listener and the exception was thrown since I saw them on the log.
And even though I changed the expected into Throwable no exception seems to be detected.
What should I do to make the exception to be detected by Junit?
Also, another interesting thing is that I tried to mock the service class which was called in the listener and return some dummy value but the service is not called when I used Mockito.verify
You seem to have some misunderstanding.
producer.send
consumer.poll
You are calling the kafka-clients directly and are not using Spring at all in this test.
ListenerExecutionFailedException is an exception that Spring's listener container wraps user exceptions thrown by message listeners.
Related
I have been using Spring integration (different types of channels) with Spring 4 for some time. After i tried to upgrade my environment to Spring 5, they stopped working with errors such as the following:
org.springframework.messaging.MessageHandlingException: error occurred during processing message in 'MethodInvokingMessageProcessor' [org.springframework.integration.handler.MethodInvokingMessageProcessor#c192373]; nested exception is java.lang.IllegalArgumentException: BeanFactory must not be null, failedMessage=GenericMessage
Sample channel creation/registration is as follows:
deltaupdatedchannel = new DirectChannel();
deltaupdatedchannel.setBeanName("deltaupdatedcontroller");
serviceActivator = new ServiceActivatingHandler(deltaSummaryController, "updateDelta2");
handlerlist.add(serviceActivator);
beanFactory.registerSingleton("deltaupdatedcontroller", deltaupdatedchannel);
beanFactory.initializeBean(deltaupdatedchannel, "deltaupdatedcontroller");
deltaupdatedchannel.subscribe(serviceActivator);
Channels are used to make the following call:
this.deltaupdatedcontrollerchannel.send(MessageBuilder.withPayload(summarydto).build());
And channel calls the following code:
public void updateDelta2(DeltaSummaryDTO dto) {
this.messagingTemplate.convertAndSend(
"/topic/updatedelta", dto);
}
Here messagingTemplate is org.springframework.messaging.core.MessageSendingOperations.
How can i make them work again?
Share, please, with us the reason doing that registerSingleton(). Why just plain bean registration is not enough for you?
To fix that problem you need to call also initializeBean(Object existingBean, String beanName) after that registerSingleton().
However there is no guarantee that this will be the end of errors. I would suggest to revise a design in favor of normal bean definitions, not that manual one...
I'm using Kafka embedded broker with spring boot and junit 5.I have been able to wire up successfully and see that the embedded broker is running.
In my setup method I pump in a few messages to the queue that my actual code listens on
#BeforeAll
public void setup() {
// code to play down some messages to topic X
}
My consumer/listener is never trigerred despite there being no errors encountered in the setup method
My Consumer is setup like
class Consumer() {
#KafkaListener(topics="X",
groupId ="...",
containerFactory="my-container-factory"
)
public void consume(ConsumerRecord<String,byte[] rec) {
//logic to handle
logger.info("Print rec : "+rec)
}
}
else where I've set up my ListenerContainerFactory with a name like
#Bean(name="my-container-factory")
public KafkaContainerListenerFactory<String,byte[]> factory() {
}
What could be wrong with this?My assertions in the test case fail and additionally I don't see my log statements that should be printed if my consume method were ever called.
I've a feeling,that auto configuration due to #SpringBootTest and #EmbeddedKafka is setting up some other listener container factory and so maybe my #KafkaListener annotation is wrong.
I know,its a bit vague but could you please tell me what/where to look at?If I run as a #SpringBootApplication my Consumer is pulling in messages from the actual queue.So no problems with my actual app.Its the test that's not executing as per expectation.
Please help.
Edit 1:
I have spring.kafka.consumer.auto-offset-reset=earliest set in my yml file.
I use spring boot 2.2.
In a method with transactional anocation, when I save via repository if there is no error, I want to send a message with rabbit mq.
How to be sure there is no error with repository?
#Transactional
public void save(CreditEvent creditEvent) {
repository.save(creditEvent);
//no error send message
}
if there is an error when sending message, I don't want to rollback saving operation.
Although it's Transactional and JPA, still it's a java method which if save failed then unchecked DataAccessException exception will be thrown and flow won't continue to send message.
class is a runtime exception, there is no need for user code to catch it or subclasses if any error is to be considered fatal (the usual case).
#Transactional
public void save(CreditEvent creditEvent) {
try {
repository.save(creditEvent);
//no error send message}
catch {
// send message
// rethrow error
}
}
I have method in a Spring component which receives messages from a Spring Integration channel. When a message is received, it is sent to a WebSocket endpoint. This doesn't work. The message is not broadcast.
this.messagingTemplate.convertAndSend("/topic/update", dto);
However when I put the same code inside a Web Controller and put a RequestMapping on it, and call that endpoint, it works. The message is broadcast.
What might be causing it not to work, when it is called by the Spring integration executor?
when it works: .14:01:19.939 [http-nio-8080-exec-4] DEBUG o.s.m.s.b.SimpleBrokerMessageHandler - Processing MESSAGE destination=/topic/update session=null payload={XXX}
.14:01:19.939 [http-nio-8080-exec-4] DEBUG o.s.m.s.b.SimpleBrokerMessageHandler - Broadcasting to 1 sessions.
when it doesnt work, second message is not there. (thread is taskExecutor-1 instead of http-nio..)
Controller code:
#RequestMapping("/testreq")
public void updateDelta() {
SummaryDTO dto = new SummaryDTO();
dto.setValue(-5000.0);
dto.setName("G");
this.messagingTemplate.convertAndSend("/topic/update", dto);
}
//this method is called by Spring Integration
//created by serviceActivator = new
//ServiceActivatingHandler(webcontroller,"update");
public void updateDelta(SummaryDTO dto) {
this.messagingTemplate.convertAndSend("/topic/update", dto);
}
message send:
synchronized(this){
...
this.updatedcontrollerchannel.send(MessageBuilder.withPayload(summarydto).build(
));
}
channel creation:
updatedchannel = new DirectChannel();
updatedchannel.setBeanName("updatedcontroller");
serviceActivator = new ServiceActivatingHandler(detailService,"update");
handlerlist.add(serviceActivator);
updatedchannel.subscribe(serviceActivator);
beanFactory.registerSingleton("updatedcontroller", channel);
UPDATE
I added spring messaging source code to my environment and realized the following: There are 2 instances of the SimpleBrokerMessageHandler class in the runtime. For the working copy subscriptionregistry has one entry and for the nonworking one, it has 0 subscriptions. Does this give a clue for the root cause of the problem? There is only one MessageSendingOperations variable defined and it is on the controller.
i found the cause of the problem. Class which has #EnableWebSocketMessageBroker annotation was loaded twice and it caused two instances of SimpleBrokerMessageHandler to be created. #Artem Bilan: thanks for your time.
Should be the problem with the non-properly injected SimpMessageSendingOperations.
This one is populated by the AbstractMessageBrokerConfiguration.brokerMessagingTemplate() #Bean.
However I would like to suggest you to take a look into the WebSocketOutboundMessageHandler from Spring Integration: https://docs.spring.io/spring-integration/docs/4.3.12.RELEASE/reference/html/web-sockets.html
UPDATE
This works for me in the test-case:
#Bean
#InboundChannelAdapter(channel = "nullChannel", poller = #Poller(fixedDelay = "1000"))
public Supplier<?> webSocketPublisher(SimpMessagingTemplate brokerMessagingTemplate) {
return () -> {
brokerMessagingTemplate.convertAndSend("/topic/foo", "foo");
return "foo";
};
}
And I have this DEBUG logs:
12:57:27.606 DEBUG [task-scheduler-1][org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler] Processing MESSAGE destination=/topic/foo session=null payload=foo
12:57:27.897 DEBUG [clientInboundChannel-2][org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler] Processing SUBSCRIBE /topic/foo id=subs1 session=941a940bf07c47a1ac786c1adfdb6299
12:57:40.797 DEBUG [task-scheduler-1][org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler] Processing MESSAGE destination=/topic/foo session=null payload=foo
12:57:40.798 DEBUG [task-scheduler-1][org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler] Broadcasting to 1 sessions.
Everything works well from Spring Integration.
That's why I asked your whole Spring Boot app to play from our side.
UPDATE 2
When you develop Web application be sure to merge all the configs contexts to a single one application context - WebApplicationContext:
If an application context hierarchy is not required, applications may return all configuration via getRootConfigClasses() and null from getServletConfigClasses().
See more info in the Spring Framework Reference Manual.
Some code like this:
public class A {
#Autoware
private B b;
public void a() {
//AAA: some logic process that maybe throw exception
b.b();
}
}
public class B {
public void b() {
//BBB: some logic process maybe also throw exception
}
}
Both exceptions in A.a() and B.b() need to be intercept, so i use #AfterThrowing annotation do it. but the question is, when i call A.a() in other code and exception has occurred in B.b(), the Advice will execute twice! because exception that occurred in B.b() was propagating to its caller A.a().
I can't swallow the exception silently, because i use spring-amqp, above codes is on Consumer side, i need some message processing that based on the exceptions that occurred in Consumer.
#Around does not work too since i can't swallow the throwed exception.
So, How can i intercept a exception just when it occurred? ignore propagation of it.
Any reply is greatly appreciated.