/*
* 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);
}
}
}