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.
Related
//I have a standalone JAVA code which can connect to IBMMQ via JNDI lookup, and can send or receive the message. The same thing I want to achieve via spring boot but I am unable to configure the jms template? I am new to spring boot if anyone can help I will appreciate.
//Here is the working JAVA code.
import java.security.Security;
import java.util.Hashtable; //Imports for JNDI
import javax.jms.Connection;
import javax.jms.ConnectionFactory; //Imports for JMS
import javax.jms.Destination;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.core.JmsTemplate;
import com.ibm.mq.MQException; //Import for MQ Linked Exception
import net.bytebuddy.asm.Advice.This;
public class Jms11ListenerSample extends Thread implements MessageListener, ExceptionListener{
public void run() {
//declaration of objects
ConnectionFactory jmsConnFactory = null;
Destination jmsDestination = null;
MessageConsumer jmsConsumer = null;
jmsConnFactory = this.lookupCF();
jmsDestination = this.lookupDestination();
while (running) {
try {
if (!connected) {
System.out.println("connecting to MQ...");
//connect to MQ and start the connection:
jmsConnection = jmsConnFactory.createConnection();
jmsConnection.setExceptionListener(this);
jmsConnection.start();
jmsSession = jmsConnection.createSession(true, 0); //Session.AUTO_ACKNOWLEDGE/CLIENT_ACKNOWLEDGE/DUPS_OK_ACKNOWLEDGE
jmsConsumer = jmsSession.createConsumer(jmsDestination);
jmsConsumer.setMessageListener(this);
connected = true;
}
System.out.println("sleeping for " + DEFAULT_RECONNECT_PAUSE/1000 + " seconds ...");
//Wait for onMSG / onExc
try {Thread.sleep(DEFAULT_RECONNECT_PAUSE);} catch (Exception exc) {/*ignored*/}
} catch (JMSException e) {
onException(e);
} //end try/catch
} //end while
if (jmsConnection != null) {
try {
jmsConnection.close(); //this closes all sub-objects, too!
} catch (JMSException e) {
System.out.println("JMS Connection could not be closed: " + e);
e.printStackTrace();
}
}
} //END RUN()
//This method is called from outside
public void onMessage(Message receiveMSG) {
try {
if (receiveMSG instanceof TextMessage) {
System.out.println("Received a message: " + ((TextMessage)receiveMSG).getText());
} else {
System.out.println("Received a non-text message");
}
jmsSession.commit();
DONE_MSGS++;
if (DONE_MSGS >= MAX_MSGS) {running = false;}
} catch (JMSException e) {
onException(e);
}
}
//This method is called when an exception occurs
public void onException(JMSException e) {
if (jmsSession != null) {try {jmsSession.rollback();} catch (JMSException exc) {/*ignored*/}}
//Print and analyse the error:
System.out.println("JMS Exception: " + e);
e.printStackTrace();
//The linked exception is vital to errorhandling and cause determination. PRINT IT OUT!!!!
MQException le = (MQException)e.getLinkedException();
if (le != null) {
int reasoncode = le.reasonCode;
System.out.println("LINKED Exception: " + le + " - REASON-CODE: " + reasoncode);
if (reasoncode == MQException.MQRC_CONNECTION_BROKEN || reasoncode == MQException.MQRC_Q_MGR_NOT_AVAILABLE) {
System.out.println("Will have to reconnect!");
connected=false;
if (jmsConnection != null) {try {jmsConnection.close();} catch (JMSException exc) {/*ignored*/}}
try {Thread.sleep(DEFAULT_RECONNECT_PAUSE);} catch (Exception exc) {/*ignored*/}
} else {
running = false;
}
//The cause is usually empty. In some cases (i.e. SSL) it might be set. PRINT IT OUT!!
if (le.getCause() != null) {
System.out.println("CAUSE of Linked Exception: " + le.getCause());
}
}
}
//lookupCF() reads the ConnectionFactory from JNDI (LDAP). All threads use the same CF, therefore we do this only once.
private ConnectionFactory lookupCF() {
//Synchronize so that not multiple threads try to overwrite 'theCF' !
synchronized (this) {
if (theCF != null) {
return theCF;
}
InitialContext ctx = null;
Hashtable jndiContext = new Hashtable();
jndiContext.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
jndiContext.put(Context.SECURITY_AUTHENTICATION, "none");
jndiContext.put(Context.PROVIDER_URL, jndiLDAPserver);
try {
ctx = new InitialContext(jndiContext);
theCF = (ConnectionFactory) ctx.lookup(jndiCFname);
if (theCF == null) {
System.out.println("FATAL ERROR: CF " + jndiCFname + " not found!");
System.exit(1); //Quit because fatal error!
}
ctx.close();
} catch (NamingException e) {
e.printStackTrace(); System.exit(1); //Quit because fatal error!
} catch(ClassCastException e) {
e.printStackTrace(); System.exit(1); //Quit because fatal error!
}
return theCF;
} //END synchonized
}//END lookupCF()
//lookupDestination() reads the Destination from JNDI (FILE). It is needed only once.
private Destination lookupDestination() {
Destination destination = null;
InitialContext ctx = null;
Hashtable jndiContext = new Hashtable();
jndiContext.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
jndiContext.put(Context.PROVIDER_URL, jndiDestinationFile);
try {
ctx = new InitialContext(jndiContext);
destination = (Destination) ctx.lookup(jndiDestinationName);
if (destination == null) {
System.out.println("FATAL ERROR: Destination " + jndiDestinationName + " not found!");
System.exit(1); //Quit because fatal error
}
ctx.close();
} catch (NamingException e) {
e.printStackTrace(); System.exit(1); //Quit because fatal error
} catch(ClassCastException e) {
e.printStackTrace(); System.exit(1); //Quit because fatal error
}
return destination;
}//END OF lookupDestination()
private static ConnectionFactory theCF = null; //Used for all threads
private Session jmsSession = null;
private Connection jmsConnection = null;
private boolean running = true;
private boolean connected = false;
private static String jndiLDAPserver = "";
private static String jndiCFname = "";
private static String jndiDestinationFile = "";
private static String jndiDestinationName = "";
private final static int DEFAULT_RECEIVE_TIMEOUT = 500; //Milliseconds to wait for message if queue is empty!
private final static int DEFAULT_RECONNECT_PAUSE = 5000; //Milliseconds to wait before next reconnect-try
private final static int DEFAULT_NUMBER_OF_THREADS = 1; //Milliseconds to wait before next reconnect-try
`enter code here`private final static int MAX_MSGS = 5;
private static int DONE_MSGS = 0;
public static void main(String[] args) {
System.out.println("starting application");
//reading config
if (args.length < 4) {
System.out.println("Arguments missing.");
System.out.println("LDAP-server&context CF-name PathTo.bindings DestinationName");
System.out.println("To force re-enabling SSLv3 enable support the property JMS11Sample.reenableSSLv3 must be <> NULL (check setup files for details)");
System.exit(1);
}
jndiLDAPserver = args[0];
jndiCFname = args[1];
jndiDestinationFile = args[2];
jndiDestinationName = args[3];
if(System.getProperty("JMS11Sample.reenableSSLv3")!=null)
{
System.out.println("Force re-enabling SSLv3 support");
String disabledAlgorithms = Security.getProperty("jdk.tls.disabledAlgorithms");
Security.setProperty("jdk.tls.disabledAlgorithms", disabledAlgorithms .replace("SSLv3,", ""));
}
//Run the demo
System.out.println("creating threads...");
Jms11ListenerSample[] myJMSthreads = new Jms11ListenerSample[DEFAULT_NUMBER_OF_THREADS];
for (int i=0; i<DEFAULT_NUMBER_OF_THREADS; i++) {
myJMSthreads[i] = new Jms11ListenerSample();
myJMSthreads[i].start();
}
System.out.println("joining threads...");
for (int i=0; i<DEFAULT_NUMBER_OF_THREADS; i++) {
try {myJMSthreads[i].join();
} catch (InterruptedException e) {
}
}
System.out.println("application ended normally");
} //END MAIN()
//END OF CLASS
//This is what I am trying in spring Boot configuration -
#Configuration
#EnableJms
public class JmsConfig {
#Value("${ibm.mq.queueManager}")
private String queueManager;
#Value("${ibm.mq.port}")
private int port;
#Value("${spring.jms.jndi-cf-name}")
private String jndiCfName;
#Value("${spring.jms.jndi-LDAP-Server}")
private String jndiLdapServer;
#Bean
public ConnectionFactory getConnection(){
ConnectionFactory connFactory = null;
InitialContext ctx = null;
Hashtable jndiContext = new Hashtable();
jndiContext.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
jndiContext.put(Context.SECURITY_AUTHENTICATION, "none");
jndiContext.put(Context.PROVIDER_URL, jndiLdapServer);
try {
ctx = new InitialContext(jndiContext);
connFactory = (ConnectionFactory) ctx.lookup(jndiCfName);
if (connFactory == null) {
System.out.println("FATAL ERROR: CF " + jndiCfName);
System.exit(1);
}
ctx.close();
} catch (NamingException e) {
e.printStackTrace(); System.exit(1);
} catch(ClassCastException e) {
e.printStackTrace(); System.exit(1);
}
return connFactory;
}
#Bean
public JmsTemplate jmsTemplate(){
return new JmsTemplate(getConnection());
}
}
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 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();
}
}
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 have written a producer application that enqueue JMS messages by using Executer Service in activeMQ and it is working finely but the problem is it's taking long time to enqueue messages .
there are three files:
1. ExecutePushServer.java
2. ActiveMQProducer.java
3. SendPush.java
ExecutePushServer.java:
package com.rh.pushserver;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
public class ExecutePushServer {
/**
* #uthor ankit
*/
static int maxThread = 0;
static BufferedReader br = null;
static String fileLocation = null;
static List<String> tokenList = new ArrayList<String>();
private static String txt;
static Properties configFile = new Properties();
private final static Logger logger = Logger
.getLogger(ExecutePushServer.class.getName());
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
configFile.load(ExecutePushServer.class.getClassLoader()
.getResourceAsStream("config.properties"));
maxThread = Integer.valueOf(configFile.getProperty("POOL_SIZE"));
fileLocation = configFile.getProperty("LOCATION");
txt = configFile.getProperty("txt");
logger.info("Message text is : " + txt);
br = new BufferedReader(new FileReader(fileLocation));
ActiveMQProducer mqProducer = new ActiveMQProducer();
tokenList = getList(br);
logger.info("tokenList created.");
ExecutorService executor = Executors.newFixedThreadPool(maxThread);
for (String id : tokenList) {
Runnable work = new SendPush(mqProducer, id);
executor.execute(work);
}
// This will make the executor accept no new threads
// and finish all existing threads in the queue
logger.info("All Ids Entered in Pool.");
executor.shutdown();
while (!executor.awaitTermination(10, TimeUnit.MINUTES)) {
logger.info("Inside awaitTermination");
}
mqProducer.closeConnection();
} catch (IOException e) {
logger.error("Error in Reading File" + e);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
logger.error("Error in termination of executer" + e);
} finally {
try {
if (br != null)
br.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
private static List<String> getList(BufferedReader br) {
// TODO Auto-generated method stub
String currentLine;
try {
while ((currentLine = br.readLine()) != null) {
tokenList.add(currentLine);
}
return tokenList;
} catch (IOException e) {
logger.error("Error occured in creating tokenList !" + e);
return null;
}
}
}
ActiveMQProducer.java
package com.rh.pushserver;
import java.io.IOException;
import java.util.Properties;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.MessageProducer;
import javax.jms.Session;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.log4j.Logger;
public class ActiveMQProducer {
/**
* #uthor ankit
*/
private final String url = ActiveMQConnection.DEFAULT_BROKER_URL;
private final String subject = "PUSH_NOTIFICATION";
private Connection connection;
private Session session;
private String txt=null;
private MessageProducer producer;
private MapMessage mapMessage;
static Properties configFile = new Properties();
private final static Logger logger=Logger.getLogger(ActiveMQProducer.class.getName());
public ActiveMQProducer() {
try {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
connection = connectionFactory.createConnection();
connection.start();
logger.info("Connection Created.");
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue(subject);
producer = session.createProducer(destination);
logger.info("Producer generated");
configFile.load(ActiveMQProducer.class.getClassLoader().getResourceAsStream("config.properties"));
txt=configFile.getProperty("txt");
mapMessage = session.createMapMessage();
} catch (JMSException e) {
// TODO Auto-generated catch block
logger.error("Error JMS Exception occured in creating connection"+e);
} catch (IOException e) {
// TODO Auto-generated catch block
logger.error("Exception occured while opening file "+e);
}
}
public MessageProducer getProducer() {
return producer;
}
public void enqueueMessage(String id){
try {
mapMessage.setString("ID", id);
mapMessage.setString("DISPLAY_STRING", txt);
mapMessage.setInt("BADGE_COUNT", 1);
mapMessage.setString("DEVICE_TYPE", "ANDROID");
producer.send(mapMessage);
logger.info("Sent on : "+id);
} catch (JMSException e) {
// TODO Auto-generated catch block
logger.error("Error while Enqueue"+e);
}
}
public void closeConnection(){
try {
connection.close();
logger.info("Connection closed");
} catch (JMSException e) {
// TODO Auto-generated catch block
logger.error("Error in connection closer"+e);
}
}
}
SendPush.java
package com.rh.pushserver;
public class SendPush implements Runnable {
/**
* #uthor ankit
*/
private String id;
private ActiveMQProducer mqProducer;
public SendPush(ActiveMQProducer mqProducer,String id) {
this.id=id;
this.mqProducer=mqProducer;
}
#Override
public void run() {
mqProducer.enqueueMessage(id);
}
}
please help me !!
The first thing I'd look at is your thread usage; you're creating a new thread for each message, which could definitely be a big part of why your performance isn't faster. Why can't you have your threads run a loop that sends messages till they run out of messages to send, and then have N threads that split the work?
You'll also probably want to run your reader logic in a separate thread, where it reads the file as fast as it can and hands off the things it reads to the threads, so you don't have to wait while the file is read to even get started. Make sure you make the data structures that you use to pass data between the reader threads and the message threads are thread-safe!
Once you do that, if the speed isn't where you want it to be, look at the broker's configuration. (And post it here, if you want someone to look at it.) In particular, if your messages are persistent, then look at where they're being persisted and see if there are other options that would be faster. (JDBC storage is generally the slowest persistence option, so consider other options.) Or you might even be able to make your messages non-persistent to make them faster; you'll have to decide if you can live with that trade-off. Figure out whether your messages are being passed asynchronously or synchronously; if sync, you might want to enable async instead. And make sure that producer flow control isn't kicking in (check the broker logs); if it is, then your consumers are probably too slow and are slowing down your producers.