/* * 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.impl; import hermes.Domain; import hermes.HermesException; import hermes.MessageFactory; import hermes.SystemProperties; import hermes.browser.HermesBrowser; import hermes.util.JMSUtils; import hermes.xml.Entry; import hermes.xml.MessageSet; import hermes.xml.ObjectFactory; import hermes.xml.Property; import hermes.xml.XMLBytesMessage; import hermes.xml.XMLMapMessage; import hermes.xml.XMLMessage; import hermes.xml.XMLObjectMessage; import hermes.xml.XMLTextMessage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Serializable; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; 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.TextMessage; import javax.naming.NamingException; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.namespace.QName; import javax.xml.transform.stream.StreamSource; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.EncoderException; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.StringEscapeUtils; import org.apache.log4j.Logger; import org.apache.tools.ant.util.ReaderInputStream; /** * Generic XML helper methods that are non-JMS specific. The serialisation is * very sub-optimal but okay for dealing withe a few thousands of messages per * file. * * @author colincrist@hermesjms.com * @version $Id: DefaultXMLHelper.java,v 1.23 2006/01/14 12:59:12 colincrist Exp * $ */ public class DefaultXMLHelper implements XMLHelper { private static final Logger log = Logger.getLogger(DefaultXMLHelper.class); private static final int XML_TEXT_MESSAGE = 1; private static final int XML_BYTES_MESSGAE = 2; private static final int XML_OBJECT_MESSAGE = 3; private static final int XML_MAP_MESSAGE = 4; private static final String BASE64_CODEC = "Base64"; private final ThreadLocal<Base64> base64EncoderTL = new ThreadLocal<Base64>() { @Override protected Base64 initialValue() { return new Base64(); } }; private final ThreadLocal<JAXBContext> contextTL = new ThreadLocal<JAXBContext>() { @Override protected JAXBContext initialValue() { try { return JAXBContext.newInstance("hermes.xml"); } catch (JAXBException e) { log.error(e.getMessage(), e); return null; } } }; public DefaultXMLHelper() { } private final ObjectFactory factory = new ObjectFactory(); public boolean isBase64EncodeTextMessages() { if (HermesBrowser.getBrowser() != null) { try { return HermesBrowser.getBrowser().getConfig().isBase64EncodeMessages(); } catch (HermesException ex) { log.error(ex.getMessage(), ex); return false; } } else { return Boolean.parseBoolean(System.getProperty(SystemProperties.BASE64_ENCODE_TEXT_MESSAGE, "false")); } } public ObjectFactory getFactory() { return factory; } public MessageSet readContent(InputStream istream) throws Exception { JAXBContext jc = contextTL.get(); Unmarshaller u = jc.createUnmarshaller(); JAXBElement<MessageSet> node = u.unmarshal(new StreamSource(istream), MessageSet.class); return node.getValue(); } public MessageSet readContent(Reader reader) throws Exception { JAXBContext jc = contextTL.get(); Unmarshaller u = jc.createUnmarshaller(); JAXBElement<MessageSet> node = u.unmarshal(new StreamSource(new ReaderInputStream(reader)), MessageSet.class); return node.getValue(); } public void saveContent(MessageSet messages, OutputStream ostream) throws Exception { JAXBContext jc = JAXBContext.newInstance("hermes.xml"); Marshaller m = jc.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); m.marshal(new JAXBElement<MessageSet>(new QName("", "content"), MessageSet.class, messages), ostream); ostream.flush(); } public void saveContent(MessageSet messages, Writer writer) throws Exception { JAXBContext jc = JAXBContext.newInstance("hermes.xml"); Marshaller m = jc.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); m.marshal(new JAXBElement<MessageSet>(new QName("", "content"), MessageSet.class, messages), writer); writer.flush(); } @Override public void toXML(Message message, OutputStream ostream) throws JMSException, IOException { final Collection<Message> c = new HashSet<Message>(); c.add(message); toXML(c, ostream); } @Override public String toXML(Message message) throws JMSException { final Collection<Message> c = new HashSet<Message>(); c.add(message); return toXML(c); } @Override public Collection fromXML(MessageFactory hermes, InputStream istream) throws JMSException { try { return fromMessageSet(hermes, readContent(istream)); } catch (Exception ex) { log.error(ex.getMessage(), ex); throw new HermesException(ex); } } @Override public Collection fromXML(MessageFactory hermes, String document) throws JMSException { try { return fromMessageSet(hermes, readContent(new StringReader(document))); } catch (Exception ex) { log.error(ex.getMessage(), ex); throw new HermesException(ex); } } @Override public void toXML(Collection messages, OutputStream ostream) throws JMSException, IOException { try { MessageSet messageSet = toMessageSet(messages); saveContent(messageSet, ostream); } catch (Exception ex) { log.error(ex.getMessage(), ex); throw new HermesException(ex); } } @Override public String toXML(Collection messages) throws JMSException { try { StringWriter writer = new StringWriter(); MessageSet messageSet = toMessageSet(messages); saveContent(messageSet, writer); return writer.getBuffer().toString(); } catch (Exception ex) { log.error(ex.getMessage(), ex); throw new HermesException(ex); } } public Collection fromMessageSet(MessageFactory hermes, MessageSet messageSet) throws JMSException, IOException, ClassNotFoundException, DecoderException { Collection rval = new ArrayList(); for (Iterator iter = messageSet.getEntry().iterator(); iter.hasNext();) { Entry entry = (Entry) iter.next(); Message jmsMessage = null; switch (entry.getType()) { case XML_TEXT_MESSAGE: jmsMessage = createMessage(hermes, entry.getTextMessage()); break; case XML_MAP_MESSAGE: jmsMessage = createMessage(hermes, entry.getMapMessage()); break; case XML_BYTES_MESSGAE: jmsMessage = createMessage(hermes, entry.getBytesMessage()); break; case XML_OBJECT_MESSAGE: jmsMessage = createMessage(hermes, entry.getObjectMessage()); break; } if (jmsMessage != null) { rval.add(jmsMessage); } } return rval; } public MessageSet toMessageSet(Collection messages) throws JMSException { try { MessageSet messageSet = factory.createMessageSet(); for (Iterator iter = messages.iterator(); iter.hasNext();) { Message jmsMessage = (Message) iter.next(); Entry entry = factory.createEntry(); XMLMessage xmlMessage = createXMLMessage(factory, jmsMessage); if (xmlMessage instanceof XMLTextMessage) { entry.setType(XML_TEXT_MESSAGE); entry.setTextMessage((XMLTextMessage) xmlMessage); } else if (xmlMessage instanceof XMLMapMessage) { entry.setType(XML_MAP_MESSAGE); entry.setMapMessage((XMLMapMessage) xmlMessage); } else if (xmlMessage instanceof XMLObjectMessage) { entry.setType(XML_OBJECT_MESSAGE); entry.setObjectMessage((XMLObjectMessage) xmlMessage); } else if (xmlMessage instanceof XMLBytesMessage) { entry.setType(XML_BYTES_MESSGAE); entry.setBytesMessage((XMLBytesMessage) xmlMessage); } messageSet.getEntry().add(entry); } return messageSet; } catch (Exception ex) { log.error(ex.getMessage(), ex); throw new HermesException(ex); } } public Message createMessage(MessageFactory hermes, XMLMessage message) throws JMSException, IOException, ClassNotFoundException, DecoderException { try { Message rval = hermes.createMessage(); if (message instanceof XMLTextMessage) { rval = hermes.createTextMessage(); XMLTextMessage textMessage = (XMLTextMessage) message; TextMessage textRval = (TextMessage) rval; if (BASE64_CODEC.equals(textMessage.getCodec())) { byte[] bytes = base64EncoderTL.get().decode(textMessage.getText().getBytes()); textRval.setText(new String(bytes, "ASCII")); } else { textRval.setText(textMessage.getText()); } } else if (message instanceof XMLMapMessage) { rval = hermes.createMapMessage(); XMLMapMessage mapMessage = (XMLMapMessage) message; MapMessage mapRval = (MapMessage) rval; for (Iterator iter = mapMessage.getBodyProperty().iterator(); iter.hasNext();) { final Property property = (Property) iter.next(); if (property.getValue() == null) { mapRval.setObject(property.getName(), null); } else if (property.getType().equals(String.class.getName())) { mapRval.setString(property.getName(), property.getValue()); } else if (property.getType().equals(Long.class.getName())) { mapRval.setLong(property.getName(), Long.parseLong(property.getValue())); } else if (property.getType().equals(Double.class.getName())) { mapRval.setDouble(property.getName(), Double.parseDouble(property.getValue())); } else if (property.getType().equals(Boolean.class.getName())) { mapRval.setBoolean(property.getName(), Boolean.getBoolean(property.getValue())); } else if (property.getType().equals(Character.class.getName())) { mapRval.setChar(property.getName(), property.getValue().charAt(0)); } else if (property.getType().equals(Short.class.getName())) { mapRval.setShort(property.getName(), Short.parseShort(property.getValue())); } else if (property.getType().equals(Integer.class.getName())) { mapRval.setInt(property.getName(), Integer.parseInt(property.getValue())); } } } else if (message instanceof XMLBytesMessage) { rval = hermes.createBytesMessage(); XMLBytesMessage bytesMessage = (XMLBytesMessage) message; BytesMessage bytesRval = (BytesMessage) rval; bytesRval.writeBytes(base64EncoderTL.get().decode(bytesMessage.getBytes().getBytes())); } else if (message instanceof XMLObjectMessage) { rval = hermes.createObjectMessage(); XMLObjectMessage objectMessage = (XMLObjectMessage) message; ObjectMessage objectRval = (ObjectMessage) rval; ByteArrayInputStream bistream = new ByteArrayInputStream(base64EncoderTL.get().decode(objectMessage.getObject().getBytes())); ObjectInputStream oistream = new ObjectInputStream(bistream); objectRval.setObject((Serializable) oistream.readObject()); } // // JMS Header properties try { rval.setJMSDeliveryMode(message.getJMSDeliveryMode()); } catch (JMSException ex) { log.error("unable to set JMSDeliveryMode to " + message.getJMSDeliveryMode() + ": " + ex.getMessage()); } try { rval.setJMSMessageID(message.getJMSMessageID()); } catch (JMSException ex) { log.error("unable to set JMSMessageID: " + ex.getMessage(), ex); } try { if (message.getJMSExpiration() != null) { rval.setJMSExpiration(message.getJMSExpiration()); } } catch (JMSException ex) { log.error("unable to set JMSExpiration: " + ex.getMessage(), ex); } try { if (message.getJMSPriority() != null) { rval.setJMSPriority(message.getJMSPriority()); } } catch (JMSException ex) { log.error("unable to set JMSPriority: " + ex.getMessage(), ex); } try { if (message.getJMSTimestamp() != null) { rval.setJMSTimestamp(message.getJMSTimestamp()); } } catch (JMSException ex) { log.error("unable to set JMSTimestamp:" + ex.getMessage(), ex); } if (message.getJMSCorrelationID() != null) { rval.setJMSCorrelationID(message.getJMSCorrelationID()); } if (message.getJMSReplyTo() != null && !message.getJMSReplyTo().equals("null")) { rval.setJMSReplyTo(hermes.getDestination(message.getJMSReplyTo(), Domain.getDomain(message.getJMSReplyToDomain()))); } if (message.getJMSType() != null) { rval.setJMSType(message.getJMSType()); } if (message.getJMSDestination() != null) { if (message.isFromQueue()) { rval.setJMSDestination(hermes.getDestination(message.getJMSDestination(), Domain.QUEUE)); } else { rval.setJMSDestination(hermes.getDestination(message.getJMSDestination(), Domain.TOPIC)); } } for (Iterator iter = message.getHeaderProperty().iterator(); iter.hasNext();) { Property property = (Property) iter.next(); if (property.getValue() == null) { rval.setObjectProperty(property.getName(), null); } else if (property.getType().equals(String.class.getName())) { rval.setStringProperty(property.getName(), StringEscapeUtils.unescapeXml(property.getValue())); } else if (property.getType().equals(Long.class.getName())) { rval.setLongProperty(property.getName(), Long.parseLong(property.getValue())); } else if (property.getType().equals(Double.class.getName())) { rval.setDoubleProperty(property.getName(), Double.parseDouble(property.getValue())); } else if (property.getType().equals(Boolean.class.getName())) { rval.setBooleanProperty(property.getName(), Boolean.parseBoolean(property.getValue())); } else if (property.getType().equals(Short.class.getName())) { rval.setShortProperty(property.getName(), Short.parseShort(property.getValue())); } else if (property.getType().equals(Integer.class.getName())) { rval.setIntProperty(property.getName(), Integer.parseInt(property.getValue())); } } return rval; } catch (NamingException e) { throw new HermesException(e); } } public XMLMessage createXMLMessage(ObjectFactory factory, Message message) throws JMSException, IOException, EncoderException { try { XMLMessage rval = factory.createXMLMessage(); if (message instanceof TextMessage) { rval = factory.createXMLTextMessage(); XMLTextMessage textRval = (XMLTextMessage) rval; TextMessage textMessage = (TextMessage) message; if (isBase64EncodeTextMessages()) { byte[] bytes = base64EncoderTL.get().encode(textMessage.getText().getBytes()); textRval.setText(new String(bytes, "ASCII")); textRval.setCodec(BASE64_CODEC); } else { textRval.setText(textMessage.getText()); } } else if (message instanceof MapMessage) { rval = factory.createXMLMapMessage(); XMLMapMessage mapRval = (XMLMapMessage) rval; MapMessage mapMessage = (MapMessage) message; for (Enumeration iter = mapMessage.getMapNames(); iter.hasMoreElements();) { String propertyName = (String) iter.nextElement(); Object propertyValue = mapMessage.getObject(propertyName); Property xmlProperty = factory.createProperty(); if (propertyValue != null) { xmlProperty.setValue(propertyValue.toString()); xmlProperty.setType(propertyValue.getClass().getName()); } xmlProperty.setName(propertyName); mapRval.getBodyProperty().add(xmlProperty); } } else if (message instanceof BytesMessage) { rval = factory.createXMLBytesMessage(); XMLBytesMessage bytesRval = (XMLBytesMessage) rval; BytesMessage bytesMessage = (BytesMessage) message; ByteArrayOutputStream bosream = new ByteArrayOutputStream(); bytesMessage.reset(); try { for (;;) { bosream.write(bytesMessage.readByte()); } } catch (MessageEOFException ex) { // NOP } bytesRval.setBytes(new String(base64EncoderTL.get().encode(bosream.toByteArray()))); } else if (message instanceof ObjectMessage) { rval = factory.createXMLObjectMessage(); XMLObjectMessage objectRval = (XMLObjectMessage) rval; ObjectMessage objectMessage = (ObjectMessage) message; ByteArrayOutputStream bostream = new ByteArrayOutputStream(); ObjectOutputStream oostream = new ObjectOutputStream(bostream); oostream.writeObject(objectMessage.getObject()); oostream.flush(); byte b[] = base64EncoderTL.get().encode(bostream.toByteArray()); String s = new String(b, "ASCII"); objectRval.setObject(s); } if (message.getJMSReplyTo() != null) { rval.setJMSReplyTo(JMSUtils.getDestinationName(message.getJMSReplyTo())); rval.setJMSReplyToDomain(Domain.getDomain(message.getJMSReplyTo()).getId()); } // try/catch each individually as we sometime find some JMS // providers // can barf try { rval.setJMSDeliveryMode(message.getJMSDeliveryMode()); } catch (JMSException ex) { log.error(ex.getMessage(), ex); } try { rval.setJMSExpiration(message.getJMSExpiration()); } catch (JMSException ex) { log.error(ex.getMessage(), ex); } try { rval.setJMSMessageID(message.getJMSMessageID()); } catch (JMSException ex) { log.error(ex.getMessage(), ex); } try { rval.setJMSPriority(message.getJMSPriority()); } catch (JMSException ex) { log.error(ex.getMessage(), ex); } try { rval.setJMSRedelivered(message.getJMSRedelivered()); } catch (JMSException ex) { log.error(ex.getMessage(), ex); } catch (IllegalStateException ex) { // http://hermesjms.com/forum/viewtopic.php?f=4&t=346 log.error(ex.getMessage(), ex); } try { rval.setJMSTimestamp(message.getJMSTimestamp()); } catch (JMSException ex) { log.error(ex.getMessage(), ex); } try { rval.setJMSType(message.getJMSType()); } catch (JMSException ex) { log.error(ex.getMessage(), ex); } try { rval.setJMSCorrelationID(message.getJMSCorrelationID()); } catch (JMSException ex) { log.error(ex.getMessage(), ex); } try { if (message.getJMSDestination() != null) { rval.setJMSDestination(JMSUtils.getDestinationName(message.getJMSDestination())); rval.setFromQueue(JMSUtils.isQueue(message.getJMSDestination())); } } catch (JMSException ex) { log.error(ex.getMessage(), ex); } for (final Enumeration iter = message.getPropertyNames(); iter.hasMoreElements();) { String propertyName = (String) iter.nextElement(); if (!propertyName.startsWith("JMS")) { Object propertyValue = message.getObjectProperty(propertyName); Property property = factory.createProperty(); property.setName(propertyName); if (propertyValue != null) { property.setValue(StringEscapeUtils.escapeXml(propertyValue.toString())); property.setType(propertyValue.getClass().getName()); } rval.getHeaderProperty().add(property); } } return rval; } catch (Exception ex) { throw new HermesException(ex); } } }