Custom jms listener with apache camel - java

I'm using Apache Camel and Apache ActiveMQ and need to react to connection closing/resuming.
I have tried to use ActiveMQConnectionFactory custom transportListener, but couldn't catch the moment when connection resumed. So I turned to custom JmsListener.
I configure component like this:
private void setJms(String suffix) {
ActiveMQComponent amqComponent = new ActiveMQComponent();
JmsConfiguration jmsConfiguration = new JmsConfiguration();
Persistence persistence = AppBeans.get("cuba_Persistence");
jmsConfiguration.setExceptionListener(e -> {
log.debug("debug");
persistence.runInTransaction(em -> {
//actions
});
});
jmsConfiguration.setConsumerType(ConsumerType.Custom);
jmsConfiguration.setMessageListenerContainerFactory(MyMessageListenerContainer::new);
jmsConfiguration.setConnectionFactory(connectionFactory);
jmsConfiguration.setConcurrentConsumers(count);
jmsConfiguration.setPreserveMessageQos(true);
jmsConfiguration.setTransacted(true);
jmsConfiguration.setCacheLevelName("CACHE_CONSUMER");
amqComponent.setConfiguration(jmsConfiguration);
context.addComponent(connectionSettings.getJmsName() + suffix, amqComponent);
}
////
and listener extending default - basically i just need to do something after resuming connection
private class MyMessageListenerContainer extends DefaultJmsMessageListenerContainer {
public MyMessageListenerContainer(JmsEndpoint endpoint) {
super(endpoint);
}
#Override
protected void refreshConnectionUntilSuccessful() {
super.refreshConnectionUntilSuccessful();
log.debug("WHOOOF!");
}
}
I can catch exceptions, but still have troubles with resuming. I get following messages:
MyMessageListenerContainer - Shutting down JMS listener container
MyMessageListenerContainer - Waiting for shutdown of message listener invokers
MyMessageListenerContainer - Still waiting for shutdown of 3 message listener invokers
Can somebody help me with my problem? Maybe there is another way to do some actions during exeptions/resuming connection to activemq?
Thank you.

Related

Invoking our Custom MessageListenerContainer in JMS

I am using a JMS queue.
I am attaching my own Listener name as MyMessageListenerContainer to endpoint using following way but it is not getting picked up.
JmsQueueEndpoint jmsQueueEndpoint = (JmsQueueEndpoint) endpoint;
// my own listener.
MyMessageListenerContainer myMessageListenerContainer = new MyMessageListenerContainer();
//overriden below method of the JmsComponent
configureMessageListenerContainer(myMessageListenerContainer, endpoint);
// MyMessageListenerContainer implementation
class MyMessageListenerContainer extends DefaultMessageListenerContainer {
#Override
protected void refreshConnectionUntilSuccessful() {
System.out.println("In My message listener **********");
super.refreshConnectionUntilSuccessful();
}
}
When the broker is down, ideally it should pickup this class and log In My message listener **********.
But in my case it is invoking DefaultMessageListenerContainer method.
Can somebody help me here ?

How to configure and enable a JMS Listener to consume messages based on some condition?

I have two listeners(fooMessages and barMessages) in my application, and both of them are connected to same queue. At one point of time only one listener will consume the message from the queue based on some condition. In application.yml file if fooEnabled is set to true then fooMessages listener should consume the messages and if fooEnabled is set to false then barMessages listener should consume the messages.
Listeners:
#JMSListener(destination="${queueName}", selector = "${selectorName}")
public void fooMessages(Message message) {
// foo logic
}
#JMSListener(destination="${queueName}", selector = "${selectorName}")
public void barMessages(Message message) {
// bar logic
}
application.yml file:
queueName: myqueue
selectorName: "priority=medium"
fooEnable: true
How can I configure the listener to handle this scenario?
Give each listener an id
Configure the container factory to not automatically start the listeners https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties.integration.spring.jms.listener.auto-startup
Start the container(s) manually using the JmsListenerEndpointRegistry bean - registry.getListenerContainer("fooListener").start();
#Bean
ApplicationRunner runner(#Value("${fooEnabled}") boolean fooEnabled,
#Value("${barEnabled}") boolean barEnabled, JmsListenerEndpointRegistry registry) {
return args -> {
if (fooEnabled) ...
}
}

Using the Java Spring Boot Framework to subscribe to STOMP messages

I am attempting to write a Spring service which subscribes to an external read only STOMP broker and read/process the messages it publishes.
The messages are pushed to the topic "/topic/TRAIN_MVT_ALL_TOC" by a rail company. I can successfully connect to the topic, but can't seem to be able to instantiate a listener to its messages.
I have set up a Spring #Configuration class to connect to this and after running the application it appears to connect correctly.
I've also created the message handling routine, using the #MessageMapping annotation to listen to the particular topic I'm interested in ("TRAIN_MVT_ALL_TOC"). The problem is that it never seems to get called.
Configuration Class code:`
#Configuration
#EnableWebSocketMessageBroker
public class StompConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/TRAIN_MVT_ALL_TOC").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableStompBrokerRelay("/topic")
.setRelayHost("datafeeds.networkrail.co.uk")
.setRelayPort(61618)
.setSystemLogin("MYEMAIL")
.setSystemPasscode("MYPASSWORD")
;
}
}
Message handler code:
#MessageMapping("/TRAIN_MVT_ALL_TOC")
public void onMessage(#Payload String message) throws Exception {
System.out.println(message);
}
The following log entry is output to the console, indicating that the connection was successful.
o.s.w.s.c.WebSocketMessageBrokerStats : WebSocketSession[0 current WS(0)-HttpStream(0)-HttpPoll(0), 0 total, 0 closed abnormally (0 connect failure, 0 send limit, 0 transport error)], stompSubProtocol[processed CONNECT(0)-CONNECTED(0)-DISCONNECT(0)], stompBrokerRelay[1 sessions, ReactorNettyTcpClient[TcpClient: connecting to datafeeds.networkrail.co.uk:61618] (available), processed CONNECT(1)-CONNECTED(1)-DISCONNECT(0)], inboundChannel[pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], outboundChannelpool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], sockJsScheduler[pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
The message never gets printed however. I have been trying to get to the bottom of this one for a few days now so any help would be hugely appreciated.
The messages are pushed to the topic "/topic/TRAIN_MVT_ALL_TOC" by a rail company. I can successfully connect to the topic, but can't seem to be able to instantiate a listener to its messages.
You mean a listener on client side, for example a sockjs client?
#MessageMapping("/TRAIN_MVT_ALL_TOC")
public void onMessage(#Payload String message) throws Exception {
System.out.println(message);
}
You do not return anything, you need to send it to a topic like this:
#MessageMapping("/TRAIN_MVT_ALL_TOC")
#SendTo("/topic/TRAIN_MVT_ALL_TOC")
public Greeting onMessage(HelloMessage message) throws Exception {
return new Greeting("hello");
}
Or if your constructor has a SimpMessageSendingOperations in its parameters (should be autoWired by spring boot itself) you can send mutiple messages to the same topic like this:
#Autowired
public Constructor(SimpMessageSendingOperations messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
#MessageMapping(WebSockets.READER_MAPPING)
public void streamOverWebsocket(HelloMessage message) throws Throwable {
String topicUrl = "/topic/TRAIN_MVT_ALL_TOC";
messagingTemplate.convertAndSend(topicUrl, new Message("response 1"));
messagingTemplate.convertAndSend(topicUrl, new Message("response 2"));
...
}
it's also best to wrap you incoming and outgoing in a defined class. That way it's easier to serialize and deserialize it.
sources:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/messaging/simp/SimpMessageSendingOperations.html
https://spring.io/guides/gs/messaging-stomp-websocket/

Issue testing spring cloud SQS Listener

Environment
Spring Boot: 1.5.13.RELEASE
Cloud: Edgware.SR3
Cloud AWS: 1.2.2.RELEASE
Java 8
OSX 10.13.4
Problem
I am trying to write an integration test for SQS.
I have a local running localstack docker container with SQS running on TCP/4576
In my test code I define an SQS client with the endpoint set to local 4576 and can successfully connect and create a queue, send a message and delete a queue. I can also use the SQS client to receive messages and pick up the message that I sent.
My problem is that if I remove the code that is manually receiving the message in order to allow another component to get the message nothing seems to be happening. I have a spring component annotated as follows:
Listener
#Component
public class MyListener {
#SqsListener(value = "my_queue", deletionPolicy = ON_SUCCESS)
public void receive(final MyMsg msg) {
System.out.println("GOT THE MESSAGE: "+ msg.toString());
}
}
Test
#RunWith(SpringRunner.class)
#SpringBootTest(properties = "spring.profiles.active=test")
public class MyTest {
#Autowired
private AmazonSQSAsync amazonSQS;
#Autowired
private SimpleMessageListenerContainer container;
private String queueUrl;
#Before
public void setUp() {
queueUrl = amazonSQS.createQueue("my_queue").getQueueUrl();
}
#After
public void tearDown() {
amazonSQS.deleteQueue(queueUrl);
}
#Test
public void name() throws InterruptedException {
amazonSQS.sendMessage(new SendMessageRequest(queueUrl, "hello"));
System.out.println("isRunning:" + container.isRunning());
System.out.println("isActive:" + container.isActive());
System.out.println("isRunningOnQueue:" + container.isRunning("my_queue"));
Thread.sleep(30_000);
System.out.println("GOT MESSAGE: " + amazonSQS.receiveMessage(queueUrl).getMessages().size());
}
#TestConfiguration
#EnableSqs
public static class SQSConfiguration {
#Primary
#Bean(destroyMethod = "shutdown")
public AmazonSQSAsync amazonSQS() {
final AwsClientBuilder.EndpointConfiguration endpoint = new AwsClientBuilder.EndpointConfiguration("http://127.0.0.1:4576", "eu-west-1");
return new AmazonSQSBufferedAsyncClient(AmazonSQSAsyncClientBuilder
.standard()
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("key", "secret")))
.withEndpointConfiguration(endpoint)
.build());
}
}
}
In the test logs I see:
o.s.c.a.m.listener.QueueMessageHandler : 1 message handler methods found on class MyListener: {public void MyListener.receive(MyMsg)=org.springframework.cloud.aws.messaging.listener.QueueMessageHandler$MappingInformation#1cd4082a}
2018-05-31 22:50:39.582 INFO 16329 ---
o.s.c.a.m.listener.QueueMessageHandler : Mapped "org.springframework.cloud.aws.messaging.listener.QueueMessageHandler$MappingInformation#1cd4082a" onto public void MyListener.receive(MyMsg)
Followed by:
isRunning:true
isActive:true
isRunningOnQueue:false
GOT MESSAGE: 1
This demonstrates that in the 30 second pause between sending the message the container didn't pick it up and when I manually poll for the message it is there on the queue and I can consume it.
My question is, why isn't the listener being invoked and why is the isRunningOnQueue:false line suggesting that it's not auto started for that queue?
Note that I also tried setting my own SimpleMessageListenerContainer bean with autostart set to true explicitly (the default anyway) and observed no change in behaviour. I thought that the org.springframework.cloud.aws.messaging.config.annotation.SqsConfiguration#simpleMessageListenerContainer that is set up by #EnableSqs ought to configure an auto started SimpleMessageListenerContainer that should be polling for me message.
I have also set
logging.level.org.apache.http=DEBUG
logging.level.org.springframework.cloud=DEBUG
in my test properties and can see the HTTP calls create the queue, send a message and delete etc but no HTTP calls to receive (apart from my manual one at the end of the test).
I figured this out after some tinkering.
Even if the simple message container factory is set to not auto start, it seems to do its initialisation anyway, which involves determining whether the queue exists.
In this case, the queue is created in my test in the setup method - but sadly this is after the spring context is set up which means that an exception occurs.
I fixed this by simply moving the queue creation to the context creation of the SQS client (which happens before the message container is created). i.e.:
#Bean(destroyMethod = "shutdown")
public AmazonSQSAsync amazonSQS() {
final AwsClientBuilder.EndpointConfiguration endpoint = new AwsClientBuilder.EndpointConfiguration("http://localhost:4576", "eu-west-1");
final AmazonSQSBufferedAsyncClient client = new AmazonSQSBufferedAsyncClient(AmazonSQSAsyncClientBuilder
.standard()
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("dummyKey", "dummySecret")))
.withEndpointConfiguration(endpoint)
.build());
client.createQueue("test-queue");
return client;
}

JMS 2.0: DurableConsumer does not receive messages

I'm rather new to programming in the Java EE environment, so this question will probably sound amateurish, but here goes:
I'm writing a simple JMS application for demonstration purposes. One of the features that has to be implemented is the ability to get messages from a topic after setting a message selector in a dynamic manner, menaing the user has to be able to set certain attributes that will determine whether he gets a message or not. The messages are sent from a different application that is running on the same local server as the application that is receiving the messages.
So, I'm using injected JMSContext components on both the sender side and on the receiver side to handle the messaging itself.
Here are the functions for sending
#Inject
#JMSConnectionFactory("jms/myConnectionFactory")
JMSContext context;
#Resource(lookup = "jms/myTopic")
private Topic topic;
//some more code
public void produceTopicForCreate(Object obj) {
ObjectMessage message = contextCreate.createObjectMessage(obj);
try {
//setting properties
} catch (JMSException ex) {
//logging
}
context.createProducer().send(topic, message)
}
And on the receiver side
#Inject
#JMSConnectionFactory("jms/myConnectionFactory")
private JMSContext context;
#Resource(lookup = "jms/myTopic")
private Topic topic
private JMSConsumer consumer;
private List<MyClass> listOfMessages;
//more code
public void subscribe(String selector) {
this.consumer = this.context.createDurableConsumer(topic, "durableClient", selector, false);
}
public void receiveMessage() {
try {
this.listOfMessages.add(this.consumer.receiveBody(MyClass.class));
} catch (Exception e) {
//logging
}
}
So, as you can see, I have created a durable consumer to consume messages from the topic. Now, whenever I try to invoke the receiveMessage method after a message has been sent to the topic, I get an exception, stating that the "Producer is closed". I looked all over the net, but found no indication as to what is the problem.
If anyone here could help in any way, I would greatly appreciate it! Thanks in advance!
Some important details:
the bean that is doing the sending is RequestScoped in app A
the bean that is doing the receiving is a Singleton the implements
the the environment is GlassFish 4.1/NetBeans 8.1

Categories