Receiver doesn't receive messages from topic - java

I have two different apps for sender and receiver.
sender:
#SpringBootApplication
public class RabbitJmsApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(RabbitJmsApplication.class, args);
}
#Autowired
private JmsTemplate template;
#Autowired
private JmsTemplate topicTemplate;
#Override
public void run(String... arg0) throws Exception {
for (int i = 0; i < 10; i++) {
template.convertAndSend("my_queue", "msg_" + i);
Thread.sleep(100);
}
for (int i = 0; i < 10; i++) {
topicTemplate.convertAndSend("my_topic", "topic_msg_" + i);
Thread.sleep(100);
}
}
#Bean
public RMQConnectionFactory connectionFactory() {
return new RMQConnectionFactory();
}
#Bean
public JmsTemplate template() {
return new JmsTemplate(connectionFactory());
}
#Bean
public JmsTemplate topicTemplate() {
final JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory());
jmsTemplate.setPubSubDomain(true);
return jmsTemplate;
}
}
and receiver:
#Component
public class Listener {
#JmsListener(destination = "my_queue")
public void receive(String str){
System.out.println(str);
}
#JmsListener(destination = "my_topic")
public void receiveTopic(String str){
System.out.println(str);
}
}
I see
msg_1
msg_2
...
on the receiver but I don't see the topic messages.
What am I doing wrong?
P.S.
management console:

Subscriptions to topics are not durable by default - you are probably sending the messages before the listener has started.
Try adding a Thread.sleep() before sending the messages to the topic.

My receiver became to receive mesagges after adding following bean to the context:
#Bean
public JmsListenerContainerFactory<?> myFactory(DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
// This provides all boot's default to this factory, including the message converter
configurer.configure(factory, connectionFactory());
// You could still override some of Boot's default if necessary.
factory.setPubSubDomain(true);
return factory;
}

Related

Azure Service Bus queues dynamic Spring

I have a challenge to set up a service in SpringBoot, where it will be listening to several queues. I searched a lot and couldn't find what I was looking for. I have queues, which can grow dynamically.
Exemple: queue-1, queue-2, queue-3...
What could I use in this service to get this service up by listening to these queues dynamically?
Using spring JMS you can do this like it is done here
Your config file is like this:
#Configuration
#EnableJms
public class AppConfig implements JmsListenerConfigurer {
#Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
List<QueueInformation> queueInformationList = consumersStatic.getQueueInformationList();
int i = 0;
for (QueueInformation queueInformation :
queueInformationList) {
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
endpoint.setId("myJmsEndpoint-" + i++);
endpoint.setDestination(queueInformation.getMqQueueName());
endpoint.setMessageListener(message -> {
logger.debug("***********************************************receivedMessage:" + message);
});
registrar.registerEndpoint(endpoint);
logger.debug("registered the endpoint for queue" + queueInformation.getMqQueueName());
}
}
Another way is using RabbitListenerConfigurer. You can get more idea from here
code from this link:
for rabbitconfig:
#Configuration
public class RabbitMqConfiguration implements RabbitListenerConfigurer {
#Autowired
private ConnectionFactory connectionFactory;
#Bean
public Jackson2JsonMessageConverter producerJackson2MessageConverter() {
return new Jackson2JsonMessageConverter();
}
#Bean
public MappingJackson2MessageConverter consumerJackson2MessageConverter() {
return new MappingJackson2MessageConverter();
}
#Bean
public RabbitTemplate rabbitTemplate() {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(producerJackson2MessageConverter());
return rabbitTemplate;
}
#Bean
public RabbitAdmin rabbitAdmin() {
return new RabbitAdmin(connectionFactory);
}
#Bean
public RabbitListenerEndpointRegistry rabbitListenerEndpointRegistry() {
return new RabbitListenerEndpointRegistry();
}
#Bean
public DefaultMessageHandlerMethodFactory messageHandlerMethodFactory() {
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
factory.setMessageConverter(consumerJackson2MessageConverter());
return factory;
}
#Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
#Override
public void configureRabbitListeners(final RabbitListenerEndpointRegistrar registrar) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setPrefetchCount(1);
factory.setConsecutiveActiveTrigger(1);
factory.setConsecutiveIdleTrigger(1);
factory.setConnectionFactory(connectionFactory);
registrar.setContainerFactory(factory);
registrar.setEndpointRegistry(rabbitListenerEndpointRegistry());
registrar.setMessageHandlerMethodFactory(messageHandlerMethodFactory());
}
}
service you can find here

How Test a Mqtt Client with Junit without run it but with a mock?

I have doing a mqtt client with org.springframework.integration.mqtt.core.MqttPaho follow this guide: Spring Mqtt Support and work correctly, I read from a topic and write in another topic.
Now I want test it with Junit5+mockito.. I don't understand well guide in internet.. in particular how mock producer or consumer.. I use configuration for consumer and producer class for example:
#Configuration
public class Consumer {
#Autowired MqttPahoClientFactory mqttClientFactory;
#Bean
public MessageChannel topicChannel(){
return new DirectChannel();
}
#Bean
public MessageProducer mqttInbound() {
MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(
Parameters.MQTT_CLIENT_ID, mqttClientFactory, Parameters.TOPICS[0]);
adapter.setCompletionTimeout(5000);
adapter.setConverter(new DefaultPahoMessageConverter());
adapter.setQos(1);
adapter.setOutputChannel(topicChannel());
return adapter;
}
}
and
#Configuration
public class Producer {
#Autowired MqttPahoClientFactory mqttClientFactory;
#Bean
public MessageChannel mqttOutboundChannel(){
return new DirectChannel();
}
#MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
public interface ProducerGateway {
void sendToMqtt(String data, #Header(MqttHeaders.TOPIC) String topic);
}
#Bean
#ServiceActivator(inputChannel = "mqttOutboundChannel")
public MessageHandler mqttOutbound() {
MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(
Parameters.MQTT_CLIENT_ID,
mqttClientFactory);
messageHandler.setAsync(true);
messageHandler.setLoggingEnabled(true);
return messageHandler;
}
}
#Configuration
public class MqttConfiguration {
#Bean
public MqttPahoClientFactory mqttClientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
MqttConnectOptions options = new MqttConnectOptions();
options.setServerURIs(Parameters.BROKER_URIs);
options.setUserName(Parameters.MQTT_USERNAME);
options.setPassword(Parameters.MQTT_PASSWORD.toCharArray());
factory.setConnectionOptions(options);
return factory;
}
}

RabbitListenerTestHarness injects actual objects in listeners

Scenario:
Junit for a microservice which listens to a queue and posts to an exchange in rabbitMQ after data extraction.
Issue:
RabbitListenerTestHarness is creating mock object for the Rabbit
Listener class alone, Actual objects are being instantiated for
Listeners Autowired components
I couldnt find a way to manually inject mock beans into the listener. This causes Junit to post the test messages to the actual queues configured in the microservice during Junit Execution.
Workaround: The only way I could use the rabbit-test project is to configure test exchange for posting the messages during Junit execution.
Query:
I wanted to understand, if there is any way better way of writing Junit for a Rabbit Listener. Also i wanted to understand if there is a way to maually inject mock objects to the Rabbit Listeners autowired components.
Sample code Snippet:
Rabbit Listener Class
#RabbitListener(id = "id", bindings = #QueueBinding(value = #Queue(value = "sampleQueue", durable = "true", autoDelete = "false"),key = "sampleRoutingKey", exchange = #Exchange(value = "sampleExchange", durable = "true", ignoreDeclarationExceptions = "true", type = EXCHANGE_TYPE)))
public void getMessageFromQueue(#Payload EventModel event) throws ListenerExecutionFailedException, JAXBException {
dataExporterService.exportDataAndPostToRabbit(event);
}
Service class
#Autowired
DataExtractorRepository dataExtractorRepository;
#Autowired
DataPublihserRepository dataPublisherRepo;
public void exportDataAndPostToRabbit(EventModel event) throws JAXBException {
dataPublisherRepo.sendMessageToExchange(dataExtractorRepository.extractOrderData(event), exchangeName, routingKeyValue);
}
DataPublihserRepository has rabbitTemplate internally Autowired. DataExtractorRepository connects to DB internally for retriving the message.
Test class
#Autowired
private RabbitListenerTestHarness harness;
#Autowired
private RabbitTemplate rabbitTemplate;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
DataExporterController = this.harness.getSpy("id");
}
#Test
public void shouldReceiveMessage() throws Exception {
LatchCountDownAndCallRealMethodAnswer answer = new LatchCountDownAndCallRealMethodAnswer(1);
doAnswer(answer).when(DataExporterController).getMessageFromQueue(any(EventModel.class));
rabbitTemplate.convertAndSend("sampleExchange", "sampleRoutingKey", createMessage());
assertTrue(answer.getLatch().await(10, TimeUnit.SECONDS));
verify(DataExporterController, times(1)).getMessageFromQueue(any(OrderEventsModel.class));
verify(orderDataExporterController, times(1)).getMessageFromQueue(any(OrderEventsModel.class));
}
private Message createMessage() {
String inputObject = "{\"id\":12345}";
MessageProperties props = MessagePropertiesBuilder.newInstance().setContentType(MessageProperties.CONTENT_TYPE_JSON).build();
return new Message(inputObject.getBytes(), props);
}
The harness is intended as a mechanism to verify that the listener received the data in an integration test. To unit test a listener, invoke its onMessage Method.
For example, using Mockito, given
public class MyListener {
#Autowired
private SomeService service;
#RabbitListener(id = "myListener", queues = "foo")
public void listen(Foo foo) {
this.service.process(foo);
}
}
and
public interface SomeService {
void process(Foo foo);
}
then
#RunWith(SpringRunner.class)
public class So53136882ApplicationTests {
#Autowired
private RabbitListenerEndpointRegistry registry;
#Autowired
private SomeService service;
#Test
public void test() throws Exception {
SimpleMessageListenerContainer container = (SimpleMessageListenerContainer) this.registry
.getListenerContainer("myListener");
ChannelAwareMessageListener listener = (ChannelAwareMessageListener) container.getMessageListener();
Message message = MessageBuilder.withBody("{\"bar\":\"baz\"}".getBytes())
.andProperties(MessagePropertiesBuilder.newInstance()
.setContentType("application/json")
.build())
.build();
listener.onMessage(message, mock(Channel.class));
verify(this.service).process(new Foo("baz"));
}
#Configuration
#EnableRabbit
public static class config {
#Bean
public ConnectionFactory mockCf() {
return mock(ConnectionFactory.class);
}
#Bean
public MessageConverter converter() {
return new Jackson2JsonMessageConverter();
}
#Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(mockCf());
factory.setMessageConverter(converter());
factory.setAutoStartup(false);
return factory;
}
#Bean
public MyListener myListener() {
return new MyListener();
}
#Bean
public SomeService service() {
return mock(SomeService.class);
}
}
}
Notice that the container factory does not start the listener container.
For testing publishing, inject a mock RabbitOperations which is implemented by RabbitTemplate.
For example, given
public class SomeServiceImpl implements SomeService {
#Autowired
private RabbitOperations rabbitOperations;
#Override
public void process(Foo foo) {
this.rabbitOperations.convertAndSend(
"someExchange", "someRoutingKey", new Foo(foo.getBar().toUpperCase()));
}
}
and
#Bean
public SomeService service() {
return new SomeServiceImpl();
}
#Bean
public RabbitOperations rabbitTemplate() {
return mock(RabbitOperations.class);
}
then
#Test
public void test() throws Exception {
SimpleMessageListenerContainer container = (SimpleMessageListenerContainer) this.registry
.getListenerContainer("myListener");
ChannelAwareMessageListener listener = (ChannelAwareMessageListener) container.getMessageListener();
Message message = MessageBuilder.withBody("{\"bar\":\"baz\"}".getBytes())
.andProperties(MessagePropertiesBuilder.newInstance()
.setContentType("application/json")
.build())
.build();
listener.onMessage(message, mock(Channel.class));
verify(this.rabbitTemplate).convertAndSend("someExchange", "someRoutingKey", new Foo("BAZ"));
}

How to setup a socket client with spring-integration?

I'm using spring 4 annotation based configuration, and would like to set up a simple telnet/socket client.
This is what I have so far:
#MessageEndpoint
public class MySocket {
#Bean
public TcpConnectionFactoryFactoryBean clientFactory() {
TcpConnectionFactoryFactoryBean fact = new TcpConnectionFactoryFactoryBean();
fact.setType("client");
fact.setHost(host);
fact.setPort(port);
fact.setUsingNio(true);
fact.setSingleUse(true);
fact.setSoTimeout(timeout);
return fact;
}
#Bean
public MessageChannel clientChannel() {
return new DirectChannel();
}
#Bean
#ServiceActivator(inputChannel = "clientChannel")
public TcpOutboundGateway outGateway(TcpNioClientConnectionFactory factory,
#Qualifier("clientChannel") MessageChannel clientChannel) throws Exception {
TcpOutboundGateway gate = new TcpOutboundGateway();
gate.setConnectionFactory(factory);
gate.setReplyChannel(clientChannel);
return gate;
}
}
#Component
public class MyMessageService {
#Autowired
#Qualifier("clientChannel")
private MessageChannel clientChannel;
public void run() {
Message<String> msg = MessageBuilder.withPayload("test").build();
Message<?> rsp = new MessagingTemplate(clientChannel).sendAndReceive(msg);
}
}
Result: org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers
What am I missing here to send the message via the socket and receive the reply?
You don't need the #MessageEndpoint annotation, but you need a consumer on the channel...
#ServiceActivator(inputChannel = "clientChannel")
#Bean
public TcpOutboundGateway outGateway(AbstractClientConnectionFactory scf) {
...
}
The gateway needs a reference to the connection factory. Since you are using a factory bean, it's easiest to add it as a parameter to the bean's factory method.

BackOffPolicy and SimpleRetryPolicy not in effect when injected into RetryTemplate

I am using Spring AMQP to send messages and be able to perform retries on a "custom" Exception. Lets say I have a Receiver which throws a custom exception "EventException" and for that, I want there to be a n number of retries (in our example 5). Between the retries I also want there to be a 5 seconds delay as well. Here is my source code:
#SpringBootApplication
public class DemoApplication implements CommandLineRunner {
final static String queueName = "testing-queue";
#Autowired
AnnotationConfigApplicationContext context;
#Autowired
RabbitTemplate rabbitTemplate;
#Bean
Queue queue() {
Map<String, Object> arguments = new HashMap<String, Object>();
arguments.put("x-dead-letter-exchange", "dead-letter-exchange");
Queue queue = new Queue(queueName, true, false, false, arguments);
return queue;
}
#Bean
TopicExchange exchange() {
return new TopicExchange("testing-exchange");
}
#Bean
Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(queueName);
}
#Bean
Queue deadLetterQueue() {
return new Queue("dead-letter-queue", true);
}
#Bean
FanoutExchange deadLetterExchange() {
return new FanoutExchange("dead-letter-exchange");
}
#Bean
Binding deadLetterBinding(Queue deadLetterQueue, FanoutExchange deadLetterExchange) {
return BindingBuilder.bind(deadLetterQueue).to(deadLetterExchange);
}
#Bean
ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory =
new CachingConnectionFactory("localhost");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
return connectionFactory;
}
#Bean
SimpleMessageListenerContainer container(
ConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter,
RetryOperationsInterceptor interceptor) {
Advice[] adviceChain = { interceptor };
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(queueName);
container.setAdviceChain(adviceChain);
container.setMessageListener(listenerAdapter);
return container;
}
#Bean
Receiver receiver() {
return new Receiver();
}
#Bean
MessageListenerAdapter listenerAdapter(Receiver receiver) {
MessageListenerAdapter adapter =
new MessageListenerAdapter(receiver, "receiveMessage");
return adapter;
}
#Bean
RetryOperations retryTemplate() {
Map<Class<? extends Throwable>, Boolean> retryableExceptions =
new HashMap<Class<? extends Throwable>, Boolean>();
retryableExceptions.put(EventException.class, false);
FixedBackOffPolicy backoffPolicy = new FixedBackOffPolicy();
backoffPolicy.setBackOffPeriod(5000);
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setBackOffPolicy(backoffPolicy);
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(5, retryableExceptions));
return retryTemplate;
}
#Bean
RetryOperationsInterceptor interceptor(RetryOperations retryTemplate) {
RetryOperationsInterceptor interceptor = new RetryOperationsInterceptor();
interceptor.setRecoverer(new CustomMessageRecover());
interceptor.setRetryOperations(retryTemplate);
return interceptor;
// return RetryInterceptorBuilder
// .stateless()
// //.retryOperations(retryTemplate)
// .maxAttempts(5)
// .recoverer(new CustomMessageRecover()).build();
}
public static void main(String[] args) throws InterruptedException {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
System.out.println("Sending message...");
rabbitTemplate.convertAndSend(queueName, "Hello from RabbitMQ!");
context.close();
}
public class Receiver {
public void receiveMessage(String message) throws Exception {
System.out.println("!!!!!!!!Message has been recieved!!!!!!");
throw new EventException("TESTING");
}
}
public class CustomMessageRecover implements MethodInvocationRecoverer<Void> {
#Override
public Void recover(Object[] args, Throwable cause) {
System.out.println("IN THE RECOVER ZONE!!!");
throw new AmqpRejectAndDontRequeueException(cause);
}
}
class EventException extends Exception {
private static final long serialVersionUID = 1L;
public EventException() {}
public EventException(String message) {
super(message);
}
}
}
Now in the code, as you can see I am using RetryOperationsInterceptor in order to intercept and check to see what type of exception it is being thrown and base on that, make the decision to either do the retry or not, along with the delay between the retries.
For this I am setting the backoffPolicy and retryPolicy of the RetryTemplate Bean and having that injected into the RetryOperationsInterceptor.
I would appreciate if anyone can help me out and tell me why the retry and the delay between the retries are not working. My messages are going directly to the dead letter exchange without the retries and the delays happening.
THANK YOU!
Your issue is here:
retryableExceptions.put(EventException.class, false);
Please, find the SimpleRetryPolicy code:
public boolean canRetry(RetryContext context) {
Throwable t = context.getLastThrowable();
return (t == null || retryForException(t)) && context.getRetryCount() < maxAttempts;
}
and further:
private boolean retryForException(Throwable ex) {
return retryableClassifier.classify(ex);
}
Since you specify a false for your EventException, it won't be retryable. Hence any retries and backoffs.

Categories