View Javadoc

1   /*
2    * Copyright 2007 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.oxm.support;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.ByteArrayOutputStream;
21  import java.io.IOException;
22  import javax.jms.BytesMessage;
23  import javax.jms.JMSException;
24  import javax.jms.Message;
25  import javax.jms.Session;
26  import javax.jms.TextMessage;
27  import javax.xml.transform.Result;
28  import javax.xml.transform.Source;
29  import javax.xml.transform.stream.StreamResult;
30  import javax.xml.transform.stream.StreamSource;
31  
32  import org.springframework.beans.factory.InitializingBean;
33  import org.springframework.jms.support.converter.MessageConversionException;
34  import org.springframework.jms.support.converter.MessageConverter;
35  import org.springframework.oxm.Marshaller;
36  import org.springframework.oxm.MarshallingFailureException;
37  import org.springframework.oxm.Unmarshaller;
38  import org.springframework.oxm.UnmarshallingFailureException;
39  import org.springframework.util.Assert;
40  import org.springframework.xml.transform.StringResult;
41  import org.springframework.xml.transform.StringSource;
42  
43  /**
44   * Spring JMS {@link MessageConverter} that uses a {@link Marshaller} and {@link Unmarshaller}. Marshals an object to a
45   * {@link BytesMessage}, or to a {@link TextMessage} if the {@link #setMarshalTo marshalTo} is set to {@link
46   * #MARSHAL_TO_TEXT_MESSAGE}. Unmarshals from a {@link TextMessage} or {@link BytesMessage} to an object.
47   *
48   * @author Arjen Poutsma
49   * @see org.springframework.jms.core.JmsTemplate#convertAndSend
50   * @see org.springframework.jms.core.JmsTemplate#receiveAndConvert
51   * @since 1.5.1
52   */
53  public class MarshallingMessageConverter implements MessageConverter, InitializingBean {
54  
55      /** Constant that indicates that {@link #toMessage(Object, Session)} should marshal to a {@link BytesMessage}. */
56      public static final int MARSHAL_TO_BYTES_MESSAGE = 1;
57  
58      /** Constant that indicates that {@link #toMessage(Object, Session)} should marshal to a {@link TextMessage}. */
59      public static final int MARSHAL_TO_TEXT_MESSAGE = 2;
60  
61      private Marshaller marshaller;
62  
63      private Unmarshaller unmarshaller;
64  
65      private int marshalTo = MARSHAL_TO_BYTES_MESSAGE;
66  
67      /**
68       * Constructs a new <code>MarshallingMessageConverter</code> with no {@link Marshaller} set. The marshaller must be
69       * set after construction by invoking {@link #setMarshaller(Marshaller)}.
70       */
71      public MarshallingMessageConverter() {
72      }
73  
74      /**
75       * Constructs a new <code>MarshallingMessageConverter</code> with the given {@link Marshaller} set.  If the given
76       * {@link Marshaller} also implements the {@link Unmarshaller} interface, it is used for both marshalling and
77       * unmarshalling. Otherwise, an exception is thrown.
78       * <p/>
79       * Note that all {@link Marshaller} implementations in Spring-WS also implement the {@link Unmarshaller} interface,
80       * so that you can safely use this constructor.
81       *
82       * @param marshaller object used as marshaller and unmarshaller
83       * @throws IllegalArgumentException when <code>marshaller</code> does not implement the {@link Unmarshaller}
84       *                                  interface
85       */
86      public MarshallingMessageConverter(Marshaller marshaller) {
87          Assert.notNull(marshaller, "marshaller must not be null");
88          if (!(marshaller instanceof Unmarshaller)) {
89              throw new IllegalArgumentException("Marshaller [" + marshaller + "] does not implement the Unmarshaller " +
90                      "interface. Please set an Unmarshaller explicitely by using the " +
91                      "AbstractMarshallingPayloadEndpoint(Marshaller, Unmarshaller) constructor.");
92          }
93          else {
94              this.marshaller = marshaller;
95              this.unmarshaller = (Unmarshaller) marshaller;
96          }
97      }
98  
99      /**
100      * Creates a new <code>MarshallingMessageConverter</code> with the given marshaller and unmarshaller.
101      *
102      * @param marshaller   the marshaller to use
103      * @param unmarshaller the unmarshaller to use
104      */
105     public MarshallingMessageConverter(Marshaller marshaller, Unmarshaller unmarshaller) {
106         Assert.notNull(marshaller, "marshaller must not be null");
107         Assert.notNull(unmarshaller, "unmarshaller must not be null");
108         this.marshaller = marshaller;
109         this.unmarshaller = unmarshaller;
110     }
111 
112     /**
113      * Indicates whether {@link #toMessage(Object,Session)} should marshal to a {@link BytesMessage} or a {@link
114      * TextMessage}. The default is {@link #MARSHAL_TO_BYTES_MESSAGE}, i.e. this converter marshals to a {@link
115      * BytesMessage}.
116      *
117      * @see #MARSHAL_TO_BYTES_MESSAGE
118      * @see #MARSHAL_TO_TEXT_MESSAGE
119      */
120     public void setMarshalTo(int marshalTo) {
121         this.marshalTo = marshalTo;
122     }
123 
124     /** Sets the {@link Marshaller} to be used by this message converter. */
125     public void setMarshaller(Marshaller marshaller) {
126         this.marshaller = marshaller;
127     }
128 
129     /** Sets the {@link Unmarshaller} to be used by this message converter. */
130     public void setUnmarshaller(Unmarshaller unmarshaller) {
131         this.unmarshaller = unmarshaller;
132     }
133 
134     public void afterPropertiesSet() throws Exception {
135         Assert.notNull(marshaller, "Property 'marshaller' is required");
136         Assert.notNull(unmarshaller, "Property 'unmarshaller' is required");
137     }
138 
139     /**
140      * Marshals the given object to a {@link TextMessage} or {@link javax.jms.BytesMessage}. The desired message type
141      * can be defined by setting the {@link #setMarshalTo(int) marshalTo} property.
142      *
143      * @see #marshalToTextMessage
144      * @see #marshalToBytesMessage
145      */
146     public Message toMessage(Object object, Session session) throws JMSException, MessageConversionException {
147         try {
148             switch (marshalTo) {
149                 case MARSHAL_TO_TEXT_MESSAGE:
150                     return marshalToTextMessage(object, session, marshaller);
151                 case MARSHAL_TO_BYTES_MESSAGE:
152                     return marshalToBytesMessage(object, session, marshaller);
153                 default:
154                     return marshalToMessage(object, session, marshaller);
155             }
156         }
157         catch (MarshallingFailureException ex) {
158             throw new MessageConversionException("Could not marshal [" + object + "]", ex);
159         }
160         catch (IOException ex) {
161             throw new MessageConversionException("Could not marshal  [" + object + "]", ex);
162         }
163     }
164 
165     /**
166      * Unmarshals the given {@link Message} into an object.
167      *
168      * @see #unmarshalFromTextMessage
169      * @see #unmarshalFromBytesMessage
170      */
171     public Object fromMessage(Message message) throws JMSException, MessageConversionException {
172         try {
173             if (message instanceof TextMessage) {
174                 TextMessage textMessage = (TextMessage) message;
175                 return unmarshalFromTextMessage(textMessage, unmarshaller);
176             }
177             else if (message instanceof BytesMessage) {
178                 BytesMessage bytesMessage = (BytesMessage) message;
179                 return unmarshalFromBytesMessage(bytesMessage, unmarshaller);
180             }
181             else {
182                 return unmarshalFromMessage(message, unmarshaller);
183             }
184         }
185         catch (UnmarshallingFailureException ex) {
186             throw new MessageConversionException("Could not unmarshal message [" + message + "]", ex);
187         }
188         catch (IOException ex) {
189             throw new MessageConversionException("Could not unmarshal message [" + message + "]", ex);
190         }
191     }
192 
193     /**
194      * Marshals the given object to a {@link TextMessage}.
195      *
196      * @param object     the object to be marshalled
197      * @param session    current JMS session
198      * @param marshaller the marshaller to use
199      * @return the resulting message
200      * @throws JMSException if thrown by JMS methods
201      * @throws IOException  in case of I/O errors
202      * @see Session#createTextMessage
203      * @see Marshaller#marshal(Object, Result)
204      */
205     protected TextMessage marshalToTextMessage(Object object, Session session, Marshaller marshaller)
206             throws JMSException, IOException {
207         StringResult result = new StringResult();
208         marshaller.marshal(object, result);
209         return session.createTextMessage(result.toString());
210     }
211 
212     /**
213      * Marshals the given object to a {@link BytesMessage}.
214      *
215      * @param object     the object to be marshalled
216      * @param session    current JMS session
217      * @param marshaller the marshaller to use
218      * @return the resulting message
219      * @throws JMSException if thrown by JMS methods
220      * @throws IOException  in case of I/O errors
221      * @see Session#createBytesMessage
222      * @see Marshaller#marshal(Object, Result)
223      */
224     protected BytesMessage marshalToBytesMessage(Object object, Session session, Marshaller marshaller)
225             throws JMSException, IOException {
226         ByteArrayOutputStream bos = new ByteArrayOutputStream();
227         StreamResult streamResult = new StreamResult(bos);
228         marshaller.marshal(object, streamResult);
229         BytesMessage message = session.createBytesMessage();
230         message.writeBytes(bos.toByteArray());
231         return message;
232     }
233 
234     /**
235      * Template method that allows for custom message marshalling. Invoked when {@link #setMarshalTo(int)} is not {@link
236      * #MARSHAL_TO_TEXT_MESSAGE} or {@link #MARSHAL_TO_BYTES_MESSAGE}.
237      * <p/>
238      * Default implemenetation throws a {@link MessageConversionException}.
239      *
240      * @param object     the object to marshal
241      * @param session    the JMS session
242      * @param marshaller the marshaller to use
243      * @return the resulting message
244      * @throws JMSException if thrown by JMS methods
245      * @throws IOException  in case of I/O errors
246      */
247     protected Message marshalToMessage(Object object, Session session, Marshaller marshaller)
248             throws JMSException, IOException {
249         throw new MessageConversionException(
250                 "Unknown 'marshalTo' value [" + marshalTo + "]. Cannot convert object to Message");
251     }
252 
253     /**
254      * Unmarshals the given {@link TextMessage} into an object.
255      *
256      * @param message      the message
257      * @param unmarshaller the unmarshaller to use
258      * @return the unmarshalled object
259      * @throws JMSException if thrown by JMS methods
260      * @throws IOException  in case of I/O errors
261      * @see Unmarshaller#unmarshal(Source)
262      */
263     protected Object unmarshalFromTextMessage(TextMessage message, Unmarshaller unmarshaller)
264             throws JMSException, IOException {
265         StringSource source = new StringSource(message.getText());
266         return unmarshaller.unmarshal(source);
267     }
268 
269     /**
270      * Unmarshals the given {@link BytesMessage} into an object.
271      *
272      * @param message      the message
273      * @param unmarshaller the unmarshaller to use
274      * @return the unmarshalled object
275      * @throws JMSException if thrown by JMS methods
276      * @throws IOException  in case of I/O errors
277      * @see Unmarshaller#unmarshal(Source)
278      */
279     protected Object unmarshalFromBytesMessage(BytesMessage message, Unmarshaller unmarshaller)
280             throws JMSException, IOException {
281         byte[] bytes = new byte[(int) message.getBodyLength()];
282         message.readBytes(bytes);
283         ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
284         StreamSource source = new StreamSource(bis);
285         return unmarshaller.unmarshal(source);
286     }
287 
288     /**
289      * Template method that allows for custom message unmarshalling. Invoked when {@link #fromMessage(Message)} is
290      * invoked with a message that is not a {@link TextMessage} or {@link BytesMessage}.
291      * <p/>
292      * Default implemenetation throws a {@link MessageConversionException}.
293      *
294      * @param message      the message
295      * @param unmarshaller the unmarshaller to use
296      * @return the unmarshalled object
297      * @throws JMSException if thrown by JMS methods
298      * @throws IOException  in case of I/O errors
299      */
300     protected Object unmarshalFromMessage(Message message, Unmarshaller unmarshaller) throws JMSException, IOException {
301         throw new MessageConversionException(
302                 "MarshallingMessageConverter only supports TextMessages and BytesMessages");
303     }
304 }
305