Sending Message to QueueChannel - spring

My knowledge of JMS is extremely weak so please bear with me.
I am trying to send a simple message to a Queue Message Channel.
#Autowired
private MessageChannel myChannel = null;
#Test
public void testRecieveMethod() {
Message m = ((QueueChannel)myChannel).receive();
System.out.println("HELLO");
}
The program hangs like its supposed to but when I try and send a message from another program, it doesn't seem to be getting recieved.
private MessageChannel channel = null;
#Test
public void testMessage() {
channel = super.ctx.getBean("myChannel", MessageChannel.class);
jackMessage message = new ameerMessage("Hello my name is jack");
Message<ameerMessage> msg = MessageBuilder.withPayload(message).build();
channel.send(msg, 10000);
}
Here is my applicationContext
<int:channel id="myChannel">
<int:queue capacity="10"/>
</int:channel>
<jms:inbound-channel-adapter id="JmsAdapter"
connection-factory="connectionFactory"
destination="myQueue"
channel="myChannel">
<int:poller fixed-rate = "1000"/>
</jms:inbound-channel-adapter>
<bean id="myQueue"
class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="MYQUEUE"/>
</bean>
<bean name="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL">
<value>tcp://localhost:61616</value>
</property>
</bean>
<bean id="myProcessor"
class="com.jack.springintegration.Processor"/>
</beans>
Not sure where I why the message is not being recieved. Can someone help?

You say "another program". If it's really another program then they are different myChannels.
I think what you want to do is send a message to JMS so the first program's myChannel will get the message from JMS.
You need to use an outbound channel adapter in the second program to send the message to the JMS queue.

Related

JMS Topic Subscriber in Spring using JMS template/ Message Subscriber

I have a simple Spring application for JMS Producer/Subscriber using ActiveMQ with below configuration :
Application Context xml :
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
<property name="userName" value="user" />
<property name="password" value="password" />
</bean>
<bean id="messageDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="messageQueue1" />
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE">
</property>
</bean>
<bean id="springJmsProducer" class="SpringJmsProducer">
<property name="destination" ref="messageDestination" />
<property name="jmsTemplate" ref="jmsTemplate" />
</bean>
<bean id="springJmsConsumer" class="SpringJmsConsumer">
<property name="destination" ref="messageDestination" />
<property name="jmsTemplate" ref="jmsTemplate" />
</bean>
and Below is Spring producer
public class SpringJmsProducer {
private JmsTemplate jmsTemplate;
private Destination destination;
public JmsTemplate getJmsTemplate() {
return jmsTemplate;
}
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public Destination getDestination() {
return destination;
}
public void setDestination(Destination destination) {
this.destination = destination;
}
public void sendMessage(final String msg) {
jmsTemplate.send(destination, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(msg);
}});
}
}
below is Spring Consumer:
public class SpringJmsConsumer {
private JmsTemplate jmsTemplate;
private Destination destination;
public JmsTemplate getJmsTemplate() {
return jmsTemplate;
}
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public Destination getDestination() {
return destination;
}
public void setDestination(Destination destination) {
this.destination = destination;
}
public String receiveMessage() throws JMSException {
TextMessage textMessage =(TextMessage) jmsTemplate.receive(destination);
return textMessage.getText();
}
}
Issue : When i start producer and post messages, and then i start consumer, Consumer is not reading old messages but only reading messages posted after consumer was started. Could anyone please help me how to make this durable subscriber so that messages in queue which are not acknowledged should be read by consumer and also i need to implement Synchronous Consumer not Asynchronous.
I have tried all possible solution but none is working. Any help is highly appreciated
if you want consumer receive messages sent to the topic before he starts you have 2 choice :
1. Use Activemq Retroactive Consumer
Background A retroactive consumer is just a regular JMS Topic consumer
who indicates that at the start of a subscription every attempt should
be used to go back in time and send any old messages (or the last
message sent on that topic) that the consumer may have missed.
See the Subscription Recovery Policy for more detail.
You mark a Consumer as being retroactive as follows:
topic = new ActiveMQTopic("TEST.Topic?consumer.retroactive=true");
http://activemq.apache.org/retroactive-consumer.html
2. Use Durable Subscribers :
Note that the Durable Subscriber receive messages sent to the topic before he starts at the 2nd run
http://activemq.apache.org/manage-durable-subscribers.html
This is possible with DefaultMessageListenerContainer Asynchronously
<bean id="jmsContainer" destroy-method="shutdown"
class="org.springframework.jms.listener.DefaultMessageListenerContainer" >
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="messageDestination" />
<property name="messageListener" ref="messageListenerAdapter" />
<property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE" />
<property name="subscriptionDurable" value="true" />
<property name="clientId" value="UniqueClientId" />
</bean>
<bean id="messageListenerAdapter"
class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg ref="springJmsConsumer" />
</bean>
<bean id="springJmsConsumer" class="SpringJmsConsumer">
</bean>
AND Update your consumer :
public class SpringJmsConsumer implements javax.jms.MessageListener {
public void onMessage(javax.jms.Message message) {
// treat message;
message.acknowledge();
}
}
UPDATE to use
if you want a Synchronous Durable Subscriber, an example
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicSubscriber;
public class SpringJmsConsumer {
private Connection conn;
private TopicSubscriber topicSubscriber;
public SpringJmsConsumer(ConnectionFactory connectionFactory, Topic destination ) {
conn = connectionFactory.createConnection("user", "password");
Session session = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
topicSubscriber = session.createDurableSubscriber(destination, "UniqueClientId");
conn.start();
}
public String receiveMessage() throws JMSException {
TextMessage textMessage = (TextMessage) topicSubscriber.receive();
return textMessage.getText();
}
}
And update springJmsConsumer
<bean id="springJmsConsumer" class="SpringJmsConsumer">
<constructor-arg ref="connectionFactory" />
<constructor-arg ref="messageDestination" />
</bean>
Note that connection failures are not managed by this code.

how to write output channel from itemWriter in spring batch and spring integration?

firstly thanks for attention, i combined spring batch and spring integration, i defined a job flow and retrieve files from ftp adapter and sent to jobChannel, and process on it with spring batch , i want to write to output channel and consume the channel after processing, my code is:
<int-ftp:outbound-gateway id="gatewayGET"
local-directory-expression="'./backup/' +#remoteDirectory"
session-factory="ftpSessionFactory"
request-channel="toGetChannel"
reply-channel="toProcessChannel"
command="get"
temporary-file-suffix=".writing"
command-options="-P"
expression="payload.remoteDirectory + '/' + payload.filename"/>
<int:channel id="toProcessChannel">
<int:interceptors>
<int:wire-tap channel="logger2"/>
</int:interceptors>
</int:channel>
<int:channel id="outboundJobRequestChannel"/>
<int:channel id="replyJobChannel"/>
<service-activator input-channel="jobLaunchReplyChannel"/>
<int:transformer input-channel="toProcessChannel" output-channel="outboundJobRequestChannel">
<bean class="ir.isc.macna.configuration.FileMessageToJobRequest">
<property name="fileParameterName" value="fileName"/>
</bean>
</int:transformer>
<batch-int:job-launching-gateway request-channel="outboundJobRequestChannel"
reply-channel="jobLaunchReplyChannel"/>
and my writer code is:
#Component
#StepScope
public class MacnaFileWriter implements ChunkMessageChannelItemWriter<FileInfo> {
#Autowired
#Qualifier("replyJobChannel")
private MessageChannel messageChannel;
#Override
public void write(List<? extends FileInfo> list) throws Exception {
// send Message to replyJobChannel channel with Send method
}
}
and use ftp adapter to write files on the server:
<int-ftp:outbound-gateway session-factory="ftpSessionFactory"
request-channel="replyJob"
command="mput"
auto-create-directory="true"
expression="payload"
remote-directory-expression="payload.remoteDirectory + '/' + payload.filename + '.out'"
reply-channel="output"/>
Is this standard way to run batch job and consume result?
MessageChannel has send method. For this purpose you can use MessageBuilder to create a Message<?> for your payload.
Since it is just a <channel> there is no difference with any other MessageChannel to consume. The standard way define a consumer like this:
<service-activator input-channel="jobLaunchReplyChannel"/>
However it isn't clear why you are going to send messages to the jobLaunchReplyChannel, when this channel is specific for the result from <batch-int:job-launching-gateway> (JobLaunchingGateway class):
jobExecution = this.jobLaunchingMessageHandler.launch(jobLaunchRequest);
So, that jobExecution is sent to the jobLaunchReplyChannel.
See Spring Batch Integration Documentation.
Maybe do you need AsyncItemWriter or ChunkMessageChannelItemWriter?

Stop a spring jms message listener

I have a scenario where i need to stop the spring's DefaultMessageListenerContainer and then later start that again. I have 10 different DefaultMessageListenerContainer listening to 10 different queue.
All 10 different containers are calling the same method of same message listener class.
Now i want to stop the messagelistenercontainer for a particular queue depending on the exception i get in onMessage method.
Please suggest me how i can achieve the above scenario.
Below is my listener configuration
<bean id="msglistenerForAuditError" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsFactory"/>
<property name="sessionTransacted" value="true"/>
<property name="destinationName" value="test.audit.error2"/>
<property name="messageListener" ref="auditerrorListener" />
</bean>
<bean id="msglistenerForAuditEvent" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsFactory"/>
<property name="sessionTransacted" value="true"/>
<property name="destinationName" value="test.audit.event2"/>
<property name="messageListener" ref="auditerrorListener" />
</bean>
The DefaultMessageListenerContainer is a lifecycle bean and as such it exposes a start and a stop method that you can use to start and stop the listener, respectively.
You can build a service on your own that is gathering all known instances in the context and you can then loop over those to stop the containers, something like
#Service
public class MyService {
private final Collection<DefaultMessageListenerContainer> containers;
#Autowired
public MyService(Collection<DefaultMessageListenerContainer> containers) {
this.containers = containers;
}
public void stopAll() {
// iterate over the collection and call "stop()" on each item
}
}
That being said:
You should not invoke this service as part of a message listener as attempting to stop the container while the thread is processing a message will have weird side effect
The whole use case looks suspicious to me. Your message listeners should be resilient and, more importantly, they should be independent of each other; if you are stopping listener A because listener B threw an exception, something is definitely wrong in your design
stop method on DefaultMessageListenerContainer did not worked but shutdown method worked perfectly.
for(DefaultMessageListenerContainer defaultCont:containers){
defaultCont.shutdown();
}
Injecting a collection of DefaultMessageListenerContainer did not work for me, I use Spring Boot 1.4.x, with Spring 4.3.x
Here's how I solved it:
package org.example.queue;
import org.springframework.jms.config.JmsListenerEndpointRegistry;
//import other stuffs
#Component
public class QueueManager {
#Autowired
JmsListenerEndpointRegistry endpointRegistry;
public void shutdown() {
endpointRegistry.getListenerContainers().forEach((container) -> {
if (container.isRunning()) {
log.debug("Shutting down listener: " + container.getClass().getName());
container.stop();
}
});
}
public void start() {
endpointRegistry.getListenerContainers().forEach((container) -> {
if (!container.isRunning()) {
log.debug("Starting listener: " + container.getClass().getName());
container.start();
}
});
}
}

Spring splitter/aggregator handling exceptions

Version : spring-integration-core - 2.2.3
Here is the simplified version of my splitter/aggregator setup.
<task:executor id="taskExecutor" pool-size="${pool.size}"
queue-capacity="${queue.capacity}"
rejection-policy="CALLER_RUNS" keep-alive="120"/>
<int:channel id="service-requests"/>
<int:channel id="service-request"/>
<int:channel id="channel-1">
<int:dispatcher task-executor="taskExecutor" failover="false"/>
</int:channel>
<int:channel id="channel-2">
<int:dispatcher task-executor="taskExecutor" failover="false"/>
</int:channel>
<int:gateway id="myServiceRequestor" default-reply-timeout="${reply.timeout}"
default-reply-channel="service-aggregated-reply"
default-request-channel="service-request"
service-interface="com.blah.blah.MyServiceRequestor"/>
<int:splitter input-channel="service-request"
ref="serviceSplitter" output-channel="service-requests"/>
<!-- To split the request and return a java.util.Collection of Type1 and Type2 -->
<bean id="serviceSplitter" class="com.blah.blah.ServiceSplitter"/>
<int:payload-type-router input-channel="service-requests" resolution-required="true">
<int:mapping
type="com.blah.blah.Type1"
channel="channel-1"/>
<int:mapping
type="com.blah.blah.Type2"
channel="channel-2"/>
</int:payload-type-router>
<!-- myService is a bean where processType1 & processType2 method is there to process the payload -->
<int:service-activator input-channel="channel-1"
method="processType1" output-channel="service-reply" requires-reply="true"
ref="myService"/>
<int:service-activator input-channel="channel-2"
method="processType2" output-channel="service-reply" requires-reply="true"
ref="myService"/>
<int:publish-subscribe-channel id="service-reply" task-executor="taskExecutor"/>
<!-- myServiceAggregator has a aggregate method which takes a Collection as argument(aggregated response from myService) -->
<int:aggregator input-channel="service-reply"
method="aggregate" ref="myServiceAggregator"
output-channel="service-aggregated-reply"
send-partial-result-on-expiry="false"
message-store="myResultMessageStore"
expire-groups-upon-completion="true"/>
<bean id="myResultMessageStore" class="org.springframework.integration.store.SimpleMessageStore" />
<bean id="myResultMessageStoreReaper" class="org.springframework.integration.store.MessageGroupStoreReaper">
<property name="messageGroupStore" ref="myResultMessageStore" />
<property name="timeout" value="2000" />
</bean>
<task:scheduled-tasks>
<task:scheduled ref="myResultMessageStoreReaper" method="run" fixed-rate="10000" />
</task:scheduled-tasks>
If the processType1/processType2 method in mySevice throws a RuntimeException, then it tries to send the message to an error channel(i believe spring does it by default) and the message payload in error channel stays on in heap and not getting garbage collected.
Updated More Info:
For my comment on error channel. I debugged the code and found that ErrorHandlingTaskExecutor is trying to use a MessagePublishingErrorHandler which inturn sending the message to the channel returned by MessagePublishingErrorHandler.resolveErrorChannel method.
Code snippet from ErrorHandlingTaskExecutor.java
public void execute(final Runnable task) {
this.executor.execute(new Runnable() {
public void run() {
try {
task.run();
}
catch (Throwable t) {
errorHandler.handleError(t); /// This is the part which sends the message in to error channel.
}
}
});
}
Code snipper from MessagePublishingErrorHandler.java
public final void handleError(Throwable t) {
MessageChannel errorChannel = this.resolveErrorChannel(t);
boolean sent = false;
if (errorChannel != null) {
try {
if (this.sendTimeout >= 0) {
sent = errorChannel.send(new ErrorMessage(t), this.sendTimeout);
.....
When i take a heap dump, I always see the reference to the payload message(which i believe is maintained in the above channel) and not getting GC'ed.
Would like to know what is the correct way to handle this case or if i'm missing any in my config?
Also is it possible to tell spring to discard the payload(instead of sending it to error channel) in case of any exception thrown by the service activator method?
Looking forward for your inputs.
Thanks.
You don't have an error-channel defined on your gateway so we won't send it there, we'll just throw an exception to the caller.
However, the partial group is sitting in the aggregator and will never complete. You need to configure a MessageGroupStoreReaper as shown in the reference manual (or set a group-timeout in Spring Integration 4.0.x) to discard the partial group.

ActiveMQ, Camel, Spring, simple route not working

I am having some trouble with a simple camel route that should be grabbing a message from an ActiveMQ topic that I have and then printing out the contents of the messages to the console through the use of log.
Right now all that it is is the camel-context.xml, and a java class that is producing the topic in ActiveMQ and adding a simple string message to the queue. I am using the ActiveMQ interface to check to see if the topic is being created, and it is, but my message is not being added to the topic nor is it being routed through the camel route. Running main I can get the output of my sys out to the console, and I see that 1 message is "enqueued" and 1 message is "dequeued" in the activemq interface. I just do not get any output to the console from the "log message" in my route.
Any help or tips would be greatly appreciated since I am new to all 3 of these technologies, and I just want to get this simple "Hello World" working.
Thank you! The two files are found below:
After further testing I think that it just has something to do with the way that I am trying to log the contents of the message, because I know that it is picking up my camel route because I added a second topic and told the camel route to route the messages to it like the following:
to uri="activemq:topic:My.SecondTestTopic"
and I am able to see if being redirected to that queue in the activeMQ interface.
TestMessageProducer.java
package com.backend;
import javax.jms.*;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class TestMessageProducer {
private static String url = ActiveMQConnection.DEFAULT_BROKER_URL;
public static void main(String[] args) throws JMSException {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("My.TestTopic");
MessageProducer producer = session.createProducer(topic);
TextMessage message = session.createTextMessage();
message.setText("THIS IS A TEST TEXT MESSAGE BEING SENT TO THE TOPIC AND HOPEFULLY BEING PICKED UP BY THE" +
"CAMEL ROUTE");
producer.send(message);
System.out.println("Sent message '" + message.getText() + "'");
connection.close();
}
}
Camel-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<spring:beans xmlns:spring="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://camel.apache.org/schema/spring"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:alch="http://service.alchemy.kobie.com/"
xsi:schemaLocation="http://www.springframework.org/schema/beans classpath:META-INF/spring/spring-beans.xsd
http://camel.apache.org/schema/spring classpath:META-INF/spring/camel-spring.xsd">
<!-- load properties -->
<spring:bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<spring:property name="locations" value="file:backend.properties" />
</spring:bean>
<spring:bean id="properties"
class="org.apache.camel.component.properties.PropertiesComponent">
<spring:property name="location" value="file:backend.properties" />
</spring:bean>
<spring:bean id="jmsConnectionFactory"
class="org.apache.activemq.ActiveMQConnectionFactory">
<spring:property name="brokerURL" value="tcp://0.0.0.0:61616?useLocalHost=true" />
</spring:bean>
<spring:bean id="pooledConnectionFactory"
class="org.apache.activemq.pool.PooledConnectionFactory">
<spring:property name="maxConnections" value="8" />
<spring:property name="maximumActive" value="500" />
<spring:property name="connectionFactory" ref="jmsConnectionFactory" />
</spring:bean>
<spring:bean id="jmsConfig"
class="org.apache.camel.component.jms.JmsConfiguration">
<spring:property name="connectionFactory" ref="pooledConnectionFactory"/>
<spring:property name="transacted" value="false"/>
<spring:property name="concurrentConsumers" value="1"/>
</spring:bean>
<spring:bean id="activemq"
class="org.apache.activemq.camel.component.ActiveMQComponent">
<spring:property name="configuration" ref="jmsConfig"/>
</spring:bean>
<!-- camel configuration -->
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="activemq:topic:My.TestTopic"/>
<log message="Output of message from Queue: ${in.body}"/>
<to uri="activemq:topic:My.SecondTestTopic" />
</route>
And you have started the Camel application first. Eg as you send non persistent messages to a topic. And if there is no active subscribers when sending, then the messages is not received by anybody. You may want to use persistent durable topic instead.
I suspect you are creating two seperate instances of an ActiveMQ broker. Can you update your TestMessageProducer to use the URL tcp://localhost:61616 ? Also, can you use jconsole to check the topic activity in the activemq instance on both VMs ?
=== UPDATE ===
I missed the bit about you verifying that the 2nd topic did receive the message, so your route is working.... Must be the logger. If you have the camel source code in your IDE, you could turn the debugger on and place a break point on
org.apache.camel.component.log.LogProducer
.process(Exchange exchange, AsyncCallback callback)
to see what happens and if it is called. Which logging package do you have configured ?

Resources