Receiving JMS messages via Spring integration inbound adapter randomly fails - spring

I am new to this Spring Integration and JMS and i started playing with it. In here i want to create plain jms message via activemq and receive it through spring inbound adapter(message driven).
following is my spring config file
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:stream="http://www.springframework.org/schema/integration/stream"
xmlns:jms="http://www.springframework.org/schema/integration/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/stream
http://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd>
http://www.springframework.org/schema/integration/jms
http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd">
<!-- jms beans -->
<beans:bean id="jms.msgQueue" class="org.apache.activemq.command.ActiveMQQueue">
<beans:constructor-arg value="MSG_QUEUE" />
</beans:bean>
<beans:bean name="jms.connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<beans:property name="brokerURL" value="tcp://localhost:61616" />
</beans:bean>
<!-- spring integration beans -->
<channel id="channels.jms.allMessages">
<queue capacity="1000" />
</channel>
<jms:message-driven-channel-adapter id="adapters.jms.msgAdapter"
connection-factory="jms.connectionFactory"
destination="jms.msgQueue"
channel="channels.jms.allMessages" />
and this is my testing class
package com.bst.jms;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.core.PollableChannel;
public class TestActiveMQ {
public static void main(String[] args){
try{
AbstractApplicationContext context = new ClassPathXmlApplicationContext("app-context.xml");
ConnectionFactory connectionFactory = (ConnectionFactory)context.getBean("jms.connectionFactory");
Destination destination = (Destination)context.getBean("jms.msgQueue");
PollableChannel msgChannel = (PollableChannel) context.getBean("channels.jms.allMessages", PollableChannel.class );
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(destination);
TextMessage textMessage = session.createTextMessage();
textMessage.setText("Message from JMS");
producer.send(textMessage);
System.out.println("--------------- Message Sending ------------------------");
Message<?> received = msgChannel.receive();
String payload = (String) received.getPayload();
System.out.println("Receving message = " + payload);
}catch(JMSException ex){
System.out.println("----------- JMS Exception --------------");
}
}
}
But the thing is i can not guarantee the delivery. some times the program can not receive the message and some tomes it succeeds with some warnings like
Setup of JMS message listener invoker failed for destination 'queue://MSG_QUEUE' - trying to recover. Cause: Connection reset
Could not refresh JMS Connection for destination 'queue://MSG_QUEUE' - retrying in 5000 ms. Cause: Could not connect to broker URL: tcp://localhost:61616. Reason: java.net.ConnectException: Connection refused: connect
Could not refresh JMS Connection for destination 'queue://MSG_QUEUE' - retrying in 5000 ms. Cause: Could not connect to broker URL: tcp://localhost:61616. Reason: java.net.ConnectException: Connection refused: connect
This occurs few times before it succeeds.
Do you guys have any idea about this.
appreciate your help.
thanks,
keth

This just means the broker isn't running when the listener container starts. When using a tcp:// URL you should run the broker in it's own context (or another JVM) before creating this context.

I have tested these code in my STS its working fine .
The only problem in your side is , first start message Broker (say ActiveMQ) then run your project, you can get your required output.
Thanks.

Related

Actuator JMS Health Check Return False Positive With Java Configuration

I have run into an issue where the Actuator probe fails for JMS health even though my routes can connect and produce message to JMS. So in short Actuator is saying it is down but it is working.
Tech stack and tech notes:
Spring-boot: 2.3.1.RELEASE
Camel: 3.4.1
Artemis: 2.11.0
Artemis has been setup to use a user name and password(artemis/artemis).
Using org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory for connection factory.
My route is as simple as chips:
<route id="timer-cluster-producer-route">
<from uri="timer:producer-ticker?delay=5000"/>
<setBody>
<groovy>
result = ["Name":"Johnny"]
</groovy>
</setBody>
<marshal>
<json library="Jackson"/>
</marshal>
<to uri="ref:jms-producer-cluster-event" />
</route>
XML Based Artemis Configuration
With Spring-boot favoring java based configuration I am busy migrating our XML beans accordingly.Thus I took a working beans.xml file pasted into the project and fired up the route and I could send messages flowing and the health check returned OK.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<bean id="jmsConnectionFactory"
class="org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
<property name="user" value="artemis"/>
<property name="password" value="artemis"/>
<property name="connectionLoadBalancingPolicyClassName" value="org.apache.activemq.artemis.api.core.client.loadbalance.RoundRobinConnectionLoadBalancingPolicy"/>
</bean>
<!--org.messaginghub.pooled.jms.JmsPoolConnectionFactory-->
<!--org.apache.activemq.jms.pool.PooledConnectionFactory-->
<bean id="jmsPooledConnectionFactory"
class="org.apache.activemq.jms.pool.PooledConnectionFactory"
init-method="start" destroy-method="stop">
<property name="maxConnections" value="64" />
<property name="MaximumActiveSessionPerConnection"
value="500" />
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory"
ref="jmsPooledConnectionFactory" />
<property name="concurrentConsumers" value="1" />
<property name="artemisStreamingEnabled" value="true"/>
</bean>
<bean id="jms"
class="org.apache.camel.component.jms.JmsComponent">
<property name="configuration" ref="jmsConfig"/>
</bean>
<!-- <bean id="activemq"
class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="configuration" ref="jmsConfig" />
</bean>-->
</beans>
Spring-boot Auto (Black)Magic Configuration
I then used the application.yaml file to configure the artemis connection by using this method as outlined in the Spring-boot documentation. This also worked when my application.yaml file contained the following configuration:
artemis:
user: artemis
host: localhost
password: artemis
pool:
max-sessions-per-connection: 500
enabled: true
max-connections: 16
This worked like a charm.
Brave Attempt At Java Configuration.
So I then went for gold and tried the Java based configuration as outlined below:
#SpringBootApplication
#ImportResource("classpath:/camel/camel.xml")
public class ClusterProducerApplication {
public static void main(String[] args) {
SpringApplication.run(ClusterProducerApplication.class, args);
}
#Bean
public JmsComponent jms() throws JMSException {
// Create the connectionfactory which will be used to connect to Artemis
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory();
cf.setBrokerURL("tcp://localhost:61616");
cf.setUser("artemis");
cf.setPassword("artemis");
//Create connection pool using connection factory
PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory();
pooledConnectionFactory.setMaxConnections(2);
pooledConnectionFactory.setConnectionFactory(cf);
//Create configuration which uses connection factory
JmsConfiguration jmsConfiguration = new JmsConfiguration();
jmsConfiguration.setConcurrentConsumers(2);
jmsConfiguration.setArtemisStreamingEnabled(true);
jmsConfiguration.setConnectionFactory(pooledConnectionFactory);
// Create the Camel JMS component and wire it to our Artemis configuration
JmsComponent jms = new JmsComponent();
jms.setConfiguration(jmsConfiguration);
return jms;
}
}
So when camel starts up I see the following warning logged on start up:
020-07-28 12:33:38.631 WARN 25329 --- [)-192.168.1.158] o.s.boot.actuate.jms.JmsHealthIndicator : JMS health check failed
javax.jms.JMSSecurityException: AMQ229031: Unable to validate user from /127.0.0.1:42028. Username: null; SSL certificate subject DN: unavailable
After the 5sec delay the timer kicks in and message are being produced. I logged into the Artemis console and I can browse the messages and can see them being created. However when I run a get on actuator health I see the following:
"jms": {
"status": "DOWN",
"details": {
"error": "javax.jms.JMSSecurityException: AMQ229031: Unable to validate user from /127.0.0.1:42816. Username: null; SSL certificate subject DN: unavailable"
}
},
This feels like a big of a bug to me.
Observations about connection pooling implementations.
I noticed that AMQ connection pooling has been moved into the following maven dependency:
<dependency>
<groupId>org.messaginghub</groupId>
<artifactId>pooled-jms</artifactId>
</dependency>
I thought let me give that a try as well. It show the same behaviour as outlined above with one more interesting thing. When using org.messaginghub.pooled-jms as the connection pool(recommended by spring-boot docs as well) the following is logged on startup.
2020-07-28 12:41:37.255 INFO 26668 --- [ main] o.m.pooled.jms.JmsPoolConnectionFactory : JMS ConnectionFactory on classpath is not a JMS 2.0+ version.
Which is weird as according to the official repo the connector is JMS 2.0 compliant.
Quick Summary:
It appears that actuator does not pick up the credentials of the connection factory when configuring the JMS component via Java. While a work around exists at the moment by using the spring-boot application.yaml configuration it limits the way you can configure JMS clients on Camel.
So after some digging and reaching out to the Spring-boot people on GitHub, I found what the issue is. When using Java configuration I am configuring the JMS component of Camel with a connection factory. However Spring-boot is completely unaware of this as it is a Camel component. Thus the connection factory used by the JMS needs to be exposed to Spring-boot for it to work.
The fix is relatively simple. See code below:
#Configuration
public class ApplicationConfiguration {
private ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory();
#Bean
public JmsComponent jms() throws JMSException {
// Create the connectionfactory which will be used to connect to Artemis
cf.setBrokerURL("tcp://localhost:61616");
cf.setUser("artemis");
cf.setPassword("artemis");
// Setup Connection pooling
PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory();
pooledConnectionFactory.setMaxConnections(2);
pooledConnectionFactory.setConnectionFactory(cf);
JmsConfiguration jmsConfiguration = new JmsConfiguration();
jmsConfiguration.setConcurrentConsumers(2);
jmsConfiguration.setArtemisStreamingEnabled(true);
jmsConfiguration.setConnectionFactory(pooledConnectionFactory);
// Create the Camel JMS component and wire it to our Artemis connectionfactory
JmsComponent jms = new JmsComponent();
jms.setConfiguration(jmsConfiguration);
return jms;
}
/*
This line will expose the connection factory to Spring-boot.
*/
#Bean
public ConnectionFactory jmsConnectionFactory() {
return cf;
}
}

Is it possible to create a new RabbitMQ queue from a proxy in WSO2 ESB that previously didn’t exist?

We are using WSO2 with RabbitMQ in our project. One requirement is that the consumer of RabbitMQ should generate the queue in case it did not previously exist.
We make the following proxy (without creating the "queue" queue previously in the Rabbit broker):
<?xml version="1.0" encoding="UTF-8"?>
<proxy name="AMQPProxy3" startOnLoad="true" trace="enable" transports="rabbitmq" xmlns="http://ws.apache.org/ns/synapse">
<target>
<inSequence>
<log level="full"/>
<property name="OUT_ONLY" scope="default" type="STRING" value="true"/>
<property name="FORCE_SC_ACCEPTED" scope="axis2" type="STRING" value="true"/>
<send>
<endpoint>
<address uri="http://localhost:8080/greeting"/>
</endpoint>
</send>
</inSequence>
<outSequence/>
<faultSequence/>
</target>
<parameter name="rabbitmq.queue.name">queue</parameter>
<parameter name="rabbitmq.connection.factory">AMQPConnectionFactory</parameter>
</proxy>
The ESB throws the following exception:
TID: [-1] [] [2017-08-25 13:23:06,139] ERROR {org.apache.axis2.transport.rabbitmq.ServiceTaskManager} - Error, Connection already closed AMQPProxy3, Listner id - 302 {org.apache.axis2.transport.rabbitmq.ServiceTaskManager}
com.rabbitmq.client.AlreadyClosedException: channel is already closed due to channel error; protocol method: #method(reply-code=404, reply-text=NOT_FOUND - no queue 'queue' in vhost '/', class-id=50, method-id=10)
In this case, the RabbitMQ consumer using the ESB does not create the queue in case it does not exist.
However, if we use a Java project that uses amqp-client-4.0.2.jar (Java library that gives us the official page of Rabbit) that consumer is able to create the queue.
package com.ing.rabbitmq;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Send2 {
private final static String QUEUE_NAME = "queue";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String message = "{\"id\":100,\"content\":\"Manolito\"}";
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
channel.close();
connection.close();
}
}
Could we get the ESB to create the queue just like if we used the java client (amqp-client-4.0.2.jar)?
I think it should be possible with WSO2 ESB.
Try setting out the following property in your proxy configuration.
<parameter name="rabbitmq.queue.autodeclare">true</parameter>
If that not works, please let me know the WSO2 ESB version you are using.

Starting a simple WebSocket Server with Spring integration

I am trying to start a simple websocket server in a spring integration application.
I build my application thanks to the following dependancies :
dependencies {
compile("org.springframework.boot:spring-boot-starter-integration")
compile("org.springframework:spring-websocket")
compile("org.springframework.integration:spring-integration-ip")
compile("org.springframework.integration:spring-integration-websocket")
compile('com.fasterxml.jackson.core:jackson-databind')
compile("org.springframework.integration:spring-integration-feed")
}
I launch my application thanks to the following Java file :
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
org.springframework.integration.annotation.IntegrationComponentScan;
#Configuration
#EnableAutoConfiguration
#IntegrationComponentScan
public class Application {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext ctx = new SpringApplication("/integration.xml").run(args);
System.out.println("Hit Enter to terminate");
System.in.read();
ctx.close();
}
}
Here is my "integartion.xml" :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:file="http://www.springframework.org/schema/integration/file"
xmlns:feed="http://www.springframework.org/schema/integration/feed"
xmlns:int-ip="http://www.springframework.org/schema/integration/ip"
xmlns:ws="http://www.springframework.org/schema/integration/websocket"
xsi:schemaLocation="http://www.springframework.org/schema/integration/feed http://www.springframework.org/schema/integration/feed/spring-integration-feed.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/file http://www.springframework.org/schema/integration/file/spring-integration-file.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/ip http://www.springframework.org/schema/integration/ip/spring-integration-ip.xsd
http://www.springframework.org/schema/integration/websocket http://www.springframework.org/schema/integration/websocket/spring-integration-websocket.xsd">
<int-ip:udp-inbound-channel-adapter id="udpReceiver"
channel="udpOutChannel"
port="11111"
receive-buffer-size="500"
multicast="true"
multicast-address="225.6.7.8"/>
<int:object-to-string-transformer input-channel="udpOutChannel" output-channel="stringified"/>
<bean id="nmeaParser" class="transformers.NMEAParser"/>
<int:transformer input-channel="stringified" ref="nmeaParser" output-channel="parsedData"/>
<int:channel id="jsondata"/>
<int:object-to-json-transformer input-channel="parsedData" output-channel="jsondata"/>
<ws:server-container id="server" path="/position"/>
<ws:outbound-channel-adapter container="server" channel="jsondata"/>
</beans>
As you can see, I create an UDP inbound channel. This channels works like a charm. I am able to see that data are sucessfully passed to my "NMEAParser" bean.
I think that my Websocket server doesn't work because :
I can't connect to it with wscat... When I try to do :
wscat -c ws://127.0.0.1:8080/position
I receive a "CONNECTION REFUSED", the same message I receive when nothing is launched.
In the Spring log's I can read this :
Starting beans in phase 0
Adding {object-to-string-transformer} as a subscriber to the 'udpOutChannel' channel
Channel 'application:8080.udpOutChannel' has 1 subscriber(s).
started org.springframework.integration.config.ConsumerEndpointFactoryBean#0
Adding {transformer} as a subscriber to the 'stringified' channel
Channel 'application:8080.stringified' has 1 subscriber(s).
started org.springframework.integration.config.ConsumerEndpointFactoryBean#1
Adding {object-to-json-transformer} as a subscriber to the 'parsedData' channel
Channel 'application:8080.parsedData' has 1 subscriber(s).
started org.springframework.integration.config.ConsumerEndpointFactoryBean#2
Adding {websocket:outbound-channel-adapter} as a subscriber to the 'jsondata' channel
Channel 'application:8080.jsondata' has 1 subscriber(s).
started org.springframework.integration.config.ConsumerEndpointFactoryBean#3
Adding {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel
Channel 'application:8080.errorChannel' has 1 subscriber(s).
started _org.springframework.integration.errorLogger
Starting beans in phase 1073741823
started udpReceiver
Started Application in 0.792 seconds (JVM running for 1.121)
We can see here that all my beans are launched, except the bean "server" which is my server container. There is no trace of this bean in this log message.
To write my application I read carrefully this sampe and this doc.
What am I doing wrong, or what I forgot ?
As discussed in the chat after our lengthy comment back and forth, you need org.springframework.boot:spring-boot-starter-websocket in your dependencies, this will bring in spring-boot-starter-web and org.springframework.boot:spring-boot-starter-tomcat transitively, giving you a websocket runtime.

HornetQ connecting to a JMS server

I am currently trying to create a JMS client for a JMS Server both using HornetQ. I did not code the server and I don't know much about hoy it works, I only know how to connect to it: no username, no password and the address is jnp://x.y.z.t:1099 .
I am trying to connect to the server without using JNDI and I am having some trouble. In fact I have found this example: http://anonsvn.jboss.org/repos/hornetq/trunk/examples/jms/instantiate-connection-factory/
and I don't know what do I need to change in the XML files and in the code to make it work.
I had this code, with JNDI:
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A simple polymorphic JMS producer which can work with Queues or Topics which
* uses JNDI to lookup the JMS connection factory and destination
*
*
*/
public final class SimpleProducer {
private static final Logger LOG = LoggerFactory.getLogger(SimpleProducer.class);
private SimpleProducer() {
}
/**
* #param args the destination name to send to and optionally, the number of
* messages to send
*/
public static void main(String[] args) {
Context jndiContext = null;
ConnectionFactory connectionFactory = null;
Connection connection = null;
Session session = null;
Destination destination = null;
MessageProducer producer = null;
String destinationName = null;
final int numMsgs;
if ((args.length < 1) || (args.length > 2)) {
LOG.info("Usage: java SimpleProducer <destination-name> [<number-of-messages>]");
System.exit(1);
}
destinationName = args[0];
LOG.info("Destination name is " + destinationName);
if (args.length == 2) {
numMsgs = (new Integer(args[1])).intValue();
} else {
numMsgs = 1;
}
/*
* Create a JNDI API InitialContext object
*/
try {
jndiContext = new InitialContext();
} catch (NamingException e) {
LOG.info("Could not create JNDI API context: " + e.toString());
System.exit(1);
}
/*
* Look up connection factory and destination.
*/
try {
connectionFactory = (ConnectionFactory)jndiContext.lookup("ConnectionFactory");
destination = (Destination)jndiContext.lookup(destinationName);
} catch (NamingException e) {
LOG.info("JNDI API lookup failed: " + e);
System.exit(1);
}
/*
* Create connection. Create session from connection; false means
* session is not transacted. Create sender and text message. Send
* messages, varying text slightly. Send end-of-messages message.
* Finally, close connection.
*/
try {
connection = connectionFactory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(destination);
TextMessage message = session.createTextMessage();
for (int i = 0; i < numMsgs; i++) {
message.setText("This is message " + (i + 1));
LOG.info("Sending message: " + message.getText());
producer.send(message);
}
/*
* Send a non-text control message indicating end of messages.
*/
producer.send(session.createMessage());
} catch (JMSException e) {
LOG.info("Exception occurred: " + e);
} finally {
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
}
}
}
}
}
with this jndi.properties file:
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url = jnp://x.y.z.t:1099
and everything worked fine. But now I need to do the same thing without JNDI.
The example I gave above (http://anonsvn.jboss.org/repos/hornetq/trunk/examples/jms/instantiate-connection-factory/) shoutd work, but I don't know what to change in the config to make it work, I have never used a JMS client this way, so I'm completelly lost!
These are the config files I'm talking about:
http://anonsvn.jboss.org/repos/hornetq/trunk/examples/jms/instantiate-connection-factory/server0/ . I coulnd't find on the net to what the files correpsond, I'm confused.
Also, the Java code is here:
http://anonsvn.jboss.org/repos/hornetq/trunk/examples/jms/instantiate-connection-factory/src/org/hornetq/jms/example/InstantiateConnectionFactoryExample.java
Thank you in advance
----- EDIT
This is the last vesion of my code:
import java.util.HashMap;
import java.util.Map;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.hornetq.api.core.TransportConfiguration;
import org.hornetq.api.jms.HornetQJMSClient;
import org.hornetq.api.jms.JMSFactoryType;
import org.hornetq.common.example.HornetQExample;
import org.hornetq.core.remoting.impl.netty.NettyConnectorFactory;
import org.hornetq.core.remoting.impl.netty.TransportConstants;
import org.hornetq.jms.client.HornetQConnectionFactory;
public class Snippet extends HornetQExample
{
public static void main(final String[] args)
{
new Snippet().run(args);
}
#Override
public boolean runExample() throws Exception
{
Connection connection = null;
try
{
// Step 1. Directly instantiate the JMS Queue object.
Queue queue = HornetQJMSClient.createQueue("exampleQueue");
// Step 2. Instantiate the TransportConfiguration object which contains the knowledge of what transport to use,
// The server port etc.
Map<String, Object> connectionParams = new HashMap<String, Object>();
connectionParams.put(TransportConstants.PORT_PROP_NAME, 5446);
//My server's port:
//connectionParams.put(TransportConstants.PORT_PROP_NAME, 1099);
TransportConfiguration transportConfiguration = new TransportConfiguration(NettyConnectorFactory.class.getName(),
connectionParams);
// Step 3 Directly instantiate the JMS ConnectionFactory object using that TransportConfiguration
HornetQConnectionFactory cf = HornetQJMSClient.createConnectionFactoryWithoutHA(JMSFactoryType.CF, transportConfiguration);
// Step 4.Create a JMS Connection
connection = cf.createConnection();
// Step 5. Create a JMS Session
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Step 6. Create a JMS Message Producer
MessageProducer producer = session.createProducer(queue);
// Step 7. Create a Text Message
TextMessage message = session.createTextMessage("This is a text message");
System.out.println("Sent message: " + message.getText());
// Step 8. Send the Message
producer.send(message);
// Step 9. Create a JMS Message Consumer
MessageConsumer messageConsumer = session.createConsumer(queue);
// Step 10. Start the Connection
connection.start();
// Step 11. Receive the message
TextMessage messageReceived = (TextMessage)messageConsumer.receive(5000);
System.out.println("Received message: " + messageReceived.getText());
return true;
}
finally
{
if (connection != null)
{
connection.close();
}
}
}
}
This is my hornetq-beans.xml (I disable JNDI)
<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="urn:jboss:bean-deployer:2.0">
<bean name="Naming" class="org.jnp.server.NamingBeanImpl"/>
<!-- JNDI server. Disable this if you don't want JNDI -->
<!-- <bean name="JNDIServer" class="org.jnp.server.Main">
<property name="namingInfo">
<inject bean="Naming"/>
</property>
<property name="port">1099</property>
<!-- <property name="bindAddress">localhost</property>
<property name="bindAddress">jnp://X.Y.Z.T</property>
<property name="rmiPort">1098</property>
<!-- <property name="rmiBindAddress">localhost</property>
<property name="bindAddress">jnp://X.Y.Z.T</property>
</bean>-->
<!-- MBean server -->
<bean name="MBeanServer" class="javax.management.MBeanServer">
<constructor factoryClass="java.lang.management.ManagementFactory"
factoryMethod="getPlatformMBeanServer"/>
</bean>
<!-- The core configuration -->
<bean name="Configuration" class="org.hornetq.core.config.impl.FileConfiguration"/>
<!-- The security manager -->
<bean name="HornetQSecurityManager" class="org.hornetq.spi.core.security.HornetQSecurityManagerImpl">
<start ignored="true"/>
<stop ignored="true"/>
</bean>
<!-- The core server -->
<bean name="HornetQServer" class="org.hornetq.core.server.impl.HornetQServerImpl">
<constructor>
<parameter>
<inject bean="Configuration"/>
</parameter>
<parameter>
<inject bean="MBeanServer"/>
</parameter>
<parameter>
<inject bean="HornetQSecurityManager"/>
</parameter>
</constructor>
<start ignored="true"/>
<stop ignored="true"/>
</bean>
<!-- The JMS server -->
<bean name="JMSServerManager" class="org.hornetq.jms.server.impl.JMSServerManagerImpl">
<constructor>
<parameter>
<inject bean="HornetQServer"/>
</parameter>
</constructor>
</bean>
</deployment>
and my hornetq-jms.xml (same as in the example)
<configuration xmlns="urn:hornetq"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:hornetq /schema/hornetq-jms.xsd">
<!--the queue used by the example-->
<queue name="exampleQueue">
<entry name="/queue/exampleQueue"/>
</queue>
</configuration>
hornetq-users.xml (There is no need to have users to connect to the JMS server, so I commented it):
<configuration xmlns="urn:hornetq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:hornetq /schema/hornetq-users.xsd">
<!-- the default user. this is used where username is null
<defaultuser name="guest" password="guest">
<role name="guest"/>
</defaultuser>-->
</configuration>
My hornetq-configuratio.xml: I am not sur whether I have to put jnp:// in the connectors and acceptors or not. Actually I don't really get much of this....
<configuration xmlns="urn:hornetq"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:hornetq /schema/hornetq-configuration.xsd">
<!-- Connectors -->
<connectors>
<connector name="netty-connector">
<factory-class>org.hornetq.core.remoting.impl.netty.NettyConnectorFactory</factory-class>
<param key="host" value="jnp://X.Y.Z.T"/>
<param key="port" value="5445"/>
</connector>
</connectors>
<!-- Acceptors -->
<acceptors>
<acceptor name="netty-acceptor">
<factory-class>org.hornetq.core.remoting.impl.netty.NettyAcceptorFactory</factory-class>
<param key="host" value="jnp://X.Y.Z.T"/>
<param key="port" value="5445"/>
</acceptor>
</acceptors>
<!-- Other config -->
<security-settings>
<!--security for example queue-->
<security-setting match="jms.queue.exampleQueue">
<permission type="createDurableQueue" roles="guest"/>
<permission type="deleteDurableQueue" roles="guest"/>
<permission type="createNonDurableQueue" roles="guest"/>
<permission type="deleteNonDurableQueue" roles="guest"/>
<permission type="consume" roles="guest"/>
<permission type="send" roles="guest"/>
</security-setting>
</security-settings>
</configuration>
What I get with this code is:
HornetQException[errorCode=2 message=Cannot connect to server(s). Tried with all available servers.]
at org.hornetq.core.client.impl.ServerLocatorImpl.createSessionFactory(ServerLocatorImpl.java:619)
at org.hornetq.jms.client.HornetQConnectionFactory.createConnectionInternal(HornetQConnectionFactory.java:601)
I am using version 2.2.2, by the way ;)
If you want to use HornetQ's native client, look at these documents
http://docs.jboss.org/hornetq/2.2.2.Final/user-manual/en/html_single/#d0e1611
This will likely also help, with direct connections
http://docs.jboss.org/hornetq/2.2.2.Final/user-manual/en/html_single/#d0e2387
You'll want to use their local core client examples.

Spring integration: Receiving JMS messages via adapter randomly fails

I am trying to write channel adapter for JMS queue. I'd like to send message to JMS queue and receive it on Spring Integration's channel.
When I use plain JMS (with ActiveMQ), everything works properly, so I assume bug is somewhere in my Spring code.
So here's my Spring config file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:jms="http://www.springframework.org/schema/integration/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/jms
http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd">
<!-- jms beans -->
<bean id="jms.msgQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="MSG_QUEUE" />
</bean>
<bean name="jms.connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
</bean>
<!-- spring integration beans -->
<int:channel id="channels.jms.allMessages">
<int:queue capacity="1000" />
</int:channel>
<jms:inbound-channel-adapter id="adapters.jms.msgAdapter"
connection-factory="jms.connectionFactory"
destination="jms.msgQueue"
channel="channels.jms.allMessages">
<int:poller fixed-rate="500" />
</jms:inbound-channel-adapter>
</beans>
Here is plain JMS sending-receiving code that works without problems:
#Test
public void testPlainJms() throws JMSException {
MessageProducer producer = session.createProducer(msgQueue);
MessageConsumer consumer = session.createConsumer(msgQueue);
// send to JMS queue
TextMessage textMessage = session.createTextMessage();
textMessage.setText("Message from JMS");
producer.send(textMessage);
connection.start();
javax.jms.Message message = consumer.receive(TIMEOUT);
assertEquals("Message from JMS", ((TextMessage) message).getText());
connection.stop();
}
And here is code using Spring's MessageChannel that usually doesn't work (it sometimes does but I am not able to determine when):
#Test
public void testInboundAdapter() throws JMSException {
MessageProducer producer = session.createProducer(msgQueue);
// send to JMS queue
TextMessage textMessage = session.createTextMessage();
textMessage.setText("Message from JMS");
producer.send(textMessage);
// receive in local channel (using inbound adapter)
Message<?> received = ((PollableChannel) msgChannel).receive(TIMEOUT);
String payload = (String) received.getPayload();
assertEquals("Message from JMS", payload);
}
I am getting NullPointerException on receiving message from pollable msgChannel. Here's how I autowired beans from Spring config to my test class:
#Autowired #Qualifier("jms.msgQueue")
Queue msgQueue;
#Autowired #Qualifier("channels.jms.allMessages")
MessageChannel msgChannel;
#Autowired
ConnectionFactory connectionFactory;
Strange, I am able to reproduce your issue, could be a bug with inbound-channel-adapter, I have something which consistently works though, by changing from an inbound-channel-adapter to message-driven-channel-adapter, try with this:
<jms:message-driven-channel-adapter id="adapters.jms.msgAdapter"
connection-factory="jms.connectionFactory"
destination="jms.msgQueue"
channel="channels.jms.allMessages" />
It fail when it timeout.
Message<?> received = ((PollableChannel) msgChannel).receive(TIMEOUT);
You must check received for null

Resources