I have made a simple ActiveMQ application.
It listens to a queue. If a message comes, print out the dataId
Here is the code:
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import org.apache.activemq.ActiveMQConnectionFactory;
public class MQ implements MessageListener {
private Connection connection = null;
private Session session = null;
private Destination destination = null;
private void errorOnConnection(JMSException e) {
System.out.println("MQ is having problems. Exception::"+ e);
}
private void init() throws JMSException {
String BROKER_URL = "failover:(tcp://myQueue001:61616,tcp://myQueue002:61616)?randomize=false";
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
connection = connectionFactory.createConnection("user", "password");
connection.setExceptionListener(
new ExceptionListener() {
#Override public void onException(JMSException e) {
errorOnConnection(e);
}
});
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue("myQueue");
MessageConsumer consumer = session.createConsumer(destination);
consumer.setMessageListener(this);
}
public boolean start() {
try {
if(connection==null )
init();
connection.start();
} catch (Exception e) {
System.out.println("MQListener cannot be started, exception: " + e);
}
return true;
}
#Override
public void onMessage(Message msg) {
try {
if(msg instanceof MapMessage){
MapMessage m = (MapMessage)msg;
int dataId = m.getIntProperty("dataId");
System.out.println(dataId);
}
} catch (JMSException e) {
System.out.println("Got an exception: " + e);
}
}
public static void main(String[] args) {
MQ mq = new MQ();
mq.start();
}
}
It works fine and does what it is meant to accomplish.
However, the problem is that it can run only for several days. After several days, it just quits silently without any exceptions or error.
The queue I am listening to is from 3rd party. From a guy there, the queue sometimes will be closed or restarted or interrupted.
But I think even if that happen, the default ActiveMQ settings will handle it by consistently reconnect to it, right? (according to http://activemq.apache.org/cms/configuring.html)
So any other possible causes which lead my code to quitting silently?
Depends on bit on your version. Since you are not doing anything yourself to keep the application running but instead depending on the ActiveMQ code to keep at least one non-deamon thread running. In some ActiveMQ versions the client wasn't always doing this so your application could quite while a failover was occurring. Best bet is to switch to v5.8.0 which I believe had some fixes for this.
You could add some polling code in main to read something from console or what not to ensure that the client stays up until you are sure you want it to go down.
Related
I'm using spring boot with mq-jms-spring-boot-starter to create a JMS Listener application which reads a message from a queue, process it and forward the message in to another queue.
In case of a poison message scenario, I'm trying to generate an alert. However, in order to not generate multiple alerts to the same message, I'm thinking of comparing the JMSXDeliveryCount with BOTHRESH value and generate the alert in the last redelivery before sending to the BOQ.
BOTHRESH and BOQNAME are configured for the source queue.
#JmsListener(destination = "${sourceQueue}")
public void processMessages(Message message) {
TextMessage msg = (TextMessage) message;
int boThresh;
int redeliveryCount;
try {
boThresh = message.getIntProperty("<WHAT COMES HERE>");
redeliveryCount = message.getIntProperty("JMSXDeliveryCount");
String processedMessage = this.processMessage(message);
this.forwardMessage("destinationQueue", processedMessage);
} catch (Exception e) {
if (redeliveryCount >= boThresh) {
//generate alert here
}
}
}
How should I get the value of BOTHRESH here? Is it possible at all? I tried to get all the available properties using getPropertyNames() method and following are all the properties I see.
JMS_IBM_Format
JMS_IBM_PutDate
JMS_IBM_Character_Set
JMSXDeliveryCount
JMS_IBM_MsgType
JMSXUserID
JMS_IBM_Encoding
JMS_IBM_PutTime
JMSXAppID
JMS_IBM_PutApplType
This will do it, but the code does need admin access to an admin channel, which may not be optimal for a client application.
The Configuration
import com.ibm.mq.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ibm.mq.constants.CMQC;
import java.util.Hashtable;
#Configuration
public class MQConfiguration {
protected final Log logger = LogFactory.getLog(getClass());
#Value("${ibm.mq.queueManager:QM1}")
public String qMgrName;
#Value("${app.mq.admin.channel:DEV.ADMIN.SVRCONN}")
private String adminChannel;
#Value("${app.mq.host:localhost}")
private String host;
#Value("${app.mq.host.port:1414}")
private int port;
#Value("${app.mq.adminuser:admin}")
private String adminUser;
#Value("${app.mq.adminpassword:passw0rd}")
private String password;
#Bean
public MQQueueManager mqQueueManager() {
try {
Hashtable<String,Object> connectionProperties = new Hashtable<String,Object>();
connectionProperties.put(CMQC.CHANNEL_PROPERTY, adminChannel);
connectionProperties.put(CMQC.HOST_NAME_PROPERTY, host);
connectionProperties.put(CMQC.PORT_PROPERTY, port);
connectionProperties.put(CMQC.USER_ID_PROPERTY, adminUser);
connectionProperties.put(CMQC.PASSWORD_PROPERTY, password);
return new MQQueueManager(qMgrName, connectionProperties);
} catch (MQException e) {
logger.warn("MQException obtaining MQQueueManager");
logger.warn(e.getMessage());
}
return null;
}
}
Obtain the Queue's backout threshold
import com.ibm.mq.*;
import com.ibm.mq.constants.CMQC;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
#Component
public class Runner {
protected final Log logger = LogFactory.getLog(getClass());
#Value("${app.mq.queue:DEV.QUEUE.1}")
private String queueName = "";
private final MQQueueManager mqQueueManager;
Runner(MQQueueManager mqQueueManager) {
this.mqQueueManager = mqQueueManager;
}
#Bean
CommandLineRunner init() {
return (args) -> {
logger.info("Determining Backout threshold");
try {
int[] selectors = {
CMQC.MQIA_BACKOUT_THRESHOLD,
CMQC.MQCA_BACKOUT_REQ_Q_NAME };
int[] intAttrs = new int[1];
byte[] charAttrs = new byte[MQC.MQ_Q_NAME_LENGTH];
int openOptions = MQC.MQOO_INPUT_AS_Q_DEF | MQC.MQOO_INQUIRE | MQC.MQOO_SAVE_ALL_CONTEXT;
MQQueue myQueue = mqQueueManager.accessQueue(queueName, openOptions, null, null, null);
logger.info("Queue Obtained");
MQManagedObject moMyQueue = (MQManagedObject) myQueue;
moMyQueue.inquire(selectors, intAttrs, charAttrs);
int boThresh = intAttrs[0];
String backoutQname = new String(charAttrs);
logger.info("Backout Threshold: " + boThresh);
logger.info("Backout Queue: " + backoutQname);
} catch (MQException e) {
logger.warn("MQException Error obtaining threshold");
logger.warn(e.getMessage());
}
};
}
}
This sounds like you are mixing retriable and non-retriable error handling.
If you are tracking redelivers and need to send an alert, then you probably do not want to set a BOTHRESH value, and instead manage it all in your client-side code.
Recommended consumer error handling pattern:
If the message is invalid (ie.. bad JSON or XML) move to DLQ immediately. The message will never improve in quality and there is no reason to do repeated retries.
If the 'next step' in processing is down (ie. the database) reject delivery and allow redelivery delays and backout retries to kick in. This also has the benefit of allowing other consumers on the queue to attempt processing the message and eliminates the problem where one consumer has a dead path from holding up a messages.
Also, consider that using client-side consumer code to do monitoring and alerting can be problematic, since it combines different functions. If your goal is to track invalid messages, monitoring the DLQ is generally a better design pattern and it removes 'monitoring' code from your consumer code.
I am using CuratorFramework (I'm still a newbie) in order to connect to a Zookeeper instance. I would like to import a configuration but before that I would like to test that my program is able to connect to Zookeeper. So far I have something like that:
public Boolean zookeeperRunning() {
CuratorFramework curatorFramework =
CuratorFrameworkFactory.newClient(zookeeperConn, new RetryOneTime(1));
curatorFramework.start();
CuratorZookeeperClient zkClient = curatorFramework.getZookeeperClient();
return zkClient.isConnected();
}
I've already started ZooKeeper on my local machine and I checked the connection with zkCli and the client is able to connect to it. The zookeeperCon variable is set to "127.0.0.1:2181" (I tried with localhost:2181 as well). The problem is that the above method always returns false despite the fact that zkServer is up n running. Most probably, the syntax is not correct but I could not find a solution online. Could you please help me with why the above code cannot find the zkServer which is up and running?
You can use a builder to create a configured client and setup a listener to monitor your zk instance's state:
// start client
client = CuratorFrameworkFactory.builder()
.connectString("localhost:2181")
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.namespace("heavenize")
.build();
client.getConnectionStateListenable().addListener(new ConnectionStateListener() {
#Override
public void stateChanged(CuratorFramework client, ConnectionState newState)
{
log.info("State changed to: "+newState);
}
});
}
You should first connect to zookeeper after you get the zkClient, if success, then check the isConnected status. Demo code below(Refer: here):
private static CuratorFramework buildConnection(String url) {
CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(url, new ExponentialBackoffRetry(100, 6));
// start connection
curatorFramework.start();
// wait 3 second to establish connect
try {
curatorFramework.blockUntilConnected(3, TimeUnit.SECONDS);
if (curatorFramework.getZookeeperClient().isConnected()) {
return curatorFramework.usingNamespace("");
}
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
// fail situation
curatorFramework.close();
throw new RuntimeException("failed to connect to zookeeper service : " + url);
}
you should connect to zookeeper server then check it. for example:
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.test.TestingServer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertTrue;
public class ZkClientTest {
TestingServer zkServer;
#Before
public void startZookeeper() throws Exception {
zkServer = new TestingServer(2181);
zkServer.start();
}
#After
public void stopZookeeper() throws IOException {
zkServer.stop();
}
#Test
public void should_connect_to_zookeeper_server_when_config_use_default_localhost_2181()
throws InterruptedException {
CuratorFramework client = ZkClient.getInstance().getClient();
try {
client.blockUntilConnected(3, TimeUnit.SECONDS);
assertTrue(ZkClient.getInstance().getClient().getZookeeperClient().isConnected());
} finally {
ZkClient.getInstance().close();
}
}
}
I am new to ActiveMQ. I have tried to implement producer-consumer (sender-receiver) in activemq. In my code, I am easily send & receive the messages from single producer to single consumer via ActiveMQ. But, the problem is, I can't send the message to multiple consumers from the same producer.
Here is my producer & consumer class.
MsgProducer.java
package jms_service;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
public class MsgProducer {
private static String url = "failover://tcp://localhost:61616";
public static javax.jms.ConnectionFactory connFactory;
public static javax.jms.Connection connection;
public static javax.jms.Session mqSession;
public static javax.jms.Topic topic;
public static javax.jms.MessageProducer producer;
public static void main(String[] args) throws JMSException {
connFactory = new ActiveMQConnectionFactory(url);
connection = connFactory.createConnection("system","manager");
connection.start();
mqSession = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
topic = mqSession.createTopic("RealTimeData");
producer = mqSession.createProducer(topic);
producer.setTimeToLive(30000);
TextMessage message = mqSession.createTextMessage();
int seq_id =1;
while(true)
{
message.setText("Hello world | " +"seq_id #"+seq_id);
producer.send(message);
seq_id++;
System.out.println("sent_msg =>> "+ message.getText());
// if(seq_id>100000) break;
try {
Thread.sleep(1000);
}
catch (InterruptedException e) { e.printStackTrace();}
}
}
}
MsgConsumer.java
package jms_service;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
public class MsgConsumer {
private static String url = "failover://tcp://localhost:61616";
public static javax.jms.ConnectionFactory connFactory;
public static javax.jms.Connection connection;
public static javax.jms.Session mqSession;
public static javax.jms.Topic topic;
public static javax.jms.MessageConsumer consumer;
public static void main(String[] args) throws JMSException, InterruptedException {
connFactory = new ActiveMQConnectionFactory(url);
connection = connFactory.createConnection("system", "manager");
connection.setClientID("0002");
//connection.start();
mqSession = connection.createSession(true, Session.CLIENT_ACKNOWLEDGE);
topic = mqSession.createTopic("RealTimeData");
consumer = mqSession.createDurableSubscriber(topic, "SUBS01");
connection.start();
MessageListener listner = new MessageListener() {
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
TextMessage txtmsg = (TextMessage) message;
Calendar cal = Calendar.getInstance();
//cal.getTime();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
String time = sdf.format(cal.getTime());
String msg="received_message =>> "+ txtmsg.getText() + " | received_at :: "+time;
System.out.println(msg);
//consumer.sendData(msg);
}
} catch (JMSException e) {
System.out.println("Caught:" + e);
e.printStackTrace();
}
}
};
consumer.setMessageListener(listner);
}
}
Can anyone help to figure out the way for sending message to multiple consumers.
Thanks in advance.
Queue semantics deliver a message once-and-only-once across all consumers. This is per the JMS spec (a great read to understand the basics).
Topic semantics deliver a message to every consumer. So, a Topic may be the answer to your needs.
Assuming your question is
Can anyone help to figure out the way for sending message to multiple consumers
and without reading through your complete code, an approach could be to put your clients in a collection
static Vector<consumer> vecConsumer;
where you put in every new client and give a reference to all existing clients.
The broadcasting is just like sending to a single client, encapsulated in, for an example, a foreach loop
for(consumer cons : vecConsumer)
{
//send stuff or put in sending queue
}
Topics is the best route. One producer to many consumers or one publisher to many subscribers. With Queues you have go write a loop to to get all the possible consumers and use different destinations to send the messages. Your motive would also determine whether to use Queues or Topics.
If u you think your consumers can be offline or have network issues then choose queues. In this case when they come back on they will receive the pending messages
With topics there is no way they will receive the message when there is a disconnection unless u explicitly persist the message however new messages would overwrite them
What are the options available to develop Java applications using Service Bus for Windows?
Java Message Broker API - This need ACS to work with, which SB for Win doesnt support.
AMQP - This doesnt seem to work on SB for Windows, I keep getting error
org.apache.qpid.amqp_1_0.client.Sender$SenderCreationException: Peer did not create remote endpoint for link, target:
While the same code works with Azure SB. So AMQP on SB for Windows seems to be not fully working?
Correct me if I have missed something?
Update
To test AMQP on local machine, this is what I did
Installed Service bus 1.1 on my local machine
Took the sample mentioned here http://www.windowsazure.com/en-us/develop/java/how-to-guides/service-bus-amqp/
Created a new namespace on my local machine
Specified the following connection string in servicebus.properties (which is correctly referred in the code
connectionfactory.SBCF = amqps://<username>:<password>#<MachineName>:5671/StringAnalyzerNS/
queue.QUEUE = queue1
Code is updated with certificates.
At runtime I get this error
javax.jms.JMSException: Peer did not create remote endpoint for link, target: queue1
at org.apache.qpid.amqp_1_0.jms.impl.MessageProducerImpl.<init>(MessageProducerImpl.java:77)
at org.apache.qpid.amqp_1_0.jms.impl.SessionImpl.createProducer(SessionImpl.java:348)
at org.apache.qpid.amqp_1_0.jms.impl.SessionImpl.createProducer(SessionImpl.java:63)
at com.stringcompany.Analyzer.SimpleSenderReceiver.<init>(SimpleSenderReceiver.java:70)
at com.stringcompany.Analyzer.SimpleSenderReceiver.main(SimpleSenderReceiver.java:95)
Caused by: org.apache.qpid.amqp_1_0.client.Sender$SenderCreationException: Peer did not create remote endpoint for link, target: queue1
at org.apache.qpid.amqp_1_0.client.Sender.<init>(Sender.java:171)
at org.apache.qpid.amqp_1_0.client.Sender.<init>(Sender.java:104)
at org.apache.qpid.amqp_1_0.client.Sender.<init>(Sender.java:97)
at org.apache.qpid.amqp_1_0.client.Sender.<init>(Sender.java:83)
at org.apache.qpid.amqp_1_0.client.Sender.<init>(Sender.java:69)
at org.apache.qpid.amqp_1_0.client.Sender.<init>(Sender.java:63)
at org.apache.qpid.amqp_1_0.client.Session.createSender(Session.java:74)
at org.apache.qpid.amqp_1_0.client.Session.createSender(Session.java:66)
at org.apache.qpid.amqp_1_0.jms.impl.MessageProducerImpl.<init>(MessageProducerImpl.java:72)
... 4 more
javax.jms.JMSException: Session remotely closed
With the same code If I point to Azure service bus by setting the SB namespace and queue like below
connectionfactory.SBCF = amqps://<Policy name>:<Sec. Key>#<ns>.servicebus.windows.net
queue.QUEUE = testq
This works, messages are exchanged.
Here is the code if someone wants to try it
package com.stringcompany.Analyzer;
//SimpleSenderReceiver.java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Hashtable;
import java.util.Random;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
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.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
public class SimpleSenderReceiver implements MessageListener {
private static boolean runReceiver = true;
private Connection connection;
private Session sendSession;
private Session receiveSession;
private MessageProducer sender;
private MessageConsumer receiver;
private static Random randomGenerator = new Random();
public SimpleSenderReceiver() throws Exception {
// Configure JNDI environment
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.qpid.amqp_1_0.jms.jndi.PropertiesFileInitialContextFactory");
env.put(Context.PROVIDER_URL, "D:\\Java\\Azure\\workspace\\Analyzer\\src\\main\\resources\\servicebus.properties");
Context context = new InitialContext(env);
// Lookup ConnectionFactory and Queue
ConnectionFactory cf = (ConnectionFactory) context.lookup("SBCF");
System.out.println("cf:"+cf);
// Create Connection
connection = cf.createConnection();
System.out.println("connection :"+connection);
connection.setExceptionListener(new ExceptionListener() {
public void onException(JMSException arg0) {
System.err.println(arg0);
}
});
connection.start();
// Create sender-side Session and MessageProducer
sendSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
System.out.println("Session open");
Destination queue = (Destination) context.lookup("QUEUE");
System.out.println("queue:"+queue);
sender = sendSession.createProducer(queue);
Queue q=(Queue) queue;
System.out.println(sender.getDestination());
System.out.println("sender:"+sender);
if (runReceiver) {
System.out.println("Waitng for new message");
// Create receiver-side Session, MessageConsumer,and MessageListener
receiveSession = connection.createSession(false,
Session.CLIENT_ACKNOWLEDGE);
receiver = receiveSession.createConsumer(queue);
receiver.setMessageListener(this);
connection.start();
}
}
public static void main(String[] args) {
try {
if ((args.length > 0) && args[0].equalsIgnoreCase("sendonly")) {
runReceiver = false;
}
//System.setProperty("javax.net.debug","ssl");
System.setProperty("javax.net.ssl.trustStore","D:\\Java\\Azure\\workspace\\Analyzer\\src\\main\\resources\\SBKeystore.keystore");
System.setProperty("log4j.configuration","D:\\Java\\Azure\\workspace\\Analyzer\\src\\main\\resources\\log4j.properties");
SimpleSenderReceiver simpleSenderReceiver = new SimpleSenderReceiver();
System.out
.println("Press [enter] to send a message. Type 'exit' + [enter] to quit.");
BufferedReader commandLine = new java.io.BufferedReader(
new InputStreamReader(System.in));
while (true) {
String s = "Message";//commandLine.readLine();
if (s.equalsIgnoreCase("exit")) {
simpleSenderReceiver.close();
System.exit(0);
} else {
simpleSenderReceiver.sendMessage();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void sendMessage() throws JMSException {
TextMessage message = sendSession.createTextMessage();
message.setText("Test AMQP message from JMS");
long randomMessageID = randomGenerator.nextLong() >>> 1;
message.setJMSMessageID("ID:" + randomMessageID);
sender.send(message);
System.out.println("Sent message with JMSMessageID = "
+ message.getJMSMessageID());
}
public void close() throws JMSException {
connection.close();
}
public void onMessage(Message message) {
try {
System.out.println("Received message with JMSMessageID = "
+ message.getJMSMessageID());
message.acknowledge();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Hi we had the same problems and thankfully MS updated their documentation to show how to do this correctly. :
http://msdn.microsoft.com/en-us/library/dn574799.aspx
The simplest answer to the question is as you should URL Encode the SASPolicyKey.
connectionfactory.SBCF = amqps://[SASPolicyName]:[SASPolicyKey]#[namespace].servicebus.windows.net
Where SASPolicyKey should be URL-Encoded.
AMQP 1.0 is supported with Service Bus 1.1 for windows server. Basically there are two differences between the cloud and on-prem usage of AMQP in ServiceBus:
1. Addressing: You will need to build an AMQP connection strings (and will need DNS in case you're looking for HA)
2. Authentication: You will need to use domain joined accounts as ACS is not there on-prem. You will also need to distribute your SB certificate to your clients.
Ok, I have sorted the first issue (Java Message Broker API not supporting SAS endpoint), by writing a wrapper which will seamlessly work with existing API. You can get the library from this GitHub repository. With this, I can develop/test my Java application on local service bus environment and host it on Azure / On-Premise Service Bus farm.
https://github.com/Dhana-Krishnasamy/ServiceBusForWindows-SASWrapper
The sender and receiver Queues you will have to configure differently. Here is an example of my working configuration (servicebus.properties):
connectionfactory.SBCF = amqps://$PolicyName:$UrlEncodedKey#$Your-EventHub-NamespaceName.servicebus.windows.net
queue.EventHubSender=$YourEventHubName
queue.EventHubReceiver=$YourEventHubName/ConsumerGroups/$YourConsumerGroupName/Partitions/1
Replace appropriately your own '$' items in there.
The Shared Policy Key has to be URL encoded.
Make sure that your sender will reference the 'EventHubSender' defined in this config and the receiver will reference the 'EventHubReciever'.
Grab the Azure Java SDK from http://www.windowsazure.com/en-us/develop/java/ and then follow this guide: http://www.windowsazure.com/en-us/develop/java/how-to-guides/service-bus-queues/
I want to write code for pulling messages from Activemq.I don't want to pull all the messages from Activemq at a time,because my requirement is whenever my Java Application receives 1 message from Activemq,based on message body I will find corresponding HTTP Link and forward to that Link. For this entire logic I wrote 2 .java files names are
MessageConsumer.java
MyListener.java
MessageConsumer.java file only for connection establishing.The corresponding code is in below.
package PackageName;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.jms.*;
import org.apache.activemq.ActiveMQConnectionFactory;
public class MessageConsumer extends HttpServlet {
#Override
protected void service(HttpServletRequest arg0, HttpServletResponse arg1)
throws ServletException, IOException {
try {
//creating connectionfactory object for way
ConnectionFactory connectionFactory=new
ActiveMQConnectionFactory("admin","admin","tcp://localhost:61617");
//establishing the connection b/w this Application and Activemq
Connection connection=connectionFactory.createConnection();
Session session=connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue=session.createQueue("MessageTesing");
javax.jms.MessageConsumer consumer=session.createConsumer(queue);
//fetching queues from Activemq
MessageListener listener = new MyListener();
consumer.setMessageListener(listener);
connection.start();
System.out.println("Press a key to terminate");
}
catch (Exception e) {
// TODO: handle exception
}
}
}
MyListener.java file is for triggering corresponding Applications.code is in below
package PackageName;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
public class MyListener implements MessageListener {
public void onMessage(Message msg) {
try {
TextMessage msg1=(TextMessage)msg;
//just for your understanding I mention dummy code
System.out.println(msg1.getText());
if (msg1.getText()=="Google") {
System.out.println("Forwarding http link to Google");
}
else {
System.out.println("Forwarding http link to Facebook");
}
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
in my post, I am triggering Google and Facebook links.But As far my requirements I will call my own Applications.each Application taking more than 20 min.So I want to pull messages one by one.once previous message process completed then only it will receive another message from Activemq .
But right know I am getting all the messages at a time.How can I fix this.I seen Activemq-Hellowworld program.I didn't understand.
Sorry I am new to Java technology.can anyone help me.
Thanks.
If you are using a MessageListener, then you are actually receiving messages asynchronously (in another thread).
You are probably looking for synchronous message reception, so try this in your main thread:
final QueueReceiver queueReceiver = queueSession.createReceiver(queue);
queueConnection.start();
while (true) {
Message message = queueReceiver.receive();
// Process your message: insert the code from MyListener.onMessage here
// Possibly add an explit message.acknowledge() here,
// if you want to make sure that in case of an exception no message is lost
// (requires Session.CLIENT_ACKNOWLEDGE, when you create the queue session)
// Possibly terminate program, if a certain condition/situation arises
}
without a MessageListener.
receive() blocks until a message is available, so your main thread (and thus your program) waits in the receive method. If a message arrives, it will receive and process it.
Update
If you use
Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
then you should call
message.acknowledge()
after the message has been processed completely.
Whereas in case of Session.AUTO_ACKNOWLEDGE the message is removed from the queue immediately (and is therefore lost, if the program terminates whilte processing the message).
Instead of using a MessageListener you could use the receive() method in the MessageConsumer object. This way you only get one message each time you call the receive() method.
MessageConsumer consumer = session.createConsumer(destination);
Message message = consumer.receive(1000);