1
2
3
4
5
6
7
8
9
10
11 package org.mule.transport.jms;
12
13 import org.mule.api.transport.OutputHandler;
14 import org.mule.util.ArrayUtils;
15 import org.mule.util.ClassUtils;
16 import org.mule.util.IOUtils;
17 import org.mule.util.StringUtils;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.ObjectOutputStream;
22 import java.io.Serializable;
23 import java.text.MessageFormat;
24 import java.util.ArrayList;
25 import java.util.Enumeration;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30
31 import javax.jms.BytesMessage;
32 import javax.jms.Destination;
33 import javax.jms.JMSException;
34 import javax.jms.MapMessage;
35 import javax.jms.Message;
36 import javax.jms.MessageEOFException;
37 import javax.jms.MessageFormatException;
38 import javax.jms.ObjectMessage;
39 import javax.jms.Queue;
40 import javax.jms.Session;
41 import javax.jms.StreamMessage;
42 import javax.jms.TextMessage;
43 import javax.jms.Topic;
44
45 import org.apache.commons.io.output.ByteArrayOutputStream;
46 import org.apache.commons.logging.Log;
47 import org.apache.commons.logging.LogFactory;
48
49
50
51
52
53 public class JmsMessageUtils
54 {
55 public static final char REPLACEMENT_CHAR = '_';
56
57 private static final Log logger = LogFactory.getLog(JmsMessageUtils.class);
58
59
60
61
62
63
64
65 public static String encodeHeader(String name)
66 {
67
68 boolean nonCompliant = false;
69
70 if (StringUtils.isEmpty(name))
71 {
72 throw new IllegalArgumentException("Header name to encode must not be null or empty");
73 }
74
75 int i = 0, length = name.length();
76 while (i < length && Character.isJavaIdentifierPart(name.charAt(i)))
77 {
78
79 i++;
80 }
81
82 if (i == length)
83 {
84
85 return name;
86 }
87 else
88 {
89
90 StringBuffer sb = new StringBuffer(name);
91 for (int j = i; j < length; j++)
92 {
93 if (!Character.isJavaIdentifierPart(sb.charAt(j)))
94 {
95 sb.setCharAt(j, REPLACEMENT_CHAR);
96 nonCompliant = true;
97 }
98 }
99
100 if (nonCompliant)
101 {
102 logger.warn(MessageFormat.format(
103 "Header: {0} is not compliant with JMS specification (sec. 3.5.1, 3.8.1.1). It will cause " +
104 "problems in your and other applications. Please update your application code to correct this. " +
105 "Mule renamed it to {1}", name, sb.toString()));
106 }
107
108 return sb.toString();
109 }
110 }
111
112 public static Message toMessage(Object object, Session session) throws JMSException
113 {
114 if (object instanceof Message)
115 {
116 return (Message) object;
117 }
118 else if (object instanceof String)
119 {
120 return stringToMessage((String) object, session);
121 }
122 else if (object instanceof Map<?, ?> && validateMapMessageType((Map<?, ?>)object))
123 {
124 return mapToMessage((Map<?, ?>) object, session);
125 }
126 else if (object instanceof InputStream)
127 {
128 return inputStreamToMessage((InputStream) object, session);
129 }
130 else if (object instanceof List<?>)
131 {
132 return listToMessage((List<?>) object, session);
133 }
134 else if (object instanceof byte[])
135 {
136 return byteArrayToMessage((byte[]) object, session);
137 }
138 else if (object instanceof Serializable)
139 {
140 return serializableToMessage((Serializable) object, session);
141 }
142 else if (object instanceof OutputHandler)
143 {
144 return outputHandlerToMessage((OutputHandler) object, session);
145 }
146 else
147 {
148 throw new JMSException(
149 "Source was not of a supported type. Valid types are Message, String, Map, InputStream, List, byte[], Serializable or OutputHandler, "
150 + "but was " + ClassUtils.getShortClassName(object, "<null>"));
151 }
152 }
153
154 private static Message stringToMessage(String value, Session session) throws JMSException
155 {
156 return session.createTextMessage(value);
157 }
158
159 private static Message mapToMessage(Map<?, ?> value, Session session) throws JMSException
160 {
161 MapMessage mMsg = session.createMapMessage();
162
163 for (Iterator<?> i = value.entrySet().iterator(); i.hasNext();)
164 {
165 Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
166 mMsg.setObject(entry.getKey().toString(), entry.getValue());
167 }
168
169 return mMsg;
170 }
171
172 private static Message inputStreamToMessage(InputStream value, Session session) throws JMSException
173 {
174 StreamMessage streamMessage = session.createStreamMessage();
175 byte[] buffer = new byte[4096];
176 int len;
177
178 try
179 {
180 while ((len = value.read(buffer)) != -1)
181 {
182 streamMessage.writeBytes(buffer, 0, len);
183 }
184 }
185 catch (IOException e)
186 {
187 throw new JMSException("Failed to read input stream to create a stream message: " + e);
188 }
189 finally
190 {
191 IOUtils.closeQuietly(value);
192 }
193
194 return streamMessage;
195 }
196
197 private static Message listToMessage(List<?> value, Session session)
198 throws JMSException
199 {
200 StreamMessage sMsg = session.createStreamMessage();
201
202 for (Iterator<?> iter = value.iterator(); iter.hasNext();)
203 {
204 Object o = iter.next();
205 if (validateStreamMessageType(o))
206 {
207 sMsg.writeObject(o);
208 }
209 else
210 {
211 throw new MessageFormatException(String.format(
212 "Invalid type passed to StreamMessage: %s . Allowed types are: "
213 + "Boolean, Byte, Short, Character, Integer, Long, Float, Double,"
214 + "String and byte[]", ClassUtils.getShortClassName(o, "null")));
215 }
216 }
217 return sMsg;
218 }
219
220 private static Message byteArrayToMessage(byte[] value, Session session) throws JMSException
221 {
222 BytesMessage bMsg = session.createBytesMessage();
223 bMsg.writeBytes(value);
224
225 return bMsg;
226 }
227
228 private static Message serializableToMessage(Serializable value, Session session) throws JMSException
229 {
230 ObjectMessage oMsg = session.createObjectMessage();
231 oMsg.setObject(value);
232
233 return oMsg;
234 }
235
236 private static Message outputHandlerToMessage(OutputHandler value, Session session) throws JMSException
237 {
238 ByteArrayOutputStream output = new ByteArrayOutputStream();
239 try
240 {
241 value.write(null, output);
242 }
243 catch (IOException e)
244 {
245 JMSException j = new JMSException("Could not serialize OutputHandler.");
246 j.initCause(e);
247 throw j;
248 }
249
250 BytesMessage bMsg = session.createBytesMessage();
251 bMsg.writeBytes(output.toByteArray());
252
253 return bMsg;
254 }
255
256 public static Object toObject(Message source, String jmsSpec, String encoding) throws JMSException, IOException
257 {
258 if (source instanceof ObjectMessage)
259 {
260 return ((ObjectMessage) source).getObject();
261 }
262 else if (source instanceof MapMessage)
263 {
264 Map<String, Object> map = new HashMap<String, Object>();
265 MapMessage m = (MapMessage) source;
266
267 for (Enumeration<?> e = m.getMapNames(); e.hasMoreElements();)
268 {
269 String name = (String) e.nextElement();
270 Object obj = m.getObject(name);
271 map.put(name, obj);
272 }
273
274 return map;
275 }
276 else if (source instanceof TextMessage)
277 {
278 return ((TextMessage) source).getText();
279 }
280 else if (source instanceof BytesMessage)
281 {
282 return toByteArray(source, jmsSpec, encoding);
283 }
284 else if (source instanceof StreamMessage)
285 {
286 List<Object> result = new ArrayList<Object>();
287 try
288 {
289 StreamMessage sMsg = (StreamMessage) source;
290 Object obj;
291 while ((obj = sMsg.readObject()) != null)
292 {
293 result.add(obj);
294 }
295 }
296 catch (MessageEOFException eof)
297 {
298
299 }
300 catch (Exception e)
301 {
302 throw new JMSException("Failed to extract information from JMS Stream Message: " + e);
303 }
304 return result;
305 }
306
307
308 return source;
309 }
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325 public static byte[] toByteArray(Message message, String jmsSpec, String encoding) throws JMSException, IOException
326 {
327 if (message instanceof BytesMessage)
328 {
329 BytesMessage bMsg = (BytesMessage) message;
330 bMsg.reset();
331
332 if (JmsConstants.JMS_SPECIFICATION_11.equals(jmsSpec))
333 {
334 long bmBodyLength = bMsg.getBodyLength();
335 if (bmBodyLength > Integer.MAX_VALUE)
336 {
337 throw new JMSException("Size of BytesMessage exceeds Integer.MAX_VALUE; "
338 + "please consider using JMS StreamMessage instead");
339 }
340
341 if (bmBodyLength > 0)
342 {
343 byte[] bytes = new byte[(int) bmBodyLength];
344 bMsg.readBytes(bytes);
345 return bytes;
346 }
347 else
348 {
349 return ArrayUtils.EMPTY_BYTE_ARRAY;
350 }
351 }
352 else
353 {
354 ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
355 byte[] buffer = new byte[4096];
356 int len;
357
358 while ((len = bMsg.readBytes(buffer)) != -1)
359 {
360 baos.write(buffer, 0, len);
361 }
362
363 if (baos.size() > 0)
364 {
365 return baos.toByteArray();
366 }
367 else
368 {
369 return ArrayUtils.EMPTY_BYTE_ARRAY;
370 }
371 }
372 }
373 else if (message instanceof StreamMessage)
374 {
375 StreamMessage sMsg = (StreamMessage) message;
376 sMsg.reset();
377
378 ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
379 byte[] buffer = new byte[4096];
380 int len;
381
382 while ((len = sMsg.readBytes(buffer)) != -1)
383 {
384 baos.write(buffer, 0, len);
385 }
386
387 return baos.toByteArray();
388 }
389 else if (message instanceof ObjectMessage)
390 {
391 ObjectMessage oMsg = (ObjectMessage) message;
392 ByteArrayOutputStream baos = new ByteArrayOutputStream();
393 ObjectOutputStream os = new ObjectOutputStream(baos);
394 os.writeObject(oMsg.getObject());
395 os.flush();
396 os.close();
397 return baos.toByteArray();
398 }
399 else if (message instanceof TextMessage)
400 {
401 TextMessage tMsg = (TextMessage) message;
402 String tMsgText = tMsg.getText();
403
404 if (null == tMsgText)
405 {
406
407
408 return ArrayUtils.EMPTY_BYTE_ARRAY;
409 }
410 else
411 {
412 return tMsgText.getBytes(encoding);
413 }
414 }
415 else
416 {
417 throw new JMSException("Cannot get bytes from Map Message");
418 }
419 }
420
421 public static String getNameForDestination(Destination dest) throws JMSException
422 {
423 if (dest instanceof Queue)
424 {
425 return ((Queue) dest).getQueueName();
426 }
427 else if (dest instanceof Topic)
428 {
429 return ((Topic) dest).getTopicName();
430 }
431 else
432 {
433 return null;
434 }
435 }
436
437 public static Message copyJMSProperties(Message from, Message to, JmsConnector connector)
438 throws JMSException
439 {
440 if (connector.supportsProperty(JmsConstants.JMS_CORRELATION_ID))
441 {
442 to.setJMSCorrelationID(from.getJMSCorrelationID());
443 }
444 if (connector.supportsProperty(JmsConstants.JMS_DELIVERY_MODE))
445 {
446 to.setJMSDeliveryMode(from.getJMSDeliveryMode());
447 }
448 if (connector.supportsProperty(JmsConstants.JMS_DESTINATION))
449 {
450 to.setJMSDestination(from.getJMSDestination());
451 }
452 if (connector.supportsProperty(JmsConstants.JMS_EXPIRATION))
453 {
454 to.setJMSExpiration(from.getJMSExpiration());
455 }
456 if (connector.supportsProperty(JmsConstants.JMS_MESSAGE_ID))
457 {
458 to.setJMSMessageID(from.getJMSMessageID());
459 }
460 if (connector.supportsProperty(JmsConstants.JMS_PRIORITY))
461 {
462 to.setJMSPriority(from.getJMSPriority());
463 }
464 if (connector.supportsProperty(JmsConstants.JMS_REDELIVERED))
465 {
466 to.setJMSRedelivered(from.getJMSRedelivered());
467 }
468 if (connector.supportsProperty(JmsConstants.JMS_REPLY_TO))
469 {
470 to.setJMSReplyTo(from.getJMSReplyTo());
471 }
472 if (connector.supportsProperty(JmsConstants.JMS_TIMESTAMP))
473 {
474 to.setJMSTimestamp(from.getJMSTimestamp());
475 }
476 if (connector.supportsProperty(JmsConstants.JMS_TYPE))
477 {
478 to.setJMSType(from.getJMSType());
479 }
480 return to;
481 }
482
483
484
485
486
487
488
489
490
491
492 protected static boolean validateStreamMessageType(Object candidate)
493 {
494 if (candidate == null ||
495 candidate instanceof Boolean ||
496 candidate instanceof Byte ||
497 candidate instanceof Short ||
498 candidate instanceof Character ||
499 candidate instanceof Integer ||
500 candidate instanceof Long ||
501 candidate instanceof Float ||
502 candidate instanceof Double ||
503 candidate instanceof String ||
504 candidate instanceof byte[])
505 {
506 return true;
507 }
508
509 return false;
510 }
511
512
513
514
515
516
517
518
519
520
521
522 protected static boolean validateMapMessageType(Map<?, ?> candidate)
523 {
524 for (Iterator<?> iterator = candidate.values().iterator(); iterator.hasNext();)
525 {
526 Object o = iterator.next();
527 if (!validateStreamMessageType(o))
528 {
529 return false;
530 }
531 }
532 return true;
533 }
534 }