package com.mcafee; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.Date; import javax.jms.BytesMessage; import javax.jms.JMSException; import javax.jms.MapMessage; import javax.jms.Message; import javax.jms.MessageEOFException; import javax.jms.ObjectMessage; import javax.jms.StreamMessage; import javax.jms.TextMessage; import org.apache.commons.codec.binary.Hex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.thoughtworks.xstream.XStream; /** * Objects of this class write Message and its data to flat files. It determines * the type of message and then writes data as per different headers. Types of * messages supported are:<br/> * 1. Message <br/> * 2. TextMessage <br/> * 3. ObjectMessage <br/> * 4. BytesMessage<br/> * 5. Stream Message <br/> * 6. MapMessage<br/> * * @author Gursev Singh Kalra @ McAfee, Inc. * */ public class JmsWriter { private static final Logger LOG = LoggerFactory.getLogger(JmsWriter.class); private static String fileSeparator; private String targetDir; private boolean targetDirExists; private static String msgSeparator; // used as a separator between messages // being dumped private String msgDumpFilePath; private PrintWriter msgDumpFileHandle; private XStream jmsXstream; private int textMsgsWritten = 0; private int bytesMsgsWritten = 0; private int objMsgsWritten = 0; private int streamMsgsWritten = 0; private int mapMsgsWritten = 0; private int msgsWritten = 0; private String identifier; static { msgSeparator = "\n~-~-~-~-~-~-~-~-~-~~-~-~-~-~-~-~-~-~-~" + (new Date()).getTime() + "\n"; } public JmsWriter(String targetDir, String identifier) throws JmsDiggerException { this.targetDir = targetDir; targetDirExists = false; fileSeparator = System.getProperty("file.separator"); try { (new File(targetDir)).mkdirs(); } catch (Exception ex) { LOG.error("Target directory could not be created", ex); throw JmsHelper.buildJmsDiggerException("Target directory could not be created"); } jmsXstream = new XStream(); this.identifier = identifier; } public void init() throws FileNotFoundException, IOException { LOG.debug("Entering init"); initFileHandles(); LOG.debug("Leaving init"); } private void initFileHandles() throws FileNotFoundException, IOException { // time in seconds since epoch LOG.debug("Entering initFileHandles"); long dt = ((new Date()).getTime()); msgDumpFilePath = targetDir + fileSeparator + identifier + "-MessageDump-" + dt + ".txt"; try { msgDumpFileHandle = new PrintWriter(new FileOutputStream(msgDumpFilePath), true); msgDumpFileHandle.println("#########################################################################\nJMS Destination message dump generated by JMSDigger\n#########################################################################\n"); } catch (FileNotFoundException ex) { LOG.error("Output file handle could not be creatd", ex); throw ex; } LOG.debug("Leaving initFileHandles"); } private String byteArrayToHexString(byte[] b) { LOG.debug("Entering byteArrayToHexString"); // Converts a byte array to string representation of hex digits // { 0x12, 0x23, 0x32, 0xA5 } 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("}"); LOG.debug("Leaving byteArrayToHexString"); return sb.toString(); } private String getBytesMessageContentAsString(BytesMessage msg) throws JmsDiggerException { LOG.debug("Entering writeBytesMessage"); int len = -1; byte[] body; try { len = (int) msg.getBodyLength(); if (len == 0) { return "{ }"; } body = new byte[len]; msg.readBytes(body); } catch (JMSException ex) { LOG.info("An error occured while retreving content from ByteMessage", ex); throw JmsHelper.buildJmsDiggerException("An error occured while retreving content from ByteMessage", ex); } LOG.debug("Leaving writeBytesMessage"); return byteArrayToHexString(body); } private String getStreamMessageContentAsString(StreamMessage msg) throws JmsDiggerException { LOG.debug("Entering writeStreamMessage"); StringBuilder sb = new StringBuilder(); sb.append("{ "); Object o; try { while (true) { o = msg.readObject(); if (o instanceof byte[]) sb.append(byteArrayToHexString((byte[]) o)); else sb.append(o); sb.append(", "); } } catch (MessageEOFException ex) { sb.deleteCharAt(sb.length() - 2); // Do nothing. End of StreamMessage content has reached. } catch (JMSException ex) { LOG.info("An error occured while reading Object from StreamMessage", ex); throw JmsHelper.buildJmsDiggerException("An error occured while reading Object from StreamMessage", ex); } sb.append("}"); LOG.debug("Leaving writeStreamMessage"); return sb.toString(); } private String getMapMessageContentAsString(MapMessage msg) throws JmsDiggerException { LOG.debug("Entering writeMapMessage"); try { return(JmsHelper.mapMessageToString(msg, "[+] MapMessage name-value pairs : ")); } catch (JMSException ex) { LOG.info("An error occured while reading MapMessage", ex); throw JmsHelper.buildJmsDiggerException("An error occured while reading MapMessage", ex); } } /** * Analyze the message type against all the JMS message types supported and * write metadata as per the message type * * @param msg * @param result * @throws JmsDiggerException * @throws IOException */ public void writeMsg(Message msg) throws JmsDiggerException { LOG.debug("Entering writeMsg"); if (msg == null) throw new IllegalArgumentException("Message parameter is null"); JmsMsgType type = null; StringBuilder messageContent = new StringBuilder(); try { if (msg instanceof BytesMessage) { LOG.debug("Message Type : BytesMessage"); type = JmsMsgType.BYTES; messageContent.append("[+] Message Type : BytesMessage"); messageContent.append("\n"); messageContent.append("[+] Message Bytes as an Array: \n"); messageContent.append(getBytesMessageContentAsString((BytesMessage) msg)); messageContent.append("\n"); bytesMsgsWritten++; } if (type == null && msg instanceof TextMessage) { LOG.debug("Message Type : TextMessage"); type = JmsMsgType.TEXT; messageContent.append("[+] Message Type : TextMessage"); messageContent.append("\n"); messageContent.append("[+] Extracted Text\n"); messageContent.append(((TextMessage)msg).getText()); messageContent.append("\n"); textMsgsWritten++; } if (type == null && msg instanceof StreamMessage) { LOG.debug("Message Type : StreamMessage"); type = JmsMsgType.STREAM; messageContent.append("[+] Message Type : StreamMessage"); messageContent.append("\n"); messageContent.append("[+] Extracted Stream\n"); messageContent.append(getStreamMessageContentAsString((StreamMessage) msg)); messageContent.append("\n"); streamMsgsWritten++; } if (type == null && msg instanceof MapMessage) { LOG.debug("Message Type : MapMessage"); type = JmsMsgType.MAP; messageContent.append("[+] Message Type : MapMessage"); messageContent.append("\n"); messageContent.append(getMapMessageContentAsString((MapMessage) msg)); messageContent.append("\n"); mapMsgsWritten++; } if (type == null && msg instanceof ObjectMessage) { LOG.debug("Message Type : ObjectMessage"); type = JmsMsgType.OBJECT; messageContent.append("[+] Message Type : ObjectMessage"); messageContent.append("\n"); objMsgsWritten++; } if (type == null && msg instanceof Message) { LOG.debug("Message Type : Message"); type = JmsMsgType.MESSAGE; messageContent.append("[+] Message Type : Message"); messageContent.append("\n"); } messageContent.append("[+] Message Properties in XML (including binary dump in content/data element)\n"); messageContent.append(jmsXstream.toXML(msg)); messageContent.append("\n"); messageContent.append(msgSeparator); msgDumpFileHandle.println(messageContent.toString()); msgsWritten++; } catch (JMSException ex) { LOG.info("A JMS operation failed", ex); throw JmsHelper.buildJmsDiggerException("A JMS operation failed", ex); } LOG.debug("Leaving writeMsg"); } private String getWriteCountBreakup() { StringBuilder sb = new StringBuilder(); sb.append("ObjectMessages written: " + objMsgsWritten + ", "); sb.append("BytesMessages written: " + bytesMsgsWritten + ", "); sb.append("StreamMessages written: " + streamMsgsWritten + ", "); sb.append("MapMessages written: " + mapMsgsWritten + ", "); sb.append("TextMessages written: " + textMsgsWritten); return sb.toString(); } public void close() throws IOException { LOG.debug("Entering close"); if (msgsWritten == 0) msgDumpFileHandle.println("No messages were written"); msgDumpFileHandle.flush(); msgDumpFileHandle.close(); LOG.debug(getWriteCountBreakup()); LOG.debug("Leaving close"); } }