Open Source Repository

Home /spring/spring-jms-3.0.5 | Repository Home



org/springframework/jms/support/converter/MarshallingMessageConverter.java
/*
 * Copyright 2002-2010 the original author or authors.
 *
 * 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 org.springframework.jms.support.converter;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.Unmarshaller;
import org.springframework.oxm.XmlMappingException;
import org.springframework.util.Assert;

/**
 * Spring JMS {@link MessageConverter} that uses a {@link Marshaller} and {@link Unmarshaller}.
 * Marshals an object to a {@link BytesMessage}, or to a {@link TextMessage} if the
 {@link #setTargetType marshalTo} is set to {@link MessageType#TEXT}.
 * Unmarshals from a {@link TextMessage} or {@link BytesMessage} to an object.
 *
 @author Arjen Poutsma
 @author Juergen Hoeller
 @since 3.0
 @see org.springframework.jms.core.JmsTemplate#convertAndSend
 @see org.springframework.jms.core.JmsTemplate#receiveAndConvert
 */
public class MarshallingMessageConverter implements MessageConverter, InitializingBean {

  private Marshaller marshaller;

  private Unmarshaller unmarshaller;

  private MessageType targetType = MessageType.BYTES;

  /**
   * Construct a new <code>MarshallingMessageConverter</code> with no {@link Marshaller} or {@link Unmarshaller} set.
   * The marshaller must be set after construction by invoking {@link #setMarshaller(Marshaller)} and
   {@link #setUnmarshaller(Unmarshaller)} .
   */
  public MarshallingMessageConverter() {
  }

  /**
   * Construct a new <code>MarshallingMessageConverter</code> with the given {@link Marshaller} set.
   <p>If the given {@link Marshaller} also implements the {@link Unmarshaller} interface,
   * it is used for both marshalling and unmarshalling. Otherwise, an exception is thrown.
   <p>Note that all {@link Marshaller} implementations in Spring also implement the
   {@link Unmarshaller} interface, so that you can safely use this constructor.
   @param marshaller object used as marshaller and unmarshaller
   @throws IllegalArgumentException when <code>marshaller</code> does not implement the
   {@link Unmarshaller} interface as well
   */
  public MarshallingMessageConverter(Marshaller marshaller) {
    Assert.notNull(marshaller, "Marshaller must not be null");
    if (!(marshaller instanceof Unmarshaller)) {
      throw new IllegalArgumentException(
          "Marshaller [" + marshaller + "] does not implement the Unmarshaller " +
          "interface. Please set an Unmarshaller explicitely by using the " +
          "MarshallingMessageConverter(Marshaller, Unmarshaller) constructor.");
    }
    else {
      this.marshaller = marshaller;
      this.unmarshaller = (Unmarshallermarshaller;
    }
  }

  /**
   * Construct a new <code>MarshallingMessageConverter</code> with the
   * given Marshaller and Unmarshaller.
   @param marshaller the Marshaller to use
   @param unmarshaller the Unmarshaller to use
   */
  public MarshallingMessageConverter(Marshaller marshaller, Unmarshaller unmarshaller) {
    Assert.notNull(marshaller, "Marshaller must not be null");
    Assert.notNull(unmarshaller, "Unmarshaller must not be null");
    this.marshaller = marshaller;
    this.unmarshaller = unmarshaller;
  }


  /**
   * Set the {@link Marshaller} to be used by this message converter.
   */
  public void setMarshaller(Marshaller marshaller) {
    this.marshaller = marshaller;
  }

  /**
   * Set the {@link Unmarshaller} to be used by this message converter.
   */
  public void setUnmarshaller(Unmarshaller unmarshaller) {
    this.unmarshaller = unmarshaller;
  }

  /**
   * Specify whether {@link #toMessage(Object, Session)} should marshal to
   * a {@link BytesMessage} or a {@link TextMessage}.
   <p>The default is {@link MessageType#BYTES}, i.e. this converter marshals
   * to a {@link BytesMessage}. Note that the default version of this converter
   * supports {@link MessageType#BYTES} and {@link MessageType#TEXT} only.
   @see MessageType#BYTES
   @see MessageType#TEXT
   */
  public void setTargetType(MessageType targetType) {
    this.targetType = targetType;
  }

  public void afterPropertiesSet() {
    Assert.notNull(this.marshaller, "Property 'marshaller' is required");
    Assert.notNull(this.unmarshaller, "Property 'unmarshaller' is required");
  }


  /**
   * This implementation marshals the given object to a {@link javax.jms.TextMessage} or
   {@link javax.jms.BytesMessage}. The desired message type can be defined by setting
   * the {@link #setTargetType "marshalTo"} property.
   @see #marshalToTextMessage
   @see #marshalToBytesMessage
   */
  public Message toMessage(Object object, Session sessionthrows JMSException, MessageConversionException {
    try {
      switch (this.targetType) {
        case TEXT:
          return marshalToTextMessage(object, session, this.marshaller);
        case BYTES:
          return marshalToBytesMessage(object, session, this.marshaller);
        default:
          return marshalToMessage(object, session, this.marshaller, this.targetType);
      }
    }
    catch (XmlMappingException ex) {
      throw new MessageConversionException("Could not marshal [" + object + "]", ex);
    }
    catch (IOException ex) {
      throw new MessageConversionException("Could not marshal  [" + object + "]", ex);
    }
  }

  /**
   * This implementation unmarshals the given {@link Message} into an object.
   @see #unmarshalFromTextMessage
   @see #unmarshalFromBytesMessage
   */
  public Object fromMessage(Message messagethrows JMSException, MessageConversionException {
    try {
      if (message instanceof TextMessage) {
        TextMessage textMessage = (TextMessagemessage;
        return unmarshalFromTextMessage(textMessage, this.unmarshaller);
      }
      else if (message instanceof BytesMessage) {
        BytesMessage bytesMessage = (BytesMessagemessage;
        return unmarshalFromBytesMessage(bytesMessage, this.unmarshaller);
      }
      else {
        return unmarshalFromMessage(message, this.unmarshaller);
      }
    }
    catch (IOException ex) {
      throw new MessageConversionException("Could not access message content: " + message, ex);
    }
    catch (XmlMappingException ex) {
      throw new MessageConversionException("Could not unmarshal message: " + message, ex);
    }
  }


  /**
   * Marshal the given object to a {@link TextMessage}.
   @param object the object to be marshalled
   @param session current JMS session
   @param marshaller the marshaller to use
   @return the resulting message
   @throws JMSException if thrown by JMS methods
   @throws IOException in case of I/O errors
   @throws XmlMappingException in case of OXM mapping errors
   @see Session#createTextMessage
   @see Marshaller#marshal(Object, Result)
   */
  protected TextMessage marshalToTextMessage(Object object, Session session, Marshaller marshaller)
      throws JMSException, IOException, XmlMappingException {

    StringWriter writer = new StringWriter();
    Result result = new StreamResult(writer);
    marshaller.marshal(object, result);
    return session.createTextMessage(writer.toString());
  }

  /**
   * Marshal the given object to a {@link BytesMessage}.
   @param object the object to be marshalled
   @param session current JMS session
   @param marshaller the marshaller to use
   @return the resulting message
   @throws JMSException if thrown by JMS methods
   @throws IOException in case of I/O errors
   @throws XmlMappingException in case of OXM mapping errors
   @see Session#createBytesMessage
   @see Marshaller#marshal(Object, Result)
   */
  protected BytesMessage marshalToBytesMessage(Object object, Session session, Marshaller marshaller)
      throws JMSException, IOException, XmlMappingException {

    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    StreamResult streamResult = new StreamResult(bos);
    marshaller.marshal(object, streamResult);
    BytesMessage message = session.createBytesMessage();
    message.writeBytes(bos.toByteArray());
    return message;
  }

  /**
   * Template method that allows for custom message marshalling.
   * Invoked when {@link #setTargetType} is not {@link MessageType#TEXT} or
   {@link MessageType#BYTES}.
   <p>The default implementation throws an {@link IllegalArgumentException}.
   @param object the object to marshal
   @param session the JMS session
   @param marshaller the marshaller to use
   @param targetType the target message type (other than TEXT or BYTES)
   @return the resulting message
   @throws JMSException if thrown by JMS methods
   @throws IOException in case of I/O errors
   @throws XmlMappingException in case of OXM mapping errors
   */
  protected Message marshalToMessage(Object object, Session session, Marshaller marshaller, MessageType targetType)
      throws JMSException, IOException, XmlMappingException {

    throw new IllegalArgumentException(
        "Unsupported message type [" + targetType + "]. Cannot marshal to the specified message type.");
  }


  /**
   * Unmarshal the given {@link TextMessage} into an object.
   @param message the message
   @param unmarshaller the unmarshaller to use
   @return the unmarshalled object
   @throws JMSException if thrown by JMS methods
   @throws IOException in case of I/O errors
   @throws XmlMappingException in case of OXM mapping errors
   @see Unmarshaller#unmarshal(Source)
   */
  protected Object unmarshalFromTextMessage(TextMessage message, Unmarshaller unmarshaller)
      throws JMSException, IOException, XmlMappingException {

    Source source = new StreamSource(new StringReader(message.getText()));
    return unmarshaller.unmarshal(source);
  }

  /**
   * Unmarshal the given {@link BytesMessage} into an object.
   @param message the message
   @param unmarshaller the unmarshaller to use
   @return the unmarshalled object
   @throws JMSException if thrown by JMS methods
   @throws IOException in case of I/O errors
   @throws XmlMappingException in case of OXM mapping errors
   @see Unmarshaller#unmarshal(Source)
   */
  protected Object unmarshalFromBytesMessage(BytesMessage message, Unmarshaller unmarshaller)
      throws JMSException, IOException, XmlMappingException {

    byte[] bytes = new byte[(intmessage.getBodyLength()];
    message.readBytes(bytes);
    ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
    StreamSource source = new StreamSource(bis);
    return unmarshaller.unmarshal(source);
  }

  /**
   * Template method that allows for custom message unmarshalling.
   * Invoked when {@link #fromMessage(Message)} is invoked with a message
   * that is not a {@link TextMessage} or {@link BytesMessage}.
   <p>The default implemenetation throws an {@link IllegalArgumentException}.
   @param message the message
   @param unmarshaller the unmarshaller to use
   @return the unmarshalled object
   @throws JMSException if thrown by JMS methods
   @throws IOException in case of I/O errors
   @throws XmlMappingException in case of OXM mapping errors
   */
  protected Object unmarshalFromMessage(Message message, Unmarshaller unmarshaller)
      throws JMSException, IOException, XmlMappingException {

    throw new IllegalArgumentException(
        "MarshallingMessageConverter only supports TextMessages and BytesMessages");
  }

}