package com.mcafee;
import java.util.ArrayList;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.Topic;
import javax.naming.InitialContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Based on explanation and code example from ActiveMQ tutorials hosted at
* http://activemq.apache.org/statisticsplugin.html
* @author Gursev Singh Kalra @ McAfee, Inc.
*/
public class AMQOps {
private static final Logger LOG = LoggerFactory.getLogger(AMQOps.class);
private static final int TIMEOUT = 1000; // Time to wait for the Queue or Topic status to return since the query is typically blocking.
private InitialContext ctx;
private JmsLoginInfo loginInfo;
private String cfName;
ArrayList<MapMessage> dstStats = new ArrayList<MapMessage>();
private Connection connection; // Connection to the broker to last for the entire querying cycle
private Session session; // Session created for the connection. This may or may not change between different request
private Queue replyToQueue; // to connect status responses
private Message message; // message to be sent and initialized during init call
private MessageConsumer messageConsumer; // To consumer receive incoming messages containing stats
private MessageProducer messageProducer; // To send messages to the destination
private boolean statsGenerated = false;
boolean initialized = false;
public AMQOps(InitialContext ctx, String connFactName, JmsLoginInfo loginInfo) {
this.ctx = ctx;
this.loginInfo = loginInfo;
this.cfName = connFactName;
}
public AMQOps(InitialContext ctx, String connFactName) {
this(ctx, connFactName, null);
}
/**
* The init method initializes the object so it can be later used. It performs following actions<br/>
* 1. Creates a new connection and corresponding session <br/>
* 2. Creates a temporary queue to receive user response <br/>
* 3. Sets a message Consumer<br/>
* 4. Sets the message JMSReplyTo header to the temporary queue<br/>
* 5. Starts the connection and sets the initialized instance variable to true<br/>
* @throws JmsDiggerException
*/
public void init() throws JmsDiggerException {
LOG.debug("Entering init");
if(ctx == null || JmsHelper.isStringNullOrEmpty(cfName))
throw JmsHelper.buildJmsDiggerException("Null value for InitialContext or Connection Factory Name");
connection = JmsHelper.createConnection(ctx, cfName, loginInfo);
try {
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
replyToQueue = session.createTemporaryQueue();
messageConsumer = session.createConsumer(replyToQueue);
message = session.createMessage();
message.setJMSReplyTo(replyToQueue);
messageProducer = session.createProducer(null);
connection.start();
} catch (JMSException ex) {
LOG.info("Error occured while initializing AMQStats", ex);
throw JmsHelper.buildJmsDiggerException("Error occured while initializing AMQStats", ex);
}
initialized = true;
LOG.debug("Leaving init");
}
/**
* This method returns the obtained statistics as String.
* @return String
* @throws JmsDiggerException
*/
public String getStatsAsString() throws JmsDiggerException {
if(!initialized)
throw new IllegalArgumentException("No stats available");
if(!statsGenerated)
throw new IllegalStateException("No stats available");
StringBuilder sb = new StringBuilder();
sb.append("");
for(MapMessage mm : dstStats) {
sb.append(JmsHelper.mapMessageToString(mm));
sb.append("\n=================================\n\n");
}
return sb.toString();
}
/**
* Returns statistics of an ActiveMQ broker
* @return ArrayList<MapMessage>
* @throws JmsDiggerException
*/
public ArrayList<MapMessage> getBrokerStats() throws JmsDiggerException {
return getDestinationStats("ActiveMQ.Statistics.Broker", false, true);
}
/**
* Returns statistics of a Subscription
* @return ArrayList
* @throws JmsDiggerException
*/
public ArrayList<MapMessage> getSubscriptionsStats() throws JmsDiggerException {
return getDestinationStats("ActiveMQ.Statistics.Subscription", false, true);
}
/**
* Returns status of Queue with given name. It optionally allows
* you to query Queue status with or without ActiveMQ.Statistics.Destination prefix to the name
*
* @param qName Queue name
* @param prepend - Prepends ActiveMQ.Statistics.Destination. to the Queue name if true, else the qName is queried raw
* @return ArrayList
* @throws JmsDiggerException
*/
public ArrayList<MapMessage> getQueueStats(String qName, boolean prepend) throws JmsDiggerException {
return getDestinationStats(qName, prepend, true);
}
/**
* Returns status of Topic with given name. It optionally allows
* you to query Topic status with or without ActiveMQ.Statistics.Destination.
*
* @param tName - Topic name
* @param prepend - Prepends ActiveMQ.Statistics.Destination. to the topic name if true, else the tName is queried raw
* @return ArrayList
* @throws JmsDiggerException
*/
public ArrayList<MapMessage> getTopicStats(String tName, boolean prepend) throws JmsDiggerException {
return getDestinationStats(tName, prepend, false);
}
/**
* This is the main (private) method to query the status and return to the caller.
* @param qName - Queue Name
* @param prepend - Boolean value that decides if ActiveMQ.Statistics.Destination. should be prepended to the qName
* @param isQueue - the destination is a Queue if true, else it is a Topic
* @return - ArrayList
* @throws JmsDiggerException
*/
private ArrayList<MapMessage> getDestinationStats(String qName, boolean prepend, boolean isQueue) throws JmsDiggerException {
statsGenerated = false; // set to false before every attempt to get status
if(qName == null)
throw new IllegalArgumentException("null Destination name passed to getDestinationStats");
assertInitialization();
if(prepend)
qName = "ActiveMQ.Statistics.Destination." + qName;
dstStats = new ArrayList<MapMessage>();
try {
if(isQueue) {
Queue queue = session.createQueue(qName);
messageProducer.send(queue, message);
} else {
Topic topic = session.createTopic(qName);
messageProducer.send(topic, message);
}
MapMessage stats;
while((stats = (MapMessage) messageConsumer.receive(TIMEOUT)) != null) {
dstStats.add(stats);
}
statsGenerated = true;
return dstStats;
} catch (JmsDiggerException ex) {
throw ex;
} catch (JMSException ex) {
LOG.info("An error occured in getDestinationStats", ex);
throw JmsHelper.buildJmsDiggerException("An error occured in getDestinationStats", ex);
}
}
public void assertInitialization() {
if(!initialized)
throw new IllegalArgumentException(this.getClass().getName().toString() + "'s object is not initialized");
}
/**
* Main method to create a Queue or Topic on the target ActiveMQ broker
* @param dstName - Name of the destination
* @param isQueue - Boolean value indicating that the destination is a Queue (when true) or a Topic (when false)
* @return - Destination object
* @throws JmsDiggerException
*/
private Destination createDestination(String dstName, boolean isQueue) throws JmsDiggerException {
if(JmsHelper.isStringNullOrEmpty(dstName))
throw JmsHelper.buildJmsDiggerException("Invalid destination name parameter");
assertInitialization();
try {
if(isQueue) {
Queue q = session.createQueue(dstName);
messageProducer.send(q, message);
return q;
} else {
Topic t = session.createTopic(dstName);
messageProducer.send(t, message);
return t;
}
} catch (JMSException ex) {
LOG.info("Destination " + dstName + " could not be created", ex);
throw JmsHelper.buildJmsDiggerException("Destination " + dstName + " could not be created", ex);
}
}
/**
* Creates a Queue on ActiveMQ broker
* @param queueName - Name of the queue
* @return - Queue object
* @throws JmsDiggerException
*/
public Queue createQueue(String queueName) throws JmsDiggerException {
return (Queue) createDestination(queueName, true);
}
/**
* Creates a Topic on ActiveMQ broker
* @param topicName - Name of the topic
* @return - Topic object
* @throws JmsDiggerException
*/
public Topic createTopic(String topicName) throws JmsDiggerException {
return (Topic) createDestination(topicName, false);
}
public void close() {
try {
messageProducer.close();
messageConsumer.close();
session.close();
connection.close();
} catch (JMSException e) {
LOG.info("AMQOps done failed");
}
}
}