/* * Copyright 2003,2004 Colin Crist * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package hermes.renderers; import hermes.browser.dialog.message.MapMessagePayloadPanel; import hermes.swing.MyTextArea; import hermes.util.MessageUtils; import java.awt.Font; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.PrintWriter; import java.io.Serializable; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.nio.charset.Charset; import java.util.Iterator; import java.util.Map; 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 javax.swing.JComponent; import javax.swing.JOptionPane; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.collections.map.LRUMap; import org.apache.log4j.Logger; /** * Tries to render the message in some simple way. * * @author colincrist@hermesjms.com * @version $Id: DefaultMessageRenderer.java,v 1.4 2004/07/30 17:25:13 * colincrist Exp $ */ public class DefaultMessageRenderer extends AbstractMessageRenderer { private static final Logger log = Logger.getLogger(DefaultMessageRenderer.class); private static final String BYTESISSTRING = "bytesIsString"; private static final String BYTESISOBJECT = "bytesIsObject"; private static final String BYTESENCODING = "bytesEncoding"; private static final String BYTESISOBJECTSIZE = "bytesIsObjectBufferSize"; private static final String TOSTRINGOBJECT = "toStringOnObjectMessage"; private static final String MESSAGE_CACHE = "messageCache"; private static final String BYTESISSTRING_INFO = "Treat a BytesMessage as a sequence of characters and convert to a String"; private static final String BYTESISOBJECT_INFO = "Treat a BytesMessage as a Serialized Java object"; private static final String BYTESISOBJECTSIZE_INFO = "Buffer size to use as temporary storage (ignored with JMS 1.1 providers as size is available on the message)"; private static final String TOSTRINGOBJECT_INFO = "Just call toString() on any Object in an ObjectMessage"; private static final String MESSAGE_CACHE_INFO = "The number of panels to cache - can speed up the user interface when switching between messags"; private static final String BYTESENCODING_INFO = "The encoding to use when treating a BytesMessage as a String"; private LRUMap panelCache; /** * Configuration bean for this renderer * * @author colincrist@hermesjms.com last changed by: $Author: colincrist $ * @version $Id: DefaultMessageRenderer.java,v 1.4 2004/07/30 17:25:13 * colincrist Exp $ */ public class MyConfig extends AbstractMessageRenderer.BasicConfig { private String name; private boolean bytesIsObject = false; private int bytesIsObjectBufferSize = 64 * 1024; private boolean toStringOnObjectMessage = false; private int messageCache = 100; private boolean bytesIsString = false; private String bytesEncoding = Charset.defaultCharset().name(); @Override public String toString() { return name + ".MyConfig: " + "bytesIsObject=" + bytesIsObject + ", bytesIsObjectBufferSize=" + bytesIsObjectBufferSize + ", toStringOnObjectMessage=" + toStringOnObjectMessage + ", messageCache=" + messageCache + ", bytesIsString=" + bytesIsString; } public int getMessageCache() { return messageCache; } public void setMessageCache(int messageCache) { this.messageCache = messageCache; } /** * @return Returns the bytesIsObject. */ public boolean isBytesIsObject() { return bytesIsObject; } /** * @param bytesIsObject * The bytesIsObject to set. */ public void setBytesIsObject(boolean bytesIsObject) { this.bytesIsObject = bytesIsObject; } /** * @return Returns the bytesIsObjectSize. */ public int getBytesIsObjectBufferSize() { return bytesIsObjectBufferSize; } /** * @param bytesIsObjectSize * The bytesIsObjectSize to set. */ public void setBytesIsObjectBufferSize(int bytesIsObjectBufferSize) { this.bytesIsObjectBufferSize = bytesIsObjectBufferSize; } /** * @return Returns the bytesEncoding. */ public String getBytesEncoding() { return bytesEncoding; } /** * @param bytesEncoding * The bytesEncoding to set. */ public void setBytesEncoding(String bytesEncoding) { this.bytesEncoding = bytesEncoding; } /** * @return Returns the name. */ @Override public String getName() { return name; } /** * @param name * The name to set. */ @Override public void setName(String name) { this.name = name; } @Override public String getPropertyDescription(String propertyName) { if (propertyName.equals(BYTESISOBJECT)) { return BYTESISOBJECT_INFO; } if (propertyName.equals(BYTESISOBJECTSIZE)) { return BYTESISOBJECTSIZE_INFO; } if (propertyName.equals(TOSTRINGOBJECT)) { return TOSTRINGOBJECT_INFO; } if (propertyName.equals(MESSAGE_CACHE)) { return MESSAGE_CACHE_INFO; } if (propertyName.equals(BYTESISSTRING)) { return BYTESISSTRING_INFO; } if (propertyName.equals(BYTESENCODING)) { return BYTESENCODING_INFO; } return propertyName; } /** * @return Returns the toStringOnObjectMessage. */ public boolean isToStringOnObjectMessage() { return toStringOnObjectMessage; } /** * @param toStringOnObjectMessage * The toStringOnObjectMessage to set. */ public void setToStringOnObjectMessage(boolean toStringOnObjectMessage) { this.toStringOnObjectMessage = toStringOnObjectMessage; } public boolean isBytesIsString() { return bytesIsString; } public void setBytesIsString(boolean bytesIsString) { this.bytesIsString = bytesIsString; } } /** * DefaultMessageRenderer constructor comment. */ public DefaultMessageRenderer() { super(); } /** * Show the TextMessage in a JTextArea. * * @param textMessage * @return * @throws JMSException */ protected JComponent handleTextMessage(final TextMessage textMessage) throws JMSException { // // Show the text in a JTextArea, you can edit the message in place and // then drop it onto another queue/topic. final String text = textMessage.getText(); final JTextArea textPane = new JTextArea(); // final CharBuffer bytes = CharBuffer.wrap(text.subSequence(0, // text.length())) ; // final JTextArea textPane = new MyTextArea(new PlainDocument(new // MappedStringContent(bytes))) ; textPane.setEditable(false); textPane.setFont(Font.decode("Monospaced-PLAIN-12")); textPane.setLineWrap(true); textPane.setWrapStyleWord(true); textPane.append(text); textPane.getDocument().addDocumentListener(new DocumentListener() { public void doChange() { try { textMessage.setText(textPane.getText()); } catch (JMSException e) { JOptionPane.showMessageDialog(textPane, "Unable to update the TextMessage: " + e.getMessage(), "Error modifying message content", JOptionPane.ERROR_MESSAGE); try { textPane.setText(textMessage.getText()); } catch (JMSException e1) { log.error(e1.getMessage(), e1); } textPane.setEditable(false); textPane.getDocument().removeDocumentListener(this); } } @Override public void changedUpdate(DocumentEvent arg0) { doChange(); } @Override public void insertUpdate(DocumentEvent arg0) { doChange(); } @Override public void removeUpdate(DocumentEvent arg0) { doChange(); } }); textPane.setCaretPosition(0); return textPane; } /** * Depending on configuration, show the object via toString() or a list of * properties. * * @param objectMessage * @return * @throws JMSException * @throws IllegalAccessException * @throws InvocationTargetException * @throws NoSuchMethodException */ protected JComponent handleObjectMessage(final ObjectMessage objectMessage) throws JMSException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { // // Unserialize the object and display all its properties Serializable obj = objectMessage.getObject(); if (obj instanceof JComponent) { return (JComponent) obj; } else { JTextArea textPane = new JTextArea(); StringBuffer buffer = new StringBuffer(); MyConfig currentConfig = (MyConfig) getConfig(); if (obj == null) { buffer.append("Payload is null"); } else if (currentConfig.isToStringOnObjectMessage()) { buffer.append(obj.toString()); } else { buffer.append(obj.toString()).append("\n\nProperty list\n"); for (Iterator iter = PropertyUtils.describe(obj).entrySet().iterator(); iter.hasNext();) { Map.Entry entry = (Map.Entry) iter.next(); buffer.append(entry.getKey().toString()).append("=").append(entry.getValue()).append("\n"); } } textPane.setEditable(false); textPane.setText(buffer.toString()); return textPane; } } /** * Show the MapMessage as a tree. * * @param mapMessage * @return * @throws JMSException */ protected JComponent handleMapMessage(MapMessage mapMessage) throws JMSException { return new MapMessagePayloadPanel(mapMessage, false); } /** * Show a BytesMessage either as a java object or just a size. * * @param parent * * @param bytesMessage * @return * @throws JMSException * @throws IOException * @throws ClassNotFoundException */ protected JComponent handleBytesMessage(JScrollPane parent, BytesMessage bytesMessage) throws JMSException, IOException, ClassNotFoundException { final MyConfig currentConfig = (MyConfig) getConfig(); JTextArea textPane = new MyTextArea(); textPane.setEditable(false); textPane.setWrapStyleWord(true); textPane.setLineWrap(true); bytesMessage.reset(); if (currentConfig.isBytesIsObject()) { final byte[] bytes = MessageUtils.asBytes(bytesMessage); final ByteArrayInputStream bistream = new ByteArrayInputStream(bytes); final ObjectInputStream oistream = new ObjectInputStream(bistream); final Object o = oistream.readObject(); textPane.setText(o.toString()); } else if (currentConfig.isBytesIsString()) { try { String text = new String(MessageUtils.asBytes(bytesMessage), currentConfig.getBytesEncoding()); textPane.setText(text); return textPane; } catch (JMSException e) { textPane.setText(e.getMessage()); } } else { HexMessageRenderer renderer = new HexMessageRenderer(); textPane = (JTextArea) renderer.render(parent, bytesMessage); // Hack. } textPane.setCaretPosition(0); return textPane; } /** * List out all the properties in the stream message. * * @param streamMessage * @return * @throws JMSException */ protected JComponent handleStreamMessage(StreamMessage streamMessage) throws JMSException { JTextArea textPane = new JTextArea(); StringBuffer buffer = new StringBuffer(); textPane.setEditable(false); streamMessage.reset(); try { for (;;) { buffer.append(streamMessage.readObject().toString()).append("\n"); } } catch (MessageEOFException ex) { // NOP } return textPane; } /** * Render the message, delegates to typed methods. */ @Override public JComponent render(JScrollPane parent, final Message m) { try { if (getPanelMap().containsKey(m)) { return (JComponent) getPanelMap().get(m); } JComponent rval = null; if (m instanceof TextMessage) { rval = handleTextMessage((TextMessage) m); } else if (m instanceof javax.jms.ObjectMessage) { rval = handleObjectMessage((ObjectMessage) m); } else if (m instanceof javax.jms.MapMessage) { rval = handleMapMessage((MapMessage) m); } else if (m instanceof BytesMessage) { rval = handleBytesMessage(parent, (BytesMessage) m); } else if (m instanceof StreamMessage) { rval = handleStreamMessage((StreamMessage) m); } else { JTextArea textPane = new JTextArea(); textPane.setEditable(false); textPane.setText("Message has no Payload"); rval = textPane; } getPanelMap().put(m, rval); return rval; } catch (Throwable ex) { final JTextArea textPane = new JTextArea(); textPane.setEditable(false); final StringWriter string = new StringWriter(); final PrintWriter writer = new PrintWriter(string); ex.printStackTrace(writer); writer.flush(); textPane.setText("Unable to display message:\n" + string.toString()); log.error(ex.getMessage(), ex); return textPane; } } /* * (non-Javadoc) * * @see hermes.browser.MessageRenderer#createConfig() */ @Override public Config createConfig() { return new MyConfig(); } /* * (non-Javadoc) * * @see * hermes.browser.MessageRenderer#setConfig(hermes.browser.MessageRenderer * .Config) */ @Override public synchronized void setConfig(Config config) { final MyConfig currentConfig = (MyConfig) config; panelCache = new LRUMap(currentConfig.getMessageCache()); super.setConfig(config); } public synchronized LRUMap getPanelMap() { if (panelCache == null) { final MyConfig currentConfig = (MyConfig) getConfig(); panelCache = new LRUMap(currentConfig.getMessageCache()); } return panelCache; } /** * This is the catch all renderer so will always render a message. */ @Override public boolean canRender(Message message) { return true; } @Override public String getDisplayName() { return "Payload"; } }