How to get number of pending message in a jms queue - jms

Is there any way to get count number of pending messages in jms queue. My aim is to close the connection if there is no message remaining in the queue to process. how can i achieve this.
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection("admin", "admin");
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue(subject);
MessageConsumer consumer = session.createConsumer(destination);
while (true) {
Message message = consumer.receive();
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
System.out.println("Incoming Message:: '" + textMessage.getText() + "'");
}
}

The only reliable way to get the true Queue count form the broker is to use the JMX MBean for the Queue and call the getQueueSize method.
The other programmatic alternative is to use the Statistics Broker Plugin which requires that you be able to change broker configuration to install it. Once installed you can send a special message to the control queue and get a response with details for the destination you want to monitor.
Using a QueueBrowser doesn't give you a true count because the browser has a max limit on how many messages it will page into memory to send you, so if your queue is deeper than the limit you won't get the actual size, just the value of the max page size limit.

I have done this by using createBrowser method below is my updated code.
public static void main(String[] args) throws JMSException {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection("admin", "admin");
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue(subject);
int queueSize = QueueConsumer.getQueueSize(session, (Queue) destination);
System.out.println("QUEUE SIZE: " + queueSize);
MessageConsumer consumer = session.createConsumer(destination);
for (int i = 0; i < queueSize; i++) {
Message message = consumer.receive();
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
System.out.println("Incomming Message: '" + textMessage.getText() + "'");
}
}
connection.close();
}
private int getQueueSize(Session session, Queue queue) {
int count = 0;
try {
QueueBrowser browser = session.createBrowser(queue);
Enumeration elems = browser.getEnumeration();
while (elems.hasMoreElements()) {
elems.nextElement();
count++;
}
} catch (JMSException ex) {
ex.printStackTrace();
}
return count;
}

I have done this with jmx it worked thanx #Tim Bish
here is my updated code
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://0.0.0.0:44444/jndi/rmi://0.0.0.0:1099/karaf-root");
HashMap<String, String[]> environment = new HashMap<String, String[]>();
String[] creds = { "admin", "admin" };
environment.put(JMXConnector.CREDENTIALS, creds);
JMXConnector jmxc = JMXConnectorFactory.connect(url, environment);
MBeanServerConnection connection = jmxc.getMBeanServerConnection();
ObjectName nameConsumers = new ObjectName("org.apache.activemq:type=Broker,brokerName=amq,destinationType=Queue,destinationName=myqueue");
DestinationViewMBean mbView = MBeanServerInvocationHandler.newProxyInstance(connection, nameConsumers, DestinationViewMBean.class, true);
long queueSize = mbView.getQueueSize();
System.out.println(queueSize);

Just break the loop and close the connection if your JMS Message is null..
while (true) {
Message message = consumer.receive(2000);
if (message == null){
break;
}
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
System.out.println("Incoming Message:: '" + textMessage.getText() + "'");
}
}
connection.close();

Related

unable to make activemq consumer to deque

I am creating a JMS chat application using activemq and spring boot. I am trying to send message from producer to multiple subscribers. I am able to send message i.e message is en-queued. but in my receiver part message is unable to de-queue.` I am using the below code for communicating message from producer to multiple subscribers.
public class WelcomeController implements MessageListener {
public static Boolean TRANSACTIONAL = false;
public static String TOPIC_NAME = "firstTopic";
public static String BROKER_URL = "tcp://localhost:61616";
public static String BROKER_USERNAME = "admin";
public static String BROKER_PASSWORD = "admin";
public void createProducer() throws JMSException {
Connection connection = null;
Session session = null;
try {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
connectionFactory.setBrokerURL(BROKER_URL);
connectionFactory.setPassword(BROKER_USERNAME);
connectionFactory.setUserName(BROKER_PASSWORD);
connection = connectionFactory.createConnection();
connection.setClientID("CircliTopic");
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
for (int i = 1; i <= 3; i++) {
session = connection.createSession(TRANSACTIONAL,
Session.AUTO_ACKNOWLEDGE);
Topic destination = session.createTopic(TOPIC_NAME);
MessageProducer producer = session.createProducer(destination);
TextMessage message = session.createTextMessage();
message.setText( "My text message was send and received");//
System.out.println("Sending text '" + message + "'");
producer.send(message);
MessageConsumer consumer = session
.createDurableSubscriber(destination, "Listener" + i);
consumer.setMessageListener(new WelcomeController());
}
} finally {
connection.close();
}`
}
#Override
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
TextMessage text = (TextMessage) message;
System.out.println(" - Consuming text msg: " + text.getText());
} else if (message instanceof ObjectMessage) {
ObjectMessage objmsg = (ObjectMessage) message;
Object obj = objmsg.getObject();
System.out.println(" - Consuming object msg: " + obj);
} else {
System.out.println(
" - Unrecognized Message type " + message.getClass());
}
} catch (JMSException e) {
e.printStackTrace();
}
}
I am able to get consuming text message in my console but my message is not de-queued to the subscribers and also in my activemq server message is not dequeued.
You are creating a Topic subscription only after the message has been sent and that won't work because these are Topics and a Topic with no subscriptions simply discards all messages sent to it. You need to establish a durable Topic subscription prior to any messages being sent, or switch to Queues if your design allows as a Queue will store a message sent to it until consumed.
It is hard to say more without knowing your requirements but it seems you need to spend a little bit more time understanding how Topics work.

How to handle failover in case of TIBCO

I am struggling to setup fail over in tibco JMS provider. I know how to do this in case of ActiveMQ.
What I have tried is as follows
public class TibcoJMSQueueProducer {
private static final Logger LOGGER = LoggerFactory.getLogger(FDPMetaCacheProducer.class);
private static QueueConnectionFactory factory;
private QueueConnection connection;
private QueueSession session;
#Inject
private FDPTibcoConfigDAO fdpTibcoConfigDao;
private String providerURL;
private String userName;
private String password;
#PostConstruct
public void constructProducer(){
configure();
}
private void configure() {
try {
List<FDPTibcoConfigDTO> tibcoConfigList = fdpTibcoConfigDao.getAllTibcoConfig();
if(!tibcoConfigList.isEmpty()){
FDPTibcoConfigDTO fdpTibcoConfigDTO = tibcoConfigList.get(tibcoConfigList.size()-1);
String providerURL = getProviderUrl(fdpTibcoConfigDTO);
setProviderUrl(providerURL);
String userName = fdpTibcoConfigDTO.getUserName();
String password = fdpTibcoConfigDTO.getPassword();
this.userName = userName;
this.password=password;
factory = new com.tibco.tibjms.TibjmsQueueConnectionFactory(providerURL);
}
} catch (Exception e) {
System.err.println("Exitting with Error");
e.printStackTrace();
System.exit(0);
}
}
private void setProviderUrl(String providerURL) {
this.providerURL = providerURL;
}
private String getProviderUrl(final FDPTibcoConfigDTO FDPTibcoConfigDTO) {
return TibcoConstant.TCP_PROTOCOL + FDPTibcoConfigDTO.getIpAddress().getValue() + TibcoConstant.COLON_SEPERATOR + FDPTibcoConfigDTO.getPort();
}
private Object lookupQueue(String queueName) {
Properties props = new Properties();
Object tibcoQueue = null;
props.setProperty(Context.INITIAL_CONTEXT_FACTORY, TibcoConstant.TIB_JMS_INITIAL_CONTEXT_FACTORY);
props.setProperty(Context.PROVIDER_URL, this.providerURL);
props.setProperty(TibcoConstant.TIBCO_CONNECT_ATTEMPT, "20,10");
props.setProperty(TibcoConstant.TIBCO_RECOVER_START_UP_ERROR, "true");
props.setProperty(TibcoConstant.TIBCO_RECOVER_RECONNECT_ATTEMPT, "20,10");
InitialContext context;
try {
context = new InitialContext(props);
tibcoQueue = context.lookup(queueName);
} catch (NamingException e) {
System.out.println(e.getMessage());
}
return tibcoQueue;
}
public void pushIntoQueueAsync(String message,String queueName) throws JMSException {
connection = factory.createQueueConnection(userName, password);
connection.start();
session = connection.createQueueSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
Queue pushingQueue = (Queue)lookupQueue(queueName);
QueueSender queueSender = session.createSender(pushingQueue);
queueSender.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
TextMessage sendXMLRequest = session.createTextMessage(message);
queueSender.send(sendXMLRequest);
LOGGER.info("Pushing Queue {0} ,Pushing Message : {1}", pushingQueue.getQueueName(), sendXMLRequest.getText());
}
public String pushIntoQueueSync(String message,String queueName,String replyQueueName) throws JMSException {
connection = factory.createQueueConnection(userName, password);
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = (Destination)lookupQueue(queueName);
MessageProducer messageProducer = session.createProducer(destination);
session = connection.createQueueSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
UUID randomUUID =UUID.randomUUID();
TextMessage textMessage = session.createTextMessage(message);
String correlationId = randomUUID.toString();
//Create Reply To Queue
Destination replyDestination = (Destination)lookupQueue(queueName);
textMessage.setJMSReplyTo(replyDestination);
textMessage.setJMSCorrelationID(correlationId);
String messgeSelector = "JMSCorrelationID = '" + correlationId + "'";
MessageConsumer replyConsumer = session.createConsumer(replyDestination,messgeSelector);
messageProducer.send(textMessage, javax.jms.DeliveryMode.PERSISTENT, javax.jms.Message.DEFAULT_PRIORITY, 1800000);
Message replayMessage = replyConsumer.receive();
TextMessage replyTextMessage = (TextMessage) replayMessage;
String replyText = replyTextMessage.getText();
LOGGER.info("Pushing Queue {0} ,Pushing Message : {1}", queueName, message);
return replyText;
}
public static QueueConnectionFactory getConnectionFactory(){
return factory;
}
}
In case of activeMQ we use
failover:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61616)?randomize=false&backup=true url to handle failover as provider url in ActiveMQconnectionfactory constructor. I have seen somewhere to use multiple url in case of TIBCO like this
tcp://169.144.87.25:7222,tcp://127.0.0.1:7222
How I checked failover like this.
First at all I checked using single IP (tcp://169.144.87.25:7222) . Message is getting sent and received normally(I have not posted TibcoJMSReceiver code).
I tried with another IP(tcp://169.144.87.25:7222). It was working fine.
But when I tried with
final String
PROVIDER_URL="tcp://169.144.87.25:7222,tcp://127.0.0.1:7222";
I started my program. But before giving input I shutdown first server. As a failover the message should be sent to other server.
But It shows me session closed Exception.
So Am I handling failover in a correct way or is there other configuration I have to do.
Two TIBCO EMS daemons only work 'as one' if you enable fault-tolrance in both of them. Only then will they heartbeat with each other and share resources. You should have this in the remote daemon's tibemsd.conf:
listen = tcp://7222
...
ft_active = tcp://<ip to your box>:7222
and this on your local box:
listen = tcp://7222
...
ft_active = tcp://169.144.87.25:7222
And you don't need to create connection and session every time! One Connection and Session for many messages - 'Fault Tolerance' means it'll reconnect automatically for you. You could have an init() or connect() method you call once or just add it inside your configure method:
private void configure() {
try {
...
connection = factory.createQueueConnection(userName, password);
connection.start();
session = connection.createQueueSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
Then pushIntoQueue becomes as simple as this:
public void pushIntoQueueAsync(String message,String queueName) throws JMSException {
Queue pushingQueue = (Queue)lookupQueue(queueName);
QueueSender queueSender = session.createSender(pushingQueue);
queueSender.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
TextMessage sendXMLRequest = session.createTextMessage(message);
queueSender.send(sendXMLRequest);
LOGGER.info("Pushing Queue {0} ,Pushing Message : {1}", pushingQueue.getQueueName(), sendXMLRequest.getText());
}

TCPLink Error: invalid magic in the message

Below is the Java code to consume the durable subscription
private void execute()throws Exception {
logger.debug("Creating JNDI context");
Properties jndiProps = new Properties();
jndiProps.setProperty(Context.INITIAL_CONTEXT_FACTORY, config.getJndiFactory());
jndiProps.setProperty(Context.PROVIDER_URL, config.getJndiProviderUrl());
jndiProps.setProperty(Context.SECURITY_CREDENTIALS, config.getJndiSecurityCredential());
jndiProps.setProperty(Context.SECURITY_PRINCIPAL, config.getJndiSecurityPrincipal());
logger.debug("JNDI properties: " + jndiProps.toString());
jndiContext = new InitialContext(jndiProps);
logger.debug("JNDI context created successfully");
logger.debug("Looking up ConnectionFactory : " + config.getJmsConnFactory());
TopicConnectionFactory connFactory = (TopicConnectionFactory) jndiContext.lookup(config.getJmsConnFactory());
logger.debug("Creating connection object");
conn = connFactory.createTopicConnection(config.getJmsUserName(), config.getJmsPasswd());
logger.debug("Connection object successfully created");
TopicSession session = null;
MessageConsumer subscriber = null;
try {
session = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
logger.debug("Starting connection");
conn.start();
logger.debug("Getting destinaton");
Topic topic = (Topic)jndiContext.lookup(config.getDestination());
if (topic == null) {
throw new RuntimeException("Invalid destination");
}
logger.debug("Creating durable subscriber");
if(config.isDurable()){
subscriber = session.createDurableSubscriber(topic, config.getSubscriberId());
}else{
subscriber = session.createSubscriber(topic);
}
boolean runFlag = true;
do {
logger.debug("Receiving messages...");
TextMessage message = (TextMessage) subscriber.receive(DEF_TIMEOUT_MILLIS);
if (message != null) {
logger.debug("Received message : " + message.getText());
continue;
}
logger.debug("No available messages now");
runFlag = false;
} while (runFlag);
logger.debug("There is no more messages available. Exiting...");
} finally {
if(config.isDurable()){
session.unsubscribe(config.getSubscriberId());
}
close(subscriber);
close(session);
}
}
While executing I am getting the "TCPLink Error: invalid magic in the message". And after that, session and connection is getting terminated automatically.
javax.jms.IllegalStateException: Session is closed
javax.jms.JMSException: Connection has been terminated
Please help.
Thanks in advance

jms publish/subscribe in websphere cluster

I write a publish/subscriber sample and deploy it on websphere application server in cluster environment.
but when I subscribe the message, each message only and only one time was read by MDB.
I configured durable subscription in websphere and MDB, also I set the Share durable subscriptions to always shared and set the Always activate MDBs in all servers.
each message was read only one time, I think it consumes or something else.
I set the #ActivationConfigProperty(propertyName = "useSharedSubscriptionInClusteredContainer",propertyValue = "false") in MDB (based on http://docs.oracle.com/cd/E18930_01/html/821-2438/gjzpg.html#MQAGgjzpg), but nothing happened.
I can not subscribe messages in all servers.
Also i set the messaging engine policy to High availability in the websphere bus.
the Default messaging provider is used.
where is the problem??
here is my publisher
#WebServlet("/publishServlet")
public class Testpublish extends HttpServlet {
#Resource(mappedName = "jms/ConnFact")
private static TopicConnectionFactory topicConnectionFactory;
#Resource(mappedName = "jms/topicJ")
private static Topic topic;
TopicConnection connection = null;
TopicSession session = null;
TopicPublisher publisher = null;
TextMessage message = null;
final int NUM_MSGS = 5;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
ServletOutputStream out = response.getOutputStream();
out.println("Start Testing");
System.out.println("Start Testing");
try {
connection = topicConnectionFactory.createTopicConnection();
session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
publisher = session.createPublisher(topic);
message = session.createTextMessage();
for (int i = 0; i < NUM_MSGS; i++) {
message.setText("This is testMessage " + (i + 1));
System.out.println("Sending testMessage: " + message.getText());
out.println("Sending testMessage: " + message.getText());
publisher.publish(message);
}
connection.close();
out.println("Finish Testing");
System.out.println("Finish Testing");
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
and my subscriber
#MessageDriven(mappedName = "jms/topicJ", activationConfig = {
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"),
#ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
#ActivationConfigProperty(propertyName = "subscriptionDurability",propertyValue = "Durable"),
#ActivationConfigProperty(propertyName = "clientId",propertyValue = "MyID"),
#ActivationConfigProperty(propertyName = "subscriptionName",propertyValue = "MySub")
})
public class testsubscribe implements MessageListener {
#Override
public void onMessage(Message message) {
TextMessage txtMessage = (TextMessage) message;
try {
System.out.println("---------MESSAGE RECIEVED------------" + txtMessage.getText()
+ " ..............");
} catch (JMSException e) {
e.printStackTrace();
}
}
}
I resolved the problem by disabling the messaging engine policy in the websphere bus. Now it works well.

testing if my jms listener is working

i want to test if my jms listener is working perfectly
when sending 5 messages ( for example ) i added a timer
"Threat.sleep(5000)" and after 5 seconds i want to compare the old messageID with the new messageID that means i want to know if the messages are listened or not so if the ID changes that means that they are successfuly listened
here is my OnMessage code ... but it doesnt work for me :((
public class Consumer implements MessageListener{
public Consumer() {
}
//#Override
public void onMessage(Message message) {
try {
TextMessage tm = (TextMessage) message;
int i;
TextMessage tm2 =tm;
for(i=0;i<1;i++)
{
try {
Thread.sleep(5000);
if (!tm.getJMSMessageID().equals(tm2.getJMSMessageID()))
{
System.out.println("\t----Listener not working----");
}
else {
System.out.println("Message reçu:");
System.out.println("\tTemps: " + System.currentTimeMillis() + " ms");
System.out.println("\tMessage ID: " + tm.getJMSMessageID());
System.out.println("\tCorrel. ID: " + tm.getJMSCorrelationID());
System.out.println("\tConsumed message: " + tm.getText());
System.out.println("\t----Listener working----");
}
//fin else
}
catch (InterruptedException ex) {
Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
catch (JMSException jex) {
System.out.println("Exception: " + jex);
}
}
}
So you're checking the JMS message id to ensure that no two messages have identical id's? This is probably overkill since, if your listener is receiving messages, it's working!...Nevertheless, your approach has a few problems....
TextMessage tm = (TextMessage) message;
...
TextMessage tm2 =tm;
Thread.sleep(5000);
if (!tm.getJMSMessageID().equals(tm2.getJMSMessageID()))
The above if() will always be false because you're comparing the same message; also, the sleep() statement is useless.
If you want to compare message id's to ensure they are always unique, although if the broker fails to deliver a message the same message will be redelivered and the id would be the same, but anyway, you can use this an an alternative to your code..
private static ConcurrentHashMap<String,String> mesgIdMap =
new ConcurrentHashMap<String, String>();
public Consumer() {
}
//#Override
public void onMessage(Message message) {
try {
TextMessage tm = (TextMessage) message;
if( mesgIdMap.contains( tm.getJMSMessageID()))
System.out.println("\tProcessing JMS message with same ID again!");
//add the message id to the map
mesgIdMap.put( tm.getJMSMessageID(),tm.getJMSMessageID());
//print statements here...

Resources