package com.mcafee;
import java.util.ArrayList;
import javax.jms.Connection;
import javax.jms.InvalidDestinationException;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.Topic;
import javax.jms.TopicSubscriber;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class provides capability to perform various operations related to durable subscribers.
* Given a Topic name, credentials (optional), durable subscriber name, the class can be used to perform the following actions:
* 1. Create random and named durable subscribers
* 2. Erase durable subscribers
* 3. Create large number of durable subscribers with random names
* @author Gursev Singh Kalra @ McAfee, Inc.
*
*/
public class JmsDurableSubscriberManipulator {
private static final Logger LOG = LoggerFactory.getLogger(JmsDurableSubscriberManipulator.class);
private InitialContext ctx;
private Topic topic = null;
private JmsLoginInfo loginInfo;
private String cfName;
private String topicName;
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 boolean initialized = false;
private void assertInitialization() {
if(!initialized)
throw new IllegalArgumentException(this.getClass().getName().toString() + "'s object is not initialized");
}
public JmsDurableSubscriberManipulator(InitialContext ctx, String topicName, String connFactName) {
this(ctx, topicName, connFactName, null);
}
public JmsDurableSubscriberManipulator(InitialContext ctx, String topicName, String connFactName, JmsLoginInfo loginInfo) {
this.ctx = ctx;
this.loginInfo = loginInfo;
this.cfName = connFactName;
this.topicName = topicName;
}
public void init() throws JmsDiggerException {
init(null);
}
/**
* Creates a connection, session and starts the connection.
* clientId is set if provided. This is typically used for creating a durable subscriber.
* @param clientId
* @throws JmsDiggerException
*/
public void init(String clientId) throws JmsDiggerException {
LOG.debug("Entering init");
if(ctx == null || JmsHelper.isStringNullOrEmpty(cfName))
throw JmsHelper.buildJmsDiggerException("Null value for InitialContext or Connection Factory Name");
if(clientId == null)
connection = JmsHelper.createConnection(ctx, cfName, loginInfo );
else
connection = JmsHelper.createConnection(ctx, cfName, loginInfo, clientId);
try {
topic = (Topic) ctx.lookup(topicName);
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
connection.start();
} catch (JMSException ex) {
LOG.info("Error occured while initializing", ex);
throw JmsHelper.buildJmsDiggerException("Error occured while initializing AMQStats", ex);
} catch (NamingException ex) {
LOG.info(topicName + " not found ", ex);
throw JmsHelper.buildJmsDiggerException(topicName + " not found", ex);
}
initialized = true;
LOG.debug("Leaving init");
}
/**
* Creates "count" number of subscribers for a Topic. Typically to be used during DoS
* by file system exhaustion. The attack may work like this:
* 1. Connect to a Topic
* 2. Create durable subscribers
* 3. Continue to Send large PERSISTENT messages with longer expiry time to the topic.
*
* @param count - number of TopicSubscribers to create
* @return ArrayList<TopicSubscriber> - An arraylist of all TopicSubscribers created
* @throws JmsDiggerException
*/
public ArrayList<TopicSubscriber> createDurableSubscribers(int count) throws JmsDiggerException {
ArrayList<TopicSubscriber> aList = new ArrayList<TopicSubscriber>();
if(count <= 0)
throw new IllegalArgumentException("Count cannot be negative");
for(int i = 0; i < count; i++) {
TopicSubscriber ts = createDurableSubscriber(JmsHelper.getRandomString());
aList.add(ts);
}
return aList;
}
/**
* Creates a durable topic subscriber with a random name.
* @return TopicSubscriber
* @throws JmsDiggerException
*/
public TopicSubscriber createRandDurableSubscriber() throws JmsDiggerException {
return createDurableSubscriber(JmsHelper.getRandomString());
}
/**
* Creates a durable subscriber with a specified name.
* The durable subscriber created can be connected to later on to retrieve content.
* @param name - Name of the topic subscriber
* @return TopicSubscriber - The topic subscriber object
* @throws JmsDiggerException
*/
public TopicSubscriber createDurableSubscriber(String name) throws JmsDiggerException {
return createDurableSubscriber(name, "", false);
}
/**
* A Wrapper around createDurableSubscriber with three arguments.
* The noLocal is set to false for every call.
* @param durableSubscriberName
* @param messageSelector
* @return
* @throws JmsDiggerException
*/
public TopicSubscriber createDurableSubscriber(String durableSubscriberName, String messageSelector) throws JmsDiggerException {
return createDurableSubscriber(durableSubscriberName, messageSelector, false);
}
/**
* This call creates a Durable Subscriber for a topic. It is mandatory to initialize
* with init before making this call. If not, the call will fail.
* @param durableSubscriberName - Name of the durable subscriber
* @param messageSelector - The message selector. Can be null, blank or contain a valid value
* @param noLocal - Enable or disable messages generated from same host
* @return TopicSubscriber - Returns the topic subscriber created
* @throws JmsDiggerException - Throws for any errors
*/
public TopicSubscriber createDurableSubscriber(String durableSubscriberName, String messageSelector, boolean noLocal) throws JmsDiggerException {
LOG.debug("Entering createDurableSubscriber");
assertInitialization();
if(JmsHelper.isStringNullOrEmpty(durableSubscriberName))
throw new IllegalArgumentException("Durable subscriber name cannot be null or blank");
try {
return session.createDurableSubscriber(topic, durableSubscriberName, messageSelector, noLocal);
} catch (JMSException ex) {
LOG.info("An error has occured while creating durable subscriber", ex);
throw JmsHelper.buildJmsDiggerException("An error has occured while creating durable subscriber", ex);
}
}
/**
* Erases a durable subscriber.
* @param durableSuscriberName
* @throws JmsDiggerException
*/
public void eraseDurableSubscriber(String durableSuscriberName) throws JmsDiggerException {
LOG.debug("Entering eraseDurableSubscriber");
assertInitialization();
try {
session.unsubscribe(durableSuscriberName);
} catch (InvalidDestinationException ex) {
LOG.info("No durable subscriber exists with name " + durableSuscriberName);
throw JmsHelper.buildJmsDiggerException("No durable subscriber exists with name " + durableSuscriberName);
} catch (JMSException ex) {
LOG.info("Error occured while erasing a durable subscriber", ex);
throw JmsHelper.buildJmsDiggerException("Error occured while erasing a durable subscriber", ex);
}
LOG.debug("Leaving eraseDurableSubscriber");
}
/**
* Close the JMS session and the connection
*/
public void close() {
try {
session.close();
connection.close();
} catch (JMSException e) {
LOG.info("AMQOps done failed");
}
}
}