ActiveMQ Artemis 2.10.1 ignoring retry settings in TomEE - apache-tomee

I downloaded latest ActiveMQ Artemis 2.10.1 (Windows 10, JDK 8) and can't get the address-settings to take affect. Reading the documentation (not much) on line I edited the broker.xml by adding:
<address-settings>
<address-setting match="BETATESTQ">
<dead-letter-address>BETATESTQ_DLQ</dead-letter-address>
<expiry-address>BETATESTQ_EXPIRY</expiry-address>
<redelivery-delay>30000</redelivery-delay>
<redelivery-delay-multiplier>1.5</redelivery-delay-multiplier>
<redelivery-collision-avoidance-factor>0.15</redelivery-collision-avoidance-factor>
<max-redelivery-delay>100000</max-redelivery-delay>
<max-delivery-attempts>999</max-delivery-attempts>
</address-setting>
</address-settings>
<addresses>
<address name="BETATESTQ_DLQ">
<anycast>
<queue name="BETATESTQ_DLQ" />
</anycast>
</address>
<address name="BETATESTQ_EXPIRY">
<anycast>
<queue name="BETATESTQ_EXPIRY" />
</anycast>
</address>
<address name="BETATESTQ">
<anycast>
<queue name="BETATESTQ" />
</anycast>
</address>
</addresses>
Everything else is default broker.xml when create a broker.
It seems to always use default values for redelivery-delay, max-redelivery-delay, max-delivery-attempts. It does read the dead letter and expiry values correctly. I can see the message getting retried in TomEE logs and shows up in Artemis console and moves to dead letter queue when done.
I am not passing in and delivery or retry data when put it on the queue initially (using bare minimal Java J2EE code).
How can I get it to not ignore redelivery-delay, max-redelivery-delay, max-delivery-attempts?
I connected to the queue with Apache TomEE (not using built-in ActiveMQ 5.x in TomEE but pointing it to ActiveMQ Artemis)
JMS listener:
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.*;
#MessageDriven(name = "BETATESTQ", activationConfig = {
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "destination", propertyValue = "BETATESTQ"),
#ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge")
})
public class BetaTestQueueListener implements MessageListener, java.io.Serializable {
private static final long serialVersionUID = 1L;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void onMessage(Message rcvMessage) {
System.out.println("omMessage throw runtime exception");
throw new RuntimeException("trigger retry");
}
}
12/20/19 - Working values I found for TomEE 8.0.0:
tomee.xml:
<?xml version="1.0" encoding="UTF-8"?>
<tomee>
<Resource id="artemis" class-name="org.apache.activemq.artemis.ra.ActiveMQResourceAdapter">
ConnectorClassName=org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory
ConnectionParameters=host=127.0.0.1;port=61617;needClientAuth=false;sslEnabled=true;keyStorePath=../ssl/server_jks_keystore.jks;keyStorePassword=mypassword;trustStorePath=../ssl/client_jks_truststore.jks;trustStorePassword=mypassword;trustAll=true;verifyHost=false;wantClientAuth=false;needClientAuth=false;keyStoreProvider=JKS;trustSToreProvider=JKS
UserName=admin
Password=admin
JndiParams=java.naming.factory.initial=org.apache.openejb.core.OpenEJBInitialContextFactory
</Resource>
<Resource id="MyJmsConnectionFactory"
class-name="org.apache.activemq.artemis.jms.client.ActiveMQXAConnectionFactory"
constructor="uri,username,password"
type="javax.jms.ConnectionFactory">
uri=tcp://localhost:61617?needClientAuth=false;sslEnabled=true;keyStorePath=C:/apache-tomee-plus-8.0.0/ssl/server_jks_keystore.jks;keyStorePassword=mypassword;trustStorePath=C:/apache-tomee-plus-8.0.0/ssl/client_jks_truststore.jks;trustStorePassword=mypassword;trustAll=true;verifyHost=false;wantClientAuth=false;needClientAuth=false;keyStoreProvider=JKS;trustSToreProvider=JKS
username=admin
password=admin
TransactionSupport=xa
PoolMaxSize=20
PoolMinSize=0
</Resource>
<Resource id="BETATESTQ_DLQ"
class-name="org.apache.activemq.artemis.api.jms.ActiveMQJMSClient"
constructor="name"
factory-name="createQueue"
type="javax.jms.Queue">
name=BETATESTQ_DLQ
</Resource>
<Resource id="BETATESTQ"
class-name="org.apache.activemq.artemis.api.jms.ActiveMQJMSClient"
constructor="name"
factory-name="createQueue"
type="javax.jms.Queue">
name=BETATESTQ
</Resource>
<Container id="mdb" type="MESSAGE">
InstanceLimit = -1
ResourceAdapter = artemis
ActivationSpecClass = org.apache.activemq.artemis.ra.inflow.ActiveMQActivationSpec
</Container>
</tomee>
Java EE class to send JMS message:
import java.util.*;
import javax.jms.*;
import org.slf4j.*;
public final class JmsPublisherInstance2 implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory.getLogger(JmsPublisherInstance2.class);
public void send( String msg,
ConnectionFactory connectionFactory,
Queue queue ) throws Exception {
Session session = null;
MessageProducer producer = null;
TextMessage message = null;
Connection connection = null;
try {
connection = connectionFactory.createConnection();
connection.start();
session = connection.createSession(true, Session.SESSION_TRANSACTED);
producer = session.createProducer(queue);
message = session.createTextMessage(msg);
producer.send(message);
session.commit();
} catch (Exception e) {
session.rollback();
LOG.error(e.getMessage(), e);
throw e;
} finally {
if (session!=null ) {
session.close();
}
if (connection!=null ) {
connection.close();
}
}
}
}
Java EE listener:
import java.io.*;
import javax.annotation.*;
import javax.ejb.*;
import javax.jms.*;
#MessageDriven ( name = "BetaTESTQMDB" , activationConfig = {
#ActivationConfigProperty(propertyName="destinationType", propertyValue = "javax.jms.Queue") ,
#ActivationConfigProperty(propertyName="destination", propertyValue = "BetaTESTQ"),
#ActivationConfigProperty(propertyName="maxSession", propertyValue = "5"),
#ActivationConfigProperty(propertyName="acknowledgeMode", propertyValue = "Auto-acknowledge")
})
public class BetaTestQueueListener implements MessageListener, java.io.Serializable {
private static final long serialVersionUID = 1L;
#Resource(name="MyJmsConnectionFactory")
private ConnectionFactory connectionFactory;
#Resource
private MessageDrivenContext mdbContext;
#Resource(name = "BETATESTQ")
private javax.jms.Queue betaTestQ;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void onMessage(Message rcvMessage) {
try {
jmsInstance.send("test message", connectionFactory, betaTestQ);
} catch (Throwable t) {
t.printStackTrace();
mdbContext.setRollbackOnly();
}
}
}

It seems likely that you're using the OpenWire JMS client. This complicates matters because, as I understand it, the OpenWire JMS client implements redelivery in the client itself and those redelivery semantics are configured on the client side not the broker side. The broker doesn't have a chance to apply any kind of redelivery policy because the OpenWire client handles everything and doesn't inform the broker of the delivery failures. This documentation may help you configure redelivery for your OpenWire client.
That said, I recommend following this tutorial. It demonstrates how to integrate TomEE and ActiveMQ Artemis without building/deploying the JCA RA. This will allow you to use the Core JMS client rather than the OpenWire JMS client.
If you want to go the RA route you can build the ActiveMQ Artemis JCA RA by running mvn verify from the examples/features/sub-modules/artemis-ra-rar/ directory. The RA will be in the target directory named artemis-rar-<version>.rar.

Related

JBoss 7 Embedded ActiveMQ - MDB message listener not working

I have to upgrade an existing app with JBOSS 4.2.2 and embedded activeMQ 5.3.0
To try with jboss 7.3 using an embedded active MQ, i did the following.
Following the instructions at https://developer.jboss.org/docs/DOC-18798#jive_content_id_ActiveMQ_as_an_internal_messaging_broker
I configured the activemq-rar-5.6.0.rar in JBoss 7.3 resource-adapter
Deployed the jboss quick start hello-world-mdb war file in jboss.
Tried to send and consume messages using a message driven bean(MDB)
The problem I am facing is, I don't see the Messages being consumed by the message listener.
Below are my configurations
1.resource-adapters subsystem changes in standalone-full.xml file
<subsystem xmlns="urn:jboss:domain:resource-adapters:5.0">
<resource-adapters>
<resource-adapter id="activemq-rar-5.6.0.rar">
<archive>
activemq-rar-5.6.0.rar
</archive>
<transaction-support>XATransaction</transaction-support>
<!--<config-property name="ServerUrl">tcp://localhost:61616</config-property> -->
<config-property name="ServerUrl">vm://localhost</config-property>
<connection-definitions>
<connection-definition class-name="org.apache.activemq.ra.ActiveMQManagedConnectionFactory" jndi-name="java:/activemq/ConnectionFactory" enabled="true" use-java-context="true" pool-name="ActiveMQConnectionFactoryPool" use-ccm="true">
<xa-pool>
<min-pool-size>1</min-pool-size>
<max-pool-size>20</max-pool-size>
</xa-pool>
</connection-definition>
</connection-definitions>
<admin-objects>
<admin-object class-name="org.apache.activemq.command.ActiveMQQueue" jndi-name="java:/queue/HELLOWORLDMDBQueue" use-java-context="true" pool-name="HELLOWORLDMDBQueue">
<config-property name="PhysicalName">HELLOWORLDMDBQueue</config-property>
</admin-object>
<admin-object class-name="org.apache.activemq.command.ActiveMQTopic" jndi-name="java:/topic/HELLOWORLDMDBTopic" use-java-context="true" pool-name="HELLOWORLDMDBTopic">
<config-property name="PhysicalName">HELLOWORLDMDBTopic</config-property>
</admin-object>
</admin-objects>
</resource-adapter>
</resource-adapters>
</subsystem>
standalone-full.xml domain:ejb3 subsystem mdb changes
<mdb>
<resource-adapter-ref resource-adapter-name="activemq-rar-5.6.0.rar"/>
<bean-instance-pool-ref pool-name="mdb-strict-max-pool"/>
</mdb>
The helloworld-mdb.war consists the following two classes.
Message sender
#WebServlet("/HelloWorldMDBServletClient")
public class HelloWorldMDBServletClient extends HttpServlet {
private static final long serialVersionUID = -1949285948189796311L;
#Resource(mappedName = "java:/activemq/ConnectionFactory")
private ConnectionFactory connectionFactory;
#Resource(mappedName = "java:/queue/HELLOWORLDMDBQueue")
private Destination queue;
private Connection connection;
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Session session = null;
MessageProducer sender = null;
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.write(
"<h1>Quickstart: Example demonstrates the use of <strong>JMS 2.0</strong> and <strong>EJB 3.2 Message-Driven Bean</strong> in JBoss EAP.</h1>");
try {
connection = connectionFactory.createConnection();
session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
sender = session.createProducer(queue);
sender.setDeliveryMode(DeliveryMode.PERSISTENT);
out.write("<p>Sending messages to <em>" + queue + "</em></p>");
out.write("<h2>The following messages will be sent to the destination:</h2>");
for (int i = 0; i < 3; i++) {
String text = "This is message " + (i + 1);
TextMessage response = session.createTextMessage(text);
sender.send(response);
out.write("Message (" + i + "): " + text + "</br>");
}
out.write(
"<p><i>Go to your JBoss EAP server console or server log to see the result of messages processing.</i></p>");
} catch (JMSException e) {
e.printStackTrace();
}finally {
try {
if (sender != null) {
sender.close();
}
if (session != null) {
session.close();
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
Message Consumer MDB
#MessageDriven(name = "HelloWorldQueueMDB", activationConfig = {
#ActivationConfigProperty(propertyName = "destination", propertyValue = "HELLOWORLDMDBQueue"),
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge")})
#ResourceAdapter(value="activemq-rar-5.6.0.rar")
public class HelloWorldQueueMDB implements MessageListener {
private static final Logger LOGGER = Logger.getLogger(HelloWorldQueueMDB.class.toString());
/**
* #see MessageListener#onMessage(Message)
*/
public void onMessage(Message rcvMessage) {
TextMessage msg = null;
try {
if (rcvMessage instanceof TextMessage) {
msg = (TextMessage) rcvMessage;
LOGGER.info("Received Message from queue: " + msg.getText());
} else {
LOGGER.warning("Message of wrong type: " + rcvMessage.getClass().getName());
}
} catch (JMSException e) {
throw new RuntimeException(e);
}
}
}
I send messages from the browser by going to http://localhost:8080/helloworld-mdb/HelloWorldMDBServletClient.
But I am not seeing the messages being consumed by the message listener, they are not showing in the jboss log.
The JBoss console log looks like this
In the HelloWorldMDBServletClient you're creating a transacted session to send the messages, i.e.:
session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
However, you're never calling session.commit() so it looks to me like the messages are never actually sent.
You should either invoke session.commit() to send the messages or create the session as non-transacted, e.g.:
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

RabbitMQ MQTT Adapter and Paho MQTT client

I’m using RabbitMQ MQTT Adapter and Paho MQTT client.
RabbitMQ version: {rabbitmq_mqtt,"RabbitMQ MQTT Adapter","3.2.1"}
Paho MQTT client version:
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>RELEASE</version>
</dependency>
Please see code inline.
I’m trying to understand if, the subscriber queue can be durable without expiration time. And If the messages can be durable also.
As I understood from RabbitMQ documentation, each time a subscriber subscribes to a topic
RabbitMQ will create a queue with this naming convention:
mqtt-subscription-<ClientName>qos<ClientQOS>
This queue has an expiration time, how can I create a queue without an expiration time? Can I change this queue expiration time to infinite?
As for now each time I run this command: “service rabbitmq-server restart”
The messages in the queue get deleted.
How can I prevent this? Is there a way I can keep the messages in the queue after restart?
In RabbitMQ management UI, I can see under “Publish message” -> “Delivery mode:” which can be “2-persistent”.
If I use management UI to publish messages with Delivery mode = 2-persistent. The messages will be in the queue after service restart.
How can I achieve the same using Paho MQTT Client?
// Heavily based on RabbitMQ MQTT adapter test case code!
// first, import the RabbitMQ Java client
// and the Paho MQTT client classes, plus any other
// requirements
import com.rabbitmq.client.*;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttTopic;
import org.eclipse.paho.client.mqttv3.internal.NetworkModule;
import org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule;
// import org.eclipse.paho.client.mqttv3.internal.trace.Trace;
import org.eclipse.paho.client.mqttv3.internal.wire.MqttOutputStream;
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish;
import javax.net.SocketFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.*;
/***
* MQTT v3.1 tests
* TODO: synchronise access to variables
*/
public class MqttTestClean implements MqttCallback {
// setup some variables which define where the MQTT broker is
private final String host = "0.0.0.0";
private final int port = 1883;
private final String brokerUrl = "tcp://" + host + ":" + port;
private String clientId;
private String clientId3;
private MqttClient client;
private MqttClient client3;
private MqttConnectOptions conOpt;
private ArrayList<MqttMessage> receivedMessages;
// specify a message payload - doesn't matter what this says, but since MQTT expects a byte array
// we convert it from string to byte array here
private final byte[] payload = "This payload was published on MQTT and read using AMQP.".getBytes();
// specify the topic to be used
private final String topic = "topic/proxy/1.0.0/Report/*";
private int testDelay = 2000;
private long lastReceipt;
private boolean expectConnectionFailure;
private ConnectionFactory connectionFactory;
private Connection conn;
private Channel ch;
// override 10s limit
private class MyConnOpts extends MqttConnectOptions {
private int keepAliveInterval = 60;
#Override
public void setKeepAliveInterval(int keepAliveInterval) {
this.keepAliveInterval = keepAliveInterval;
}
#Override
public int getKeepAliveInterval() {
return keepAliveInterval;
}
}
public void setUpMqtt() throws MqttException {
clientId = getClass().getSimpleName() + ((int) (10000*Math.random()));
client = new MqttClient(brokerUrl, clientId);
conOpt = new MyConnOpts();
setConOpts(conOpt);
receivedMessages = new ArrayList<MqttMessage>();
expectConnectionFailure = false;
}
public void tearDownMqtt() throws MqttException {
try {
client.disconnect();
} catch (Exception _) {}
}
private void setUpAmqp() throws Exception {
connectionFactory = new ConnectionFactory();
connectionFactory.setHost(host);
conn = connectionFactory.newConnection();
ch = conn.createChannel();
}
private void tearDownAmqp() throws IOException {
conn.close();
}
private void setConOpts(MqttConnectOptions conOpts) {
conOpts.setCleanSession(true);
conOpts.setKeepAliveInterval(60);
}
private void publish(MqttClient client, String topicName, int qos, byte[] payload) throws MqttException {
MqttTopic topic = client.getTopic(topicName);
MqttMessage message = new MqttMessage(payload);
message.setQos(qos);
MqttDeliveryToken token = topic.publish(message);
token.waitForCompletion();
}
public void connectionLost(Throwable cause) {
if (!expectConnectionFailure)
System.out.println("Connection unexpectedly lost");
}
public void messageArrived(String topic, MqttMessage message) throws Exception {
lastReceipt = System.currentTimeMillis();
System.out.println("-------------------------------------------------");
System.out.println("------------------" + lastReceipt + "-------------------------------");
System.out.println("------------------" + message.toString() + "-------------------------------");
receivedMessages.add(message);
}
public void deliveryComplete(IMqttDeliveryToken token) {
}
public void run() {
try {
setUpMqtt(); // initialise the MQTT connection
setUpAmqp(); // initialise the AMQP connection
connect();
//String queue = ch.queueDeclare().getQueue();
// String queue = ch.queueDeclare("mqtt-subscription-Snabel-3qos1", true, false, false, null).getQueue();
//ch.queueBind(queue, "amq.topic", "sci-topic.sc.proxy_1393.1.0.0.ApReport.*"/*topic*/);
client.connect(conOpt);
publish(client, "topic/proxy/1.0.0/Report/123456789",1, payload); // publish the MQTT message
client.disconnect();
Thread.sleep(testDelay);
tearDownAmqp(); // cleanup AMQP resources
tearDownMqtt(); // cleanup MQTT resources*/
disConnect();
} catch (Exception mqe) {
mqe.printStackTrace();
}
}
private void connect() throws Exception {
clientId3 = "Test-3";
client3 = new MqttClient(brokerUrl, clientId3);
MqttConnectOptions connOpts = new MqttConnectOptions();
connOpts.setCleanSession(false);
client3.connect(connOpts);
client3.setCallback(this);
client3.subscribe(topic);
if(!client3.isConnected()){
System.out.println("Not Connected");
return;
}
System.out.println("Connected");
}
private void disConnect() throws Exception {
try {
client3.disconnect();
} catch (Exception _) {}
}
public static void main(String[] args) {
MqttTest mqt = new MqttTest();
mqt.run();
}
}
This was a RabbitMQ bug:
http://rabbitmq.1065348.n5.nabble.com/MQTT-plugin-message-delivery-mode-td32925.html
It was fixed in:
http://www.rabbitmq.com/release-notes/README-3.2.4.txt

Apache camel write to rabbitmq

I am trying to read data from a file and write to a rabbitmq queue using apache camel but ending up with the error
Exception in thread "main" org.apache.camel.FailedToCreateRouteException: Failed to create route route1 at: >>> To[rabbitmq://localhost:15672?queue=hello] <<< in route: Route(route1)[[From[file://target/?fileName=doctor.txt&chars... because of Failed to resolve endpoint: rabbitmq://localhost:15672?queue=hello due to: No URI path as the exchangeName for the RabbitMQEndpoint, the URI is rabbitmq://localhost:15672?queue=hello
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:945)
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:187)
at org.apache.camel.impl.DefaultCamelContext.startRoute(DefaultCamelContext.java:794)
at org.apache.camel.impl.DefaultCamelContext.startRouteDefinitions(DefaultCamelContext.java:2184)
at org.apache.camel.impl.DefaultCamelContext.doStartCamel(DefaultCamelContext.java:1916)
at org.apache.camel.impl.DefaultCamelContext.doStart(DefaultCamelContext.java:1777)
at org.apache.camel.support.ServiceSupport.start(ServiceSupport.java:61)
at org.apache.camel.impl.DefaultCamelContext.start(DefaultCamelContext.java:1745)
at test.RMQCamelSender.main(RMQCamelSender.java:38)
Caused by: org.apache.camel.ResolveEndpointFailedException: Failed to resolve endpoint: rabbitmq://localhost:15672?queue=hello due to: No URI path as the exchangeName for the RabbitMQEndpoint, the URI is rabbitmq://localhost:15672?queue=hello
at org.apache.camel.impl.DefaultCamelContext.getEndpoint(DefaultCamelContext.java:545)
at org.apache.camel.util.CamelContextHelper.getMandatoryEndpoint(CamelContextHelper.java:71)
at org.apache.camel.model.RouteDefinition.resolveEndpoint(RouteDefinition.java:202)
at org.apache.camel.impl.DefaultRouteContext.resolveEndpoint(DefaultRouteContext.java:106)
at org.apache.camel.impl.DefaultRouteContext.resolveEndpoint(DefaultRouteContext.java:112)
at org.apache.camel.model.SendDefinition.resolveEndpoint(SendDefinition.java:61)
at org.apache.camel.model.SendDefinition.createProcessor(SendDefinition.java:55)
at org.apache.camel.model.ProcessorDefinition.makeProcessor(ProcessorDefinition.java:500)
at org.apache.camel.model.ProcessorDefinition.addRoutes(ProcessorDefinition.java:213)
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:942)
... 8 more
Caused by: java.lang.IllegalArgumentException: No URI path as the exchangeName for the RabbitMQEndpoint, the URI is rabbitmq://localhost:15672?queue=hello
at org.apache.camel.component.rabbitmq.RabbitMQComponent.createEndpoint(RabbitMQComponent.java:50)
at org.apache.camel.component.rabbitmq.RabbitMQComponent.createEndpoint(RabbitMQComponent.java:31)
at org.apache.camel.impl.DefaultComponent.createEndpoint(DefaultComponent.java:122)
at org.apache.camel.impl.DefaultCamelContext.getEndpoint(DefaultCamelContext.java:525)
... 17 more
Following are my implementations of the classes which are creating camel context and rabbitmq queue.
RMQCamleSender.java
package test;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import test.Producer;
public class RMQCamelSender {
public static void main(String[] argv) throws Exception {
Producer queueProd = new Producer();
queueProd.setupConnection();
System.out.println(queueProd.toString());
CamelContext context = new DefaultCamelContext();
context.addRoutes(new RouteBuilder() {
public void configure() throws Exception {
System.out.println("hello world");
from("file://target/?fileName=doctor.txt&charset=utf-8")
.process(new Processor() {
public void process(Exchange msg) throws Exception {
System.out.println(msg.getIn().getBody(
String.class));
}
}).to("rabbitmq://localhost:15672?queue=hello");
}
});
context.start();
Thread.sleep(4000);
context.stop();
}
}
Producer.java
package test;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
public class Producer {
public static final String QUEUE_NAME = "hello";
public static Connection connection;
public void setupConnection() throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
}
}
The program is working fine if I am not using Camel and trying to contact the queue using standard rabbitmq library.
The error message is pretty indicative: you are missing the exchange name in your endpoint URI, it cannot be blank, as the official Camel documentation says it has to follow the given format:
rabbitmq://hostname[:port]/exchangeName?[options]
I suggest you try using the amq.direct exchange (for more details, refer to the official RabbitMQ documentation), like so:
public void configure() throws Exception {
System.out.println("hello world");
from("file://target/?fileName=doctor.txt&charset=utf-8")
.process(new Processor() {
public void process(Exchange msg) throws Exception {
System.out.println(msg.getIn().getBody(String.class));
}
}).to("rabbitmq://localhost:5672/amq.direct?routingKey=hello");
}
Additionally, you are using 15672 port, which is by default the port for RabbitMQ's web management console, I guess you haven't changed the default settings so the port needs to be 5672. You also have to use the routingKey parameter instead of the queue one since in RabbitMQ semantics you publish to exchange:routingKey and only consume from queues.
You need to include the rabbitmq exchange name. It is missing from here;
.to("rabbitmq://localhost:15672?queue=hello");
It should be something like this:
.to("rabbitmq://localhost:15672/exchangeName?routingKey=hello");
Also, if you are sending to exchange, why do you want to specify a queue? You need only to specify the routing key and the exchange will send to that queue if there is a binding for that routing key.

Spring integration with Rabbit AMQP for "Client Sends Message -> Server Receives & returns msg on return queue --> Client get correlated msg"

I am able to write a java program using Rabbit Java API's doing the following:
Client sends message over Rabbit MQ exchange/queue with correlation Id (Say UUID - "348a07f5-8342-45ed-b40b-d44bfd9c4dde").
Server receives the message.
Server sends response message over Rabbit MQ exchange/queue with the same correlation Id - "348a07f5-8342-45ed-b40b-d44bfd9c4dde".
Client received the correlated message only in the same thread as 1.
Below is the Send.java and Recv.java using Rabbit APIs. I need help to convert this sample to use Spring AMQP integration especially receiving part on step 4. I am looking for something like receive method which can filter message using correlation Id.
Send.java:
import java.util.UUID;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
public class Send {
private final static String REQUEST_QUEUE = "REQUEST.QUEUE";
private final static String RESPONSE_QUEUE = "RESPONSE.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(REQUEST_QUEUE, false, false, false, null);
String message = "Hello World!";
String cslTransactionId = UUID.randomUUID().toString();
BasicProperties properties = (new BasicProperties.Builder())
.correlationId(cslTransactionId)
.replyTo(RESPONSE_QUEUE).build();
channel.basicPublish("", REQUEST_QUEUE, properties, message.getBytes());
System.out.println("Client Sent '" + message + "'");
Channel responseChannel = connection.createChannel();
responseChannel.queueDeclare(RESPONSE_QUEUE, false, false, false, null);
QueueingConsumer consumer = new QueueingConsumer(channel);
responseChannel.basicConsume(RESPONSE_QUEUE, false, consumer);
String correlationId = null;
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String responseMessage = new String(delivery.getBody());
correlationId = delivery.getProperties().getCorrelationId();
System.out.println("Correlation Id:" + correlationId);
if (correlationId.equals(cslTransactionId)) {
System.out.println("Client Received '" + responseMessage + "'");
responseChannel.basicAck(delivery.getEnvelope().getDeliveryTag(), true);
break;
}
}
channel.close();
connection.close();
}
}
Recv.java
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.AMQP.BasicProperties;
public class Recv {
private final static String REQUEST_QUEUE = "REQUEST.QUEUE";
private final static String RESPONSE_QUEUE = "RESPONSE.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(REQUEST_QUEUE, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(REQUEST_QUEUE, true, consumer);
String correlationId = null;
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
correlationId = delivery.getProperties().getCorrelationId();
System.out.println("Correlation Id:" + correlationId);
System.out.println("Server Received '" + message + "'");
if (correlationId != null)
break;
}
String responseMsg = "Response Message";
Channel responseChannel = connection.createChannel();
responseChannel.queueDeclare(RESPONSE_QUEUE, false, false, false, null);
BasicProperties properties = (new BasicProperties.Builder())
.correlationId(correlationId).build();
channel.basicPublish("", RESPONSE_QUEUE, properties,responseMsg.getBytes());
System.out.println("Server Sent '" + responseMsg + "'");
channel.close();
connection.close();
}
}
After running the Java configuration provided by gary, I am trying to move the configuration to XML format for server side adding listener. Below is the XML configuration:
server.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:int-amqp="http://www.springframework.org/schema/integration/amqp"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xmlns:int-stream="http://www.springframework.org/schema/integration/stream"
xsi:schemaLocation="http://www.springframework.org/schema/integration/amqp http://www.springframework.org/schema/integration/amqp/spring-integration-amqp.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/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean
id="serviceListenerContainer"
class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="queues" ref="requestQueue"/>
<property name="messageListener" ref="messageListenerAdaptor"/>
</bean>
<bean id="messageListenerAdaptor"
class="org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter">
<property name="delegate" ref="pojoListener" />
</bean>
<bean
id="pojoListener"
class="PojoListener"/>
<bean
id="replyListenerContainer"
class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="queues" ref="replyQueue"/>
<property name="messageListener" ref="fixedReplyQRabbitTemplate"/>
</bean>
<!-- Infrastructure -->
<rabbit:connection-factory
id="connectionFactory"
host="localhost"
username="guest"
password="guest"
cache-mode="CHANNEL"
channel-cache-size="5"/>
<rabbit:template
id="fixedReplyQRabbitTemplate"
connection-factory="connectionFactory"
exchange="fdms.exchange"
routing-key="response.key"
reply-queue="RESPONSE.QUEUE">
<rabbit:reply-listener/>
</rabbit:template>
<rabbit:admin connection-factory="connectionFactory"/>
<rabbit:queue id="requestQueue" name="REQUEST.QUEUE" />
<rabbit:queue id="replyQueue" name="RESPONSE.QUEUE" />
<rabbit:direct-exchange name="fdms.exchange" durable="true" auto-delete="false">
<rabbit:bindings>
<rabbit:binding queue="RESPONSE.QUEUE" key="response.key" />
</rabbit:bindings>
</rabbit:direct-exchange>
</beans>
SpringReceive.java
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringReceive {
/**
* #param args
*/
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("cslclient.xml");
SimpleMessageListenerContainer serviceListenerContainer = context.getBean("serviceListenerContainer", SimpleMessageListenerContainer.class);
serviceListenerContainer.start();
}
}
You can use RabbitTemplate.sendAndReceive() (or convertSendAndReceive()) with a reply listener container (Docs here); the template will take care of the correlation for you.
If you are using Spring Integration, use an outbound gateway with an appropriately configured rabbit template.

Message Driven Bean onMessage() is not invoked in Glassfish

I am new to JMS coding. I am trying to create message from stand-alone java client which creates and send the message to queue and message driven bean is used for further processing of the messages.
I referred the following guidelines :
http://techtipsjava.blogspot.de/2013/05/jms-on-glassfish-queue-and-topic-with.html
I am using Glassfish application server (3.1). And setup everything to create JMS message from stand-alone java client.
Here is my code:
Client
import java.util.Properties;
import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.QueueConnectionFactory;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
public class TruckMonitor {
public static void main(String args[]) {
try {
// Provide the details of remote JMS Client
Properties props = new Properties();
props.put(Context.PROVIDER_URL, "mq://localhost:7676");
// Create the initial context for remote JMS server
InitialContext cntxt = new InitialContext(props);
System.out.println("Context Created");
// JNDI Lookup for QueueConnectionFactory in remote JMS Provider
QueueConnectionFactory qFactory = (QueueConnectionFactory)cntxt.lookup("OmazanQueueConnectionFactory");
// Create a Connection from QueueConnectionFactory
Connection connection = qFactory.createConnection();
System.out.println("Connection established with JMS Provide ");
connection.start();
// Initialise the communication session
Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
// Create the message
TextMessage message = session.createTextMessage();
message.setJMSDeliveryMode(DeliveryMode.PERSISTENT);
message.setText(getMessage());
// JNDI Lookup for the Queue in remote JMS Provider
Queue queue = (Queue)cntxt.lookup("OmazanQueue");
// Create the MessageProducer for this communication
// Session on the Queue we have
MessageProducer mp = session.createProducer(queue);
// Send the message to Queue
mp.send(message);
System.out.println("Message Sent: " + getMessage());
// Make sure all the resources are released
mp.close();
session.close();
cntxt.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
private static String getMessage() {
String msg = null;
StringBuffer sbExceptionEvent = new StringBuffer("<exceptionEvent>");
sbExceptionEvent.append("</exceptionEvent>");
msg = sbExceptionEvent.toString();
return msg;
}
}
Message Driven Bean:
import java.util.Properties;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.QueueConnectionFactory;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
/** * Message-Driven Bean implementation class for: OmazanMDBean*/
#MessageDriven(
activationConfig = {
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "destination", propertyValue = "OmazanQueue"),
#ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge")
},
mappedName = "OmazanQueue")
public class OmazanMDBean implements MessageListener {
/**
* Default constructor.
* #throws NamingException
* #throws JMSException
*/
public OmazanMDBean() {
super();
}
/**
* #see MessageListener#onMessage(Message)
*/
public void onMessage(Message message) {
System.out.println("Inside omMessage");
try {
message.acknowledge();
} catch (Exception e) {
e.printStackTrace();
}
TextMessage txtMessage = (TextMessage) message;
try {
System.out.println(txtMessage.getText());
} catch (Exception e) {
e.printStackTrace();
}
}
}
The problem is: onMessage() is not getting invoked. Did I miss anything? Please help me.
I guess if you remove #ActivationConfigProperty(propertyName = "destination", propertyValue = "OmazanQueue") from you MessageDrivenBean it will work since you have already used mappedName = "OmazanQueue"