package com.mcafee; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.math.BigInteger; import java.util.Enumeration; import java.util.Random; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.MapMessage; import javax.jms.Session; //import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import org.apache.commons.codec.binary.Hex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is a helper class that offers various functionalities:<br/> * 1. Creates random strings with <b> getRandomString </b> method <br/> * 2. Obtain a destination object with <b>getDestination</b> <br/> * 3. Converts byte array to hex String with <b> byteArrayToHexString </b><br/> * 4. Converts a MapMessage to string with <b>mapMessageToString</b> method<br/> * 5. Creates a JMS connection and returns the connection object with <b>createConnection</b> method<br/> * 6. Breaks a string to character array with <b>stringToCharArrayString</b><br/> * 7. Build JmsDiggerException with two overloaded methods with name <b>buildJmsDiggerException</b> <br/> * 8. Creates a connection factory from initial context and connection factory name with <b>getConnectionFactory</b> method<br/> * 9. Creates ActiveMQ's initial context with <b>getActiveMQInitialContext</b> method <br/> * @author Gursev Singh Kalra @ McAfee, Inc. * */ public class JmsHelper { private static final Logger LOG = LoggerFactory.getLogger(JmsHelper.class); private static Random random = new Random(); /** * Generates and returns a random string * @return String */ public static String getRandomString() { return new BigInteger(64, random).toString(16); } /** * Converts exception's stack trace to a string for easy printing and consumption * @param ex * @return */ public static String exceptionStacktraceToString(Exception ex) { StringWriter sWriter = new StringWriter(); PrintWriter pWriter = new PrintWriter(sWriter); ex.printStackTrace(pWriter); String result = sWriter.toString(); pWriter.close(); try { sWriter.close(); } catch (IOException e) { // Eating it up } return result; } /** * Obtains a Destination object. Any generated exception is added to the cause. * @param ctx - InitialContext * @param destName - Destination Name * @return - Destination Object * @throws JmsDiggerException */ public static Destination getDestination(InitialContext ctx, String destName) throws JmsDiggerException { LOG.debug("Entering getDestination method"); Destination dest = null; if(ctx == null || isStringNullOrEmpty(destName)) { LOG.info("Either InitialContext or Destination Name is null"); throw new JmsDiggerException("Either InitialContext or Destination Name is null"); } try { dest = (Destination)(ctx.lookup(destName)); } catch(ClassCastException ex) { LOG.info("The returned object for name " + destName + " was not of type Destination", ex); throw buildJmsDiggerException("The returned object for name " + destName + " was not of type Destination", ex); } catch(NamingException ne) { LOG.info("No destination found with name " + destName, ne); throw buildJmsDiggerException("No destination found with name " + destName, ne); } LOG.debug("Leaving getDestination method"); return dest; } /** * Convert a byte array to hex string * Converts a byte array to string representation of hex digits * Example conversion -> { 0x12, 0x23, 0x32, 0xA5 } * @param b * @return */ public static String byteArrayToHexString(byte[] b) { StringBuilder sb = new StringBuilder(); if (b.length == 0) return "{ }"; String str = Hex.encodeHexString(b); String[] twoCharArray = str.split("(?<=\\G.{2})"); sb.append("{ "); for (String s : twoCharArray) sb.append("0x" + s + ", "); sb.deleteCharAt(sb.length() - 2); sb.append("}"); return sb.toString(); } /** * Convers a map message to a string with a custom header * @param msg - The MapMessage * @return string representation of the message * @throws JmsDiggerException */ public static String mapMessageToString(MapMessage msg) throws JmsDiggerException { return mapMessageToString(msg, null); } /** * Convers a map message to a string with a custom header * @param msg - The MapMessage * @param customHdr - The header to be used for separating message * @return string representation of the message * @throws JmsDiggerException */ public static String mapMessageToString(MapMessage msg, String customHdr) throws JmsDiggerException { Enumeration e = null; StringBuilder sb = new StringBuilder(); String name; Object value; try { e = ((MapMessage) msg).getMapNames(); if (e.hasMoreElements()) { if(customHdr != null) sb.append(customHdr + "\n"); while (e.hasMoreElements()) { name = e.nextElement().toString(); sb.append("\t" + name + " : "); value = msg.getObject(name); if (value instanceof byte[]) sb.append(JmsHelper.byteArrayToHexString((byte[]) value) + "\n"); else sb.append(value + "\n"); } } } catch(JMSException ex) { throw buildJmsDiggerException("An exception occured while creating String representation of MapMessage"); } return sb.toString(); } /** * This method returns a connection or null if connection could not be generated. * @param ctx - Initial Context * @param cfName - ConnectionFactory name * @param loginInfo - JmsLoginInfo object with username and password * @param clientId - Client ID to be used * @return * @throws JmsDiggerException */ public static Connection createConnection(InitialContext ctx, String cfName, JmsLoginInfo loginInfo, String clientId) throws JmsDiggerException { LOG.debug("Entering getConnection method"); Connection conn = null; ConnectionFactory connFact = getConnectionFactory(ctx, cfName); try { if(loginInfo == null) conn = connFact.createConnection(); else conn = connFact.createConnection(loginInfo.getUsername(), loginInfo.getPassword()); //conn.setClientID(getRandomString()); if(clientId != null) conn.setClientID(clientId); // A new session is created as ActiveMQ does not initiate a new connection unless session creation is attempted. // The connection is valid and usable only if it allows session creation. Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); //Close the session if it gets created as it is not needed. if(sess != null) sess.close(); } catch(JMSException e) { LOG.info("Connection to the broker failed", e); throw buildJmsDiggerException("Connection to the broker failed", e); } LOG.debug("Leaving getConnection method"); return conn; } /** * Convert a string to its character representation * @param str * @return */ public static String stringToCharArrayString(String str) { StringBuilder sb = new StringBuilder(); if(str == null) throw new IllegalArgumentException("String cannot be null"); if(isStringNullOrEmpty(str)) return ""; sb.append("{"); char[] c = str.toCharArray(); for(char chr : c) { sb.append("\""+chr+"\""); sb.append("=> "); sb.append((int)chr); sb.append(", "); } sb.append("}"); return sb.toString(); } public static Connection createConnection(InitialContext ctx, String cfName, JmsLoginInfo loginInfo) throws JmsDiggerException { return createConnection(ctx, cfName, loginInfo, null); } /** * This belongs to Unit tests. TODO move it to unit test code * @return * @throws JmsDiggerException */ public static InitialContext getActiveMQInitialContextForUnitTest() throws JmsDiggerException { JmsInitialContextFactory contextFactory = new JmsInitialContextFactory("org.apache.activemq.jndi.ActiveMQInitialContextFactory", "vm://localhost?broker.persistent=false"); contextFactory.addConnectionFactory("ConnectionFactory"); contextFactory.addQueue("submissions", "jms.submissions"); InitialContext ctx = contextFactory.getInitialContext(); return ctx; } /** * Returns InitialContext for ActiveMQ * @return InitialContext * @throws JmsDiggerException */ public static InitialContext getActiveMQInitialContext() throws JmsDiggerException { JmsInitialContextFactory contextFactory = new JmsInitialContextFactory("org.apache.activemq.jndi.ActiveMQInitialContextFactory", "tcp://192.168.127.130:61616"); contextFactory.addConnectionFactory("ConnectionFactory"); contextFactory.addQueue("submissions", "jms.submissions"); InitialContext ctx = contextFactory.getInitialContext(); return ctx; } /** * This method returns connection created with anonymous authentication. * @param ctx - Initial Context * @param cfName - ConnectionFactory name * @param result - JmsResposne object, primarily a DAO to carry back details on failure. * @return * @throws JmsDiggerException */ public static Connection createConnection(InitialContext ctx, String cfName) throws JmsDiggerException { return createConnection(ctx, cfName, null); } /** * Build a JMSDiggerException when another exception has occured and we want to wrap * that exception with JMSDiggerException * @param msg - The message * @param cause - Cause exception * @return */ public static JmsDiggerException buildJmsDiggerException(String msg, Throwable cause) { JmsDiggerException je = new JmsDiggerException(msg); je.initCause(cause); return je; } /** * Build a JMSDiggerException when another exception has occured and we want to wrap * that exception with JMSDiggerException * @param msg - The message * @return */ public static JmsDiggerException buildJmsDiggerException(String msg) { JmsDiggerException je = new JmsDiggerException(msg); return je; } /** * This method returns a connection factory from initial context and connection factory name * @param ctx - Initial Context * @param cfName - Connection factory name * @return - ConnectionFactory * @throws JmsDiggerException */ public static ConnectionFactory getConnectionFactory(InitialContext ctx, String cfName) throws JmsDiggerException { LOG.debug("Entering getConnectionFactory method"); if(ctx == null) { throw new JmsDiggerException("InitialContext parameter was null"); } //Doing this because of the way ActiveMQ handles local resolution of Connection Factory names if(isStringNullOrEmpty(cfName)) { cfName = "ConnectionFactory"; } ConnectionFactory cf = null; try { cf = (ConnectionFactory)(ctx.lookup(cfName)); } catch(ClassCastException ex) { LOG.info(cfName + " is not of type ConnectionFactory", ex); //The returned object can be of type Destination throw buildJmsDiggerException(cfName + " is not of type ConnectionFactory", ex); } catch(NamingException ne) { LOG.info("No Connection Factory with name " + cfName + " identified", ne); throw buildJmsDiggerException("No Connection Factory with name " + cfName + " identified", ne); } LOG.debug("Leaving getConnectionFactory method"); return cf; } /** * String value is checked against null or with spaces * @param str * @return true or false */ public static boolean isStringNullOrEmpty(String str) { if(str == null || str.trim().equals("")) return true; return false; } }