I have a stateful session bean where I send and receive JMS messages. All the connection setup is handled manually, so the bean is holding instances of javax.jms.connection and javax.jms.session. The bean also implements MessageListener to be able receive messages.
Now, when I send a message, I create a temporary queue with session.createTemporaryQueue(). I set the message.setJMSReplyTo() to this same temporary queue, and at last creates a consumer of this queue and sets the MessageListener to the same stateful session bean that all this is implemented in.
I am abel to receive the message to the onMessage() method. However, I want to close the session and connection as soon as the message has been received, and this is apparently not allowed in the onMessage() method.
So the question is:
How can I close the session and connection once the message has been received? I must handle the connection setup manually and can not use a MDB.
Note that:
This is executed in the Java EE environment (GlassFish 4.0)
EDIT:
import javax.ejb.LocalBean;
import javax.ejb.Stateful;
import javax.inject.Inject;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import com.sun.messaging.ConnectionConfiguration;
import com.sun.messaging.QueueConnectionFactory;
#LocalBean
#Stateful
public class OpenMqClient implements MessageListener{
private Connection connection;
private Session session;
private MessageConsumer responseConsumer;
public OpenMqClient(){}
public void sendMessage(String messageContent, String jmsBrokerUri, String queueName) {
try{
String host = System.getProperty("foo", jmsBrokerUri);
QueueConnectionFactory cf = new QueueConnectionFactory();
cf.setProperty(ConnectionConfiguration.imqAddressList, host);
connection = null;
session = null;
//Setup connection
connection = cf.createConnection();
connection.start();
session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
//Setup queue and producer
Queue queue = session.createQueue(queueName);
MessageProducer producer = session.createProducer(queue);
//Reply destination
Queue responseQueue = session.createTemporaryQueue();
responseConsumer = session.createConsumer(responseQueue);
responseConsumer.setMessageListener(this);
//Create message
TextMessage textMessage = session.createTextMessage();
textMessage.setJMSReplyTo(responseQueue);
textMessage.setJMSCorrelationID("test0101");
textMessage.setText(messageContent);
producer.send(textMessage);
System.out.println("Message sent");
} catch (JMSException e) {
e.printStackTrace();
System.out.println("JMSException in Sender");
}
}
#Override
public void onMessage(Message arg0) {
//On this event I want to close the session and connection, but it's not permitted
}
}
Personally, this is how I would do it (note I haven't tested or added much error handling to this code).
Make the connection static - you can (probably should) reuse the same connection for all your beans unless you have a specific reason not to
Close the session in a new thread
public class OpenMqClient implements MessageListener {
private static Connection connection;
private static final String mutex = "mutex";
private Session session;
private MessageConsumer responseConsumer;
public OpenMqClient() {
if(connection == null) {
synchronized(mutex) {
if(connection == null) {
String host = System.getProperty("foo", jmsBrokerUri);
QueueConnectionFactory cf = new QueueConnectionFactory();
cf.setProperty(ConnectionConfiguration.imqAddressList, host);
// Setup connection
connection = cf.createConnection();
connection.start();
}
}
}
}
public void sendMessage(String messageContent, String jmsBrokerUri, String queueName) {
try {
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Setup queue and producer
Queue queue = session.createQueue(queueName);
MessageProducer producer = session.createProducer(queue);
// Reply destination
Queue responseQueue = session.createTemporaryQueue();
responseConsumer = session.createConsumer(responseQueue);
responseConsumer.setMessageListener(this);
// Create message
TextMessage textMessage = session.createTextMessage();
textMessage.setJMSReplyTo(responseQueue);
textMessage.setJMSCorrelationID("test0101");
textMessage.setText(messageContent);
producer.send(textMessage);
System.out.println("Message sent");
} catch (JMSException e) {
e.printStackTrace();
System.out.println("JMSException in Sender");
}
}
#Override
public void onMessage(Message arg0) {
// do stuff
new Thread(
new Runnable() {
#Override
public void run() {
if(session != null)
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
).start();
}
}
Related
I am a newbie to this so forgive me if my mistake seems stupid.I am using active mq with glassfish and eclipse. I have created JMS resources in glass fish such as Destination Resources and Connection Factories. Active Mq is running and I have deployed message driven bean on glassfish and no exceptions occur but i can't find my Queue on active mq and neither is the message printed on console.
I am basically using example given on here : https://www.javatpoint.com/message-driven-bean
public class MessageSender {
public static void sendMessage() throws JMSException, NamingException {
InitialContext ctx = new InitialContext();
QueueConnectionFactory f = (QueueConnectionFactory) ctx.lookup("jms/myQueueConnectionFactory");
QueueConnection connection = f.createQueueConnection();
connection.start();
QueueSession ses = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Queue t = (Queue) ctx.lookup("jms/myQueue");
QueueSender sender = ses.createSender(t);
TextMessage message=ses.createTextMessage("Hello !!! Welcome to the world of ActiveMQ.");
sender.send(message);
connection.close();
}
}
#MessageDriven(mappedName = "jms/myQueue")
public class MessageReceiver implements MessageListener {
/**
* Default constructor.
*/
public MessageReceiver() {
// TODO Auto-generated constructor stub
}
/**
* #see MessageListener#onMessage(Message)
*/
public void onMessage(Message message) {
// TODO Auto-generated method stub
TextMessage m=(TextMessage) message;
try{
System.out.println("message received: "+m.getText());
}catch(Exception e){System.out.println(e);}
}
}
I am trying to interact with RabbitMQ server using RabbitMQ-java client API.
I read from the java client api guide:
As a rule of thumb, sharing Channel instances between threads is something to be avoided. Applications should prefer using a Channel per thread instead of sharing the same Channel across multiple threads.
I am trying to use a ThreadPoolExecutor with corePoolSize 1 and adding Runnable tasks to save messages in RabbitMQ queues. Here is the code that I am using:
package common;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Properties;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonObject;
import com.rabbitmq.client.BlockedListener;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
import com.rabbitmq.client.ShutdownListener;
import com.rabbitmq.client.ShutdownSignalException;
public class RabbitMQUtil {
private static Logger log= LoggerFactory.getLogger("logger");
private static RabbitMQUtil gmInstance;
private ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(10000));
private final String PROPERTIES_FILE_NAME = "config/rabbitmq.properties";
private final Properties properties = new Properties();
private String host = null;
private int port = 0;
private String username = null;
private String password = null;
private String useSSL = "false";
private ConnectionFactory factory;
private Connection connection;
private Channel channel;
private RabbitMQUtil() throws IOException, TimeoutException, Exception {
try {
InputStream stream = RabbitMQUtil.class.getClassLoader().getResourceAsStream(PROPERTIES_FILE_NAME);
if(stream != null) {
properties.load(stream);
}
} catch (Exception ex) {
log.error("Exception while loading the rabbitmq properties file:", ex);
}
host = properties.getProperty("rabbitmq.host", "localhost");
port = Integer.parseInt(properties.getProperty("rabbitmq.port", "5672"));
username = properties.getProperty("rabbitmq.username", "guest");
password = properties.getProperty("rabbitmq.password", "guest");
useSSL = properties.getProperty("rabbitmq.usessl", "false");
factory = new ConnectionFactory();
factory.setHost(host);
factory.setPort(port);
factory.setUsername(username);
factory.setPassword(password);
if("true".equalsIgnoreCase(useSSL)) {
try {
factory.useSslProtocol();
} catch (KeyManagementException | NoSuchAlgorithmException e) {
log.error("Exception while applying the tls for rabbitmq:", e);
}
}
connection = factory.newConnection();
connection.addBlockedListener(new RabbitMQBlockedListener());
connection.addShutdownListener(new RabbitMQShutDownListener());
channel = connection.createChannel();
}
public static RabbitMQUtil getInstance() {
if(gmInstance == null) {
synchronized (RabbitMQUtil.class) {
if(gmInstance == null) {
try {
gmInstance = new RabbitMQUtil();
} catch (IOException | TimeoutException e) {
log.error("Exception in getInstance:", e);
} catch (Exception e) {
log.error("Exception in getInstance:", e);
}
}
}
}
return gmInstance;
}
public static void saveErrorMessagesInLogs(JsonObject obj, String queueName) {
log.info("data to be saved for :"+queueName+" is:"+obj.toString());
}
public void saveMsgInQueue(JsonObject gson, String queueName) {
this.executor.execute(new RabbitMQData(gson, queueName));
}
private class RabbitMQBlockedListener implements BlockedListener {
#Override
public void handleBlocked(String arg0) throws IOException {
log.warn("blocked listener called:", arg0);
}
#Override
public void handleUnblocked() throws IOException {
log.warn("unblocked listener called:");
}
}
private class RabbitMQShutDownListener implements ShutdownListener {
#Override
public void shutdownCompleted(ShutdownSignalException cause) {
log.error("Shutdown event listener called:", cause);
log.error("shutdown event listener:"+cause.isHardError());
}
}
private class RabbitMQData implements Runnable{
JsonObject obj;
String queueName;
public RabbitMQData(JsonObject obj, String queueName) {
Thread.currentThread().setName("RabbitMQ Thread:"+obj.get("userid")+" -->"+queueName);
this.obj = obj;
this.queueName = queueName;
}
#Override
public void run() {
try {
channel.queueDeclare(this.queueName, true, false, false, null);
channel.basicPublish("", this.queueName, MessageProperties.PERSISTENT_BASIC, this.obj.toString().getBytes());
} catch (Exception e) {
log.info("Error while running the scheduled rabbitmq task:", e);
log.info("data to be saved for :"+this.queueName+" is:"+this.obj.toString());
}
}
}
public static void saveRabbitMQData(JsonObject obj, String queueName) {
RabbitMQUtil util = RabbitMQUtil.getInstance();
if(util != null)
util.saveMsgInQueue(obj, queueName);
else
RabbitMQUtil.saveErrorMessagesInLogs(obj, queueName);
}
}
I would like to know the following things:
Is is fine to use a single channel when a threadpool of only 1 thread is used ?
How should connection and channel objects be handled when blocked/unblocked and shutdown events are triggered ? Although the API automatically establishes connection when RabbitMQ server is up again.
Any other feedback will be appreciated.
Thank you
1.- Is is fine to use a single channel when a threadpool of only 1 thread is used ?
yes, it is fine. that is the way you should do it. only one thread must use a Channel instance. Otherwise, confirmations might be lost (see here: https://www.rabbitmq.com/releases/rabbitmq-java-client/v3.1.1/rabbitmq-java-client-javadoc-3.1.1/com/rabbitmq/client/Channel.html)
2.- How should connection and channel objects be handled when blocked/unblocked and shutdown events are triggered ? Although the API automatically establishes connection when RabbitMQ server is up again.
when the application is shutting down, you should close the channel, then close the connection to RabbitMQ.
channel.close();
conn.close();
about blocking/unblocking, please read here (https://www.rabbitmq.com/api-guide.html) :
Callbacks to Consumers are dispatched in a thread pool separate from the thread that instantiated its Channel. This means that Consumers can safely call blocking methods on the Connection or Channel, such as Channel#queueDeclare or Channel#basicCancel.
Each Channel has its own dispatch thread. For the most common use case of one Consumer per Channel, this means Consumers do not hold up other Consumers. If you have multiple Consumers per Channel be aware that a long-running Consumer may hold up dispatch of callbacks to other Consumers on that Channel.
I would like to know how can i get list of destinations (queues or topics) on a remote broker in ActiveMQ in Java without using JMX. I tried to get topics by DestinationSource class, but it doesn't work. In this thread I found that advisory support have to be enabled on the client and the broker. I found that advisory support is enabled by default.
Here is my broker
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.apache.activemq.api.core.TransportConfiguration;
import org.apache.activemq.core.config.Configuration;
import org.apache.activemq.core.config.impl.ConfigurationImpl;
import org.apache.activemq.core.remoting.impl.netty.NettyAcceptorFactory;
import org.apache.activemq.core.server.ActiveMQServer;
import org.apache.activemq.core.server.ActiveMQServers;
public class Server {
public static void main(final String arg[]) throws Exception
{
try
{
// Step 1. Create the Configuration, and set the properties accordingly
Configuration configuration = new ConfigurationImpl();
//we only need this for the server lock file
configuration.setJournalDirectory("target/data/journal");
configuration.setPersistenceEnabled(false); // http://activemq.apache.org/what-is-the-difference-between-persistent-and-non-persistent-delivery.html
configuration.setSecurityEnabled(false); // http://activemq.apache.org/security.html
/**
* this map with configuration values is not necessary (it configures the default values).
* If you want to modify it to run the example in two different hosts, remember to also
* modify the client's Connector at {#link EmbeddedRemoteExample}.
*/
Map<String, Object> map = new HashMap<String, Object>();
map.put("host", "localhost");
map.put("port", 61616);
// https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/5/html/HornetQ_User_Guide/ch14s04.html
TransportConfiguration transpConf = new TransportConfiguration(NettyAcceptorFactory.class.getName(),map);
HashSet<TransportConfiguration> setTransp = new HashSet<TransportConfiguration>();
setTransp.add(transpConf);
configuration.setAcceptorConfigurations(setTransp); // https://github.com/apache/activemq-6/blob/master/activemq-server/src/main/java/org/apache/activemq/spi/core/remoting/Acceptor.java
// Step 2. Create and start the server
ActiveMQServer server = ActiveMQServers.newActiveMQServer(configuration);
server.start();
}
catch (Exception e)
{
e.printStackTrace();
throw e;
}
}
}
And client, where I want to get created topic
import java.util.Set;
import javax.jms.Destination;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.advisory.DestinationSource;
import org.apache.activemq.command.ActiveMQTopic;
public class MessageConsumer implements ExceptionListener{
ActiveMQConnection connection = null;
javax.jms.MessageConsumer consumer = null;
Session session = null;
public MessageConsumer(){
try {
// create a new intial context, which loads from jndi.properties file
javax.naming.Context context = new javax.naming.InitialContext();
// Create a ConnectionFactory
ActiveMQConnectionFactory connectionFactory = (org.apache.activemq.ActiveMQConnectionFactory)context.lookup("ConnectionFactory");
// Create a Connection
connection= (ActiveMQConnection) connectionFactory.createConnection();
DestinationSource destSource = connection.getDestinationSource();
// Create a Session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Create the destination (Topic or Queue)
Destination destination = new ActiveMQTopic("MyTopic");
// Create a MessageConsumer from the Session to the Topic or Queue
consumer = session.createConsumer(destination);
connection.start();
Thread.sleep(3000);
Set<ActiveMQTopic> b = destSource.getTopics();
MessageListener listener = new MessageListener() {
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
System.out.println("Received message"
+ textMessage.getText() + "'");
}
} catch (JMSException e) {
System.out.println("Caught:" + e);
e.printStackTrace();
}
}
};
consumer.setMessageListener(listener);
} catch (Exception e) {
System.out.println("Caught: " + e);
e.printStackTrace();
}
}
#Override
public void onException(JMSException exception) {
System.out.println("JMS Exception occured. Shutting down client.");
}
public void close(){
// Clean up
try {
consumer.close();
session.close();
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
destSource.getTopics() returns set of 0 topics.
I am new in JMS and want to create a basic MessageProducer who sends a message and MessageConsumer who receives the message asynchronously. When I run this code I get error message :
MessageProducer.java
package activemq.test;
import java.util.Date;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class MessageProducer{
javax.jms.MessageProducer producer = null;
Connection connection = null;
Session session = null;
public MessageProducer(){
try {
// Create a ConnectionFactory
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
// Create a Connection
connection = connectionFactory.createConnection();
connection.start();
// Create a Session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Create the destination (Topic or Queue)
Destination destination = session.createQueue("TEST.FOO");
// Create a MessageProducer from the Session to the Topic or Queue
producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
// Create a messages
String text = "Hello world! From: MessageProducer";
TextMessage message = session.createTextMessage(text);
// Tell the producer to send the message
System.out.println("Producer is going to send a message");
producer.send(message);
} catch (Exception e) {
System.out.println("Caught: " + e);
e.printStackTrace();
}
}
public void sendMessage(){
try
{
// Create a messages
String text = "Hello world! From: " + new Date();
TextMessage message = session.createTextMessage(text);
// Tell the producer to send the message
System.out.println("Sent message: "+ message.hashCode());
producer.send(message);
}
catch (Exception e)
{
e.printStackTrace();
}
}
public void close(){
// Clean up
try {
session.close();
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
MessageConsumer.java
package activemq.test;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class MessageConsumer implements ExceptionListener{
Connection connection = null;
javax.jms.MessageConsumer consumer = null;
Session session = null;
public MessageConsumer(){
try {
// Create a ConnectionFactory
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
// Create a Connection
connection = connectionFactory.createConnection();
connection.start();
connection.setExceptionListener(this);
// Create a Session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Create the destination (Topic or Queue)
Destination destination = session.createQueue("TEST.FOO");
// Create a MessageConsumer from the Session to the Topic or Queue
consumer = session.createConsumer(destination);
MessageListener listener = new MessageListener() {
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
System.out.println("Received message"
+ textMessage.getText() + "'");
}
} catch (JMSException e) {
System.out.println("Caught:" + e);
e.printStackTrace();
}
}
};
consumer.setMessageListener(listener);
} catch (Exception e) {
System.out.println("Caught: " + e);
e.printStackTrace();
}
}
#Override
public void onException(JMSException exception) {
System.out.println("JMS Exception occured. Shutting down client.");
}
public void close(){
// Clean up
try {
consumer.close();
session.close();
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
AppMain.java
public class AppMain {
public static void main(final String arg[]) throws Exception
{
MessageProducer msProducer = new MessageProducer();
msProducer.sendMessage();
msProducer.close();
MessageConsumer msConsumer = new MessageConsumer();
msConsumer.close();
}
}
When MessageConsumer is created, I get error message:
Caught: javax.jms.JMSException: AMQ119017: Queue jms.queue.TEST.FOO does not exist
javax.jms.JMSException: AMQ119017: Queue jms.queue.TEST.FOO does not exist
at org.apache.activemq.util.JMSExceptionSupport.create(JMSExceptionSupport.java:54)
at org.apache.activemq.ActiveMQConnection.syncSendPacket(ActiveMQConnection.java:1405)
at org.apache.activemq.ActiveMQSession.syncSendPacket(ActiveMQSession.java:1925)
at org.apache.activemq.ActiveMQMessageConsumer.<init>(ActiveMQMessageConsumer.java:275)
at org.apache.activemq.ActiveMQSession.createConsumer(ActiveMQSession.java:1157)
at org.apache.activemq.ActiveMQSession.createConsumer(ActiveMQSession.java:1101)
at org.apache.activemq.ActiveMQSession.createConsumer(ActiveMQSession.java:1014)
at org.apache.activemq.ActiveMQSession.createConsumer(ActiveMQSession.java:987)
at activemq.test.MessageConsumer.<init>(MessageConsumer.java:36)
at activemq.test.AppMain.main(AppMain.java:17)
Caused by: ActiveMQNonExistentQueueException[errorType=QUEUE_DOES_NOT_EXIST message=AMQ119017: Queue jms.queue.TEST.FOO does not exist]
at org.apache.activemq.core.server.impl.ServerSessionImpl.createConsumer(ServerSessionImpl.java:448)
at org.apache.activemq.core.protocol.openwire.amq.AMQServerSession.createConsumer(AMQServerSession.java:326)
at org.apache.activemq.core.protocol.openwire.amq.AMQConsumer.init(AMQConsumer.java:138)
at org.apache.activemq.core.protocol.openwire.amq.AMQSession.createConsumer(AMQSession.java:144)
at org.apache.activemq.core.protocol.openwire.OpenWireProtocolManager.addConsumer(OpenWireProtocolManager.java:544)
at org.apache.activemq.core.protocol.openwire.OpenWireConnection.processAddConsumer(OpenWireConnection.java:1118)
at org.apache.activemq.command.ConsumerInfo.visit(ConsumerInfo.java:347)
at org.apache.activemq.core.protocol.openwire.OpenWireConnection.bufferReceived(OpenWireConnection.java:272)
at org.apache.activemq.core.remoting.server.impl.RemotingServiceImpl$DelegatingBufferHandler.bufferReceived(RemotingServiceImpl.java:678)
at org.apache.activemq.core.remoting.impl.netty.ActiveMQChannelHandler.channelRead(ActiveMQChannelHandler.java:77)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:332)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:318)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:787)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:125)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:507)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:464)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:378)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:350)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116)
at java.lang.Thread.run(Thread.java:745)
Why I get this error when MessageConsumer is created, but don't get this error when MessageProducer is created.
I use ActiveMQServer as a broker:
Server.java
package activemq.test;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.apache.activemq.api.core.TransportConfiguration;
import org.apache.activemq.core.config.Configuration;
import org.apache.activemq.core.config.impl.ConfigurationImpl;
import org.apache.activemq.core.remoting.impl.netty.NettyAcceptorFactory;
import org.apache.activemq.core.server.ActiveMQServer;
import org.apache.activemq.core.server.ActiveMQServers;
public class Server {
public static void main(final String arg[]) throws Exception
{
try
{
// Step 1. Create the Configuration, and set the properties accordingly
Configuration configuration = new ConfigurationImpl();
//we only need this for the server lock file
configuration.setJournalDirectory("target/data/journal");
configuration.setPersistenceEnabled(false); // http://activemq.apache.org/what-is-the-difference-between-persistent-and-non-persistent-delivery.html
configuration.setSecurityEnabled(false); // http://activemq.apache.org/security.html
/**
* this map with configuration values is not necessary (it configures the default values).
* If you want to modify it to run the example in two different hosts, remember to also
* modify the client's Connector at {#link EmbeddedRemoteExample}.
*/
Map<String, Object> map = new HashMap<String, Object>();
map.put("host", "localhost");
map.put("port", 61616);
// https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/5/html/HornetQ_User_Guide/ch14s04.html
TransportConfiguration transpConf = new TransportConfiguration(NettyAcceptorFactory.class.getName(),map);
HashSet<TransportConfiguration> setTransp = new HashSet<TransportConfiguration>();
setTransp.add(transpConf);
configuration.setAcceptorConfigurations(setTransp); // https://github.com/apache/activemq-6/blob/master/activemq-server/src/main/java/org/apache/activemq/spi/core/remoting/Acceptor.java
// Step 2. Create and start the server
ActiveMQServer server = ActiveMQServers.newActiveMQServer(configuration);
server.start();
}
catch (Exception e)
{
e.printStackTrace();
throw e;
}
}
}
I think, in the producer, you are starting the connection before setting the destination.
Try it starting afterwards....
// Create a ConnectionFactory
ActiveMQConnectionFactory connectionFactory = new
ActiveMQConnectionFactory("tcp://localhost:61616");
// Create a Connection
connection = connectionFactory.createConnection();
// Create a Session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Create the destination (Topic or Queue)
Destination destination = session.createQueue("TEST.FOO");
// Create a MessageProducer from the Session to the Topic or Queue
producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
connection.start();
// Create a messages
String text = "Hello world! From: MessageProducer";
TextMessage message = session.createTextMessage(text);
// Tell the producer to send the message
System.out.println("Producer is going to send a message");
producer.send(message);
On the other hand, for the consumer, I suggest to implement MessageConsumer (instead of the Exception).
Once implemented, in the constructor you can initiate the consumer
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(url);
connection = factory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Create the destination (Topic or Queue)
Destination destination = session.createQueue("TEST.FOO");
// Create a MessageConsumer from the Session to the Topic or Queue
consumer = session.createConsumer(destination).setMessageListener(this);
connection.start();
....
and then implement the onMessage method
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
System.out.println("Received message"
+ textMessage.getText() + "'");
}
} catch (JMSException e) {
System.out.println("Caught:" + e);
e.printStackTrace();
}
}
I am trying to use multiple threads to consume jms queue.
I know that there should be a separate JMS Session for each Thread and that what I did in my code as shown below. But I am getting a weird exception
here is the exception stack trace:
javax.jms.IllegalStateException: Forbidden call on a closed connection.
at org.objectweb.joram.client.jms.Connection.checkClosed(Connection.java:404)
at org.objectweb.joram.client.jms.Connection.createSession(Connection.java:530)
at MessageWorker.run(ReceiveJmsDemoMultiThreaded.java:96)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
I need your help because this is a blocking issue for me
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.QueueConnectionFactory;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class ReceiveJmsDemoMultiThreaded {
public static void main(String[] args) {
Context context = null;
ConnectionFactory factory = null;
Connection connection = null;
Destination destination = null;
try {
context = getInitialContext();
factory = (QueueConnectionFactory) context.lookup("JQCF");
destination = (Destination) context.lookup("sampleQueue");
connection = factory.createConnection();
final ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(new MessageWorker(connection, destination) );
executor.submit(new MessageWorker(connection, destination) );
executor.submit(new MessageWorker(connection, destination) );
executor.submit(new MessageWorker(connection, destination) );
connection.start();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (context != null) {
try {
context.close();
} catch (NamingException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
private static InitialContext getInitialContext() throws NamingException {
Properties prop = new Properties();
prop.put("java.naming.provider.url", "rmi://localhost:1099");
prop.put("java.naming.factory.initial",
"org.objectweb.carol.jndi.spi.MultiOrbInitialContextFactory");
return new InitialContext(prop);
}
}
class MessageWorker extends Thread {
Connection connection = null;
Destination dest = null;
Session session = null;
Destination destination = null;
public MessageWorker(Connection connection, Destination dest) {
this.connection = connection;
this.destination = dest;
}
#Override
public void run() {
try {
MessageConsumer receiver = null;
System.out.println("Starting Thread "+currentThread().getName());
while (true) {
try {
System.out.println("Waiting for next msg "+currentThread().getName());
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
receiver = session.createConsumer(destination);
Message msg = receiver.receive();
if (msg instanceof Message && msg != null) {
System.out.println("STARTING consuming "+msg.toString()+" by thread "+currentThread().getName() );
Thread.sleep(2000);//some work here
System.out.println("ENDING consuming "+msg.toString()+" by thread "+currentThread().getName() );
}
} catch (JMSException e) {
e.printStackTrace();
System.exit(1);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}
Many thanks
You are seeing this problem because, in the main thread, after submitting the jobs to the Executor Service, the connection is closed using:
connection.close();
So, when the threads try to create a Session using this shared connection (which just got closed), they are getting this exception. Nothing unexpected here.
Just for testing, you can make your main thread sleep for a long time until all your threads are done receiving messages. This way, you can confirm that you dont receive this exception.
A real solution may be to shutdown the Executor service and make the main thread awaitTermination() to wait for completion of the submitted jobs.