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