View Javadoc

1   /*
2    * $Id: DefaultMuleMessage.java 12269 2008-07-10 04:19:03Z dfeist $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule;
12  
13  import org.mule.api.ExceptionPayload;
14  import org.mule.api.MuleMessage;
15  import org.mule.api.MuleRuntimeException;
16  import org.mule.api.ThreadSafeAccess;
17  import org.mule.api.transformer.Transformer;
18  import org.mule.api.transformer.TransformerException;
19  import org.mule.api.transport.MessageAdapter;
20  import org.mule.api.transport.MutableMessageAdapter;
21  import org.mule.api.transport.PropertyScope;
22  import org.mule.config.i18n.CoreMessages;
23  import org.mule.transport.AbstractMessageAdapter;
24  import org.mule.transport.DefaultMessageAdapter;
25  import org.mule.transport.NullPayload;
26  import org.mule.util.ClassUtils;
27  
28  import java.io.InputStream;
29  import java.lang.reflect.Proxy;
30  import java.util.ArrayList;
31  import java.util.Iterator;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Set;
35  
36  import javax.activation.DataHandler;
37  
38  import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList;
39  
40  import org.apache.commons.logging.Log;
41  import org.apache.commons.logging.LogFactory;
42  
43  /**
44   * <code>DefaultMuleMessage</code> is a wrapper that contains a payload and properties
45   * associated with the payload.
46   */
47  
48  public class DefaultMuleMessage implements MuleMessage, ThreadSafeAccess
49  {
50      /** Serial version */
51      private static final long serialVersionUID = 1541720810851984842L;
52      private static Log logger = LogFactory.getLog(DefaultMuleMessage.class);
53  
54      private MessageAdapter adapter;
55      private MessageAdapter originalAdapter = null;
56      private transient List appliedTransformerHashCodes = new CopyOnWriteArrayList();
57      private byte[] cache;
58      
59      private static final List consumableClasses = new ArrayList(); 
60      
61      static
62      {
63          try
64          {
65              consumableClasses.add(ClassUtils.loadClass("javax.xml.stream.XMLStreamReader",
66                  DefaultMuleMessage.class));
67          }
68          catch (ClassNotFoundException e)
69          {
70          }
71      }
72      public DefaultMuleMessage(Object message)
73      {
74          this(message, (Map) null);
75      }
76  
77      public DefaultMuleMessage(Object message, Map properties)
78      {
79          if (message instanceof MessageAdapter)
80          {
81              adapter = (MessageAdapter) message;
82          }
83          else
84          {
85              adapter = new DefaultMessageAdapter(message);
86          }
87          addProperties(properties);
88          resetAccessControl();
89      }
90  
91  
92      public DefaultMuleMessage(Object message, MessageAdapter previous)
93      {
94          if (message instanceof MessageAdapter)
95          {
96              adapter = (MessageAdapter) message;
97              ((ThreadSafeAccess) adapter).resetAccessControl();
98          }
99          else
100         {
101             adapter = new DefaultMessageAdapter(message, previous);
102         }
103         if (previous.getExceptionPayload() != null)
104         {
105             setExceptionPayload(previous.getExceptionPayload());
106         }
107         setEncoding(previous.getEncoding());
108         if (previous.getAttachmentNames().size() > 0)
109         {
110             Set attNames = adapter.getAttachmentNames();
111             synchronized (attNames)
112             {
113                 for (Iterator iterator = attNames.iterator(); iterator.hasNext();)
114                 {
115                     String s = (String) iterator.next();
116                     try
117                     {
118                         addAttachment(s, adapter.getAttachment(s));
119                     }
120                     catch (Exception e)
121                     {
122                         throw new MuleRuntimeException(CoreMessages.failedToReadAttachment(s), e);
123                     }
124                 }
125             }
126         }
127         resetAccessControl();
128     }
129 
130     /** {@inheritDoc} */
131     public Object getPayload(Class outputType) throws TransformerException
132     {
133         return getPayload(outputType, getEncoding());
134     }
135 
136     /**
137      * Will attempt to obtain the payload of this message with the desired Class type. This will
138      * try and resolve a trnsformr that can do this transformation. If a transformer cannot be found
139      * an exception is thrown.  Any transfromers added to the reqgistry will be checked for compatability
140      *
141      * @param outputType the desired return type
142      * @param encoding   the encoding to use if required
143      * @return The converted payload of this message. Note that this method will not alter the payload of this
144      *         message *unless* the payload is an inputstream in which case the stream will be read and the payload will become
145      *         the fully read stream.
146      * @throws TransformerException if a transformer cannot be found or there is an error during transformation of the
147      *                              payload
148      */
149     protected Object getPayload(Class outputType, String encoding) throws TransformerException
150     {
151         //Handle null by ignoring the request
152         if (outputType == null)
153         {
154             return getPayload();
155         }
156 
157         Class inputCls = getPayload().getClass();
158 
159         //Special case where proxies are used for testing
160         if (Proxy.isProxyClass(inputCls))
161         {
162             inputCls = inputCls.getInterfaces()[0];
163         }
164 
165         //If no conversion is necessary, just return the payload as-is
166         if (outputType.isAssignableFrom(inputCls))
167         {
168             return getPayload();
169         }
170         //Grab a list of transformers that batch out input/output requirements
171         // List transformers = RegistryContext.getRegistry().lookupTransformers(inputCls, outputType);
172 
173         //The transformer to execute on this message
174         Transformer transformer = null;
175         transformer = MuleServer.getMuleContext().getRegistry().lookupTransformer(inputCls, outputType);
176 
177         //no transformers found
178         if (transformer == null)
179         {
180             throw new TransformerException(CoreMessages.noTransformerFoundForMessage(inputCls, outputType));
181         }
182 
183         // Pass in the adapter itself, so we respect the encoding
184         Object result = transformer.transform(this);
185 
186         //TODO Unless we disallow Object.class as a valid return type we need to do this extra check
187         if (!outputType.isAssignableFrom(result.getClass()))
188         {
189             throw new TransformerException(CoreMessages.transformOnObjectNotOfSpecifiedType(outputType.getName(), result.getClass()));
190         }
191         //If the payload is a stream and we've consumed it, then we should
192         //set the payload on the message
193         //This is the only time this method will alter the payload on the message
194         if (isPayloadConsumed(inputCls))
195         {
196             setPayload(result);
197         }
198 
199         return result;
200     }
201 
202     /**
203      * Checks if the payload has been consumed for this message. This only applies to Streaming payload types
204      * since once the stream has been read, the payload of the message should be updated to represent the data read
205      * from the stream
206      *
207      * @param inputCls the input type of the message payload
208      * @return true if the payload message type was stream-based, false otherwise
209      */
210     protected boolean isPayloadConsumed(Class inputCls)
211     {
212         return InputStream.class.isAssignableFrom(inputCls) || isConsumedFromAdditional(inputCls);
213     }
214 
215     private boolean isConsumedFromAdditional(Class inputCls)
216     {
217         if (consumableClasses.isEmpty())
218         {
219             return false;
220         }
221         
222         for (Iterator itr = consumableClasses.iterator(); itr.hasNext();) 
223         {
224             Class c = (Class) itr.next();
225         
226             if (c.isAssignableFrom(inputCls))
227             {
228                 return true;
229             }
230         }
231         return false;
232     }
233 
234     /** {@inheritDoc} */
235     public MessageAdapter getAdapter()
236     {
237         return adapter;
238     }
239 
240     /** {@inheritDoc} */
241     public Object getOrginalPayload()
242     {
243         return (originalAdapter == null ? adapter.getPayload() : originalAdapter.getPayload());
244     }
245 
246     /** {@inheritDoc} */
247     public MessageAdapter getOriginalAdapter()
248     {
249         return (originalAdapter == null ? adapter : originalAdapter);
250     }
251 
252     /** {@inheritDoc} */
253     public void setProperty(String key, Object value, PropertyScope scope)
254     {
255         adapter.setProperty(key, value, scope);
256     }
257 
258 
259     /** {@inheritDoc} */
260     public Object getProperty(String key)
261     {
262         return adapter.getProperty(key);
263     }
264 
265     /** {@inheritDoc} */
266     public Object removeProperty(String key)
267     {
268         return adapter.removeProperty(key);
269     }
270 
271     /** {@inheritDoc} */
272     public void setProperty(String key, Object value)
273     {
274         adapter.setProperty(key, value);
275     }
276 
277     /** {@inheritDoc} */
278     public final String getPayloadAsString() throws Exception
279     {
280         assertAccess(READ);
281         return getPayloadAsString(getEncoding());
282     }
283 
284     /** {@inheritDoc} */
285     public byte[] getPayloadAsBytes() throws Exception
286     {
287         assertAccess(READ);
288         if (cache != null)
289         {
290             return cache;
291         }
292         byte[] result = (byte[]) getPayload(byte[].class);
293         if (MuleServer.getMuleContext().getConfiguration().isCacheMessageAsBytes())
294         {
295             cache = result;
296         }
297         return result;
298     }
299 
300     /** {@inheritDoc} */
301     public String getPayloadAsString(String encoding) throws Exception
302     {
303         assertAccess(READ);
304         if (cache != null)
305         {
306             return new String(cache, encoding);
307         }
308         String result = (String) getPayload(String.class);
309         if (MuleServer.getMuleContext().getConfiguration().isCacheMessageAsBytes())
310         {
311             cache = result.getBytes(encoding);
312         }
313         return result;
314     }
315 
316     /** {@inheritDoc} */
317     public Set getPropertyNames()
318     {
319         return adapter.getPropertyNames();
320     }
321 
322     //** {@inheritDoc} */
323     public double getDoubleProperty(String name, double defaultValue)
324     {
325         return adapter.getDoubleProperty(name, defaultValue);
326     }
327 
328     /** {@inheritDoc} */
329     public void setDoubleProperty(String name, double value)
330     {
331         adapter.setDoubleProperty(name, value);
332     }
333 
334     /** {@inheritDoc} */
335     public String getUniqueId()
336     {
337         return adapter.getUniqueId();
338     }
339 
340     /** {@inheritDoc} */
341     public Object getProperty(String name, Object defaultValue)
342     {
343         return adapter.getProperty(name, defaultValue);
344     }
345 
346     /** {@inheritDoc} */
347     public int getIntProperty(String name, int defaultValue)
348     {
349         return adapter.getIntProperty(name, defaultValue);
350     }
351 
352     /** {@inheritDoc} */
353     public long getLongProperty(String name, long defaultValue)
354     {
355         return adapter.getLongProperty(name, defaultValue);
356     }
357 
358     /** {@inheritDoc} */
359     public boolean getBooleanProperty(String name, boolean defaultValue)
360     {
361         return adapter.getBooleanProperty(name, defaultValue);
362     }
363 
364     /** {@inheritDoc} */
365     public void setBooleanProperty(String name, boolean value)
366     {
367         adapter.setBooleanProperty(name, value);
368     }
369 
370     /** {@inheritDoc} */
371     public void setIntProperty(String name, int value)
372     {
373         adapter.setIntProperty(name, value);
374     }
375 
376     /** {@inheritDoc} */
377     public void setLongProperty(String name, long value)
378     {
379         adapter.setLongProperty(name, value);
380     }
381 
382     /** {@inheritDoc} */
383     public void setCorrelationId(String id)
384     {
385         adapter.setCorrelationId(id);
386     }
387 
388     /** {@inheritDoc} */
389     public String getCorrelationId()
390     {
391         return adapter.getCorrelationId();
392     }
393 
394     /** {@inheritDoc} */
395     public void setReplyTo(Object replyTo)
396     {
397         adapter.setReplyTo(replyTo);
398     }
399 
400     /** {@inheritDoc} */
401     public Object getReplyTo()
402     {
403         return adapter.getReplyTo();
404     }
405 
406     /** {@inheritDoc} */
407     public int getCorrelationSequence()
408     {
409         return adapter.getCorrelationSequence();
410     }
411 
412     /** {@inheritDoc} */
413     public void setCorrelationSequence(int sequence)
414     {
415         adapter.setCorrelationSequence(sequence);
416     }
417 
418     /** {@inheritDoc} */
419     public int getCorrelationGroupSize()
420     {
421         return adapter.getCorrelationGroupSize();
422     }
423 
424     //** {@inheritDoc} */
425     public void setCorrelationGroupSize(int size)
426     {
427         adapter.setCorrelationGroupSize(size);
428     }
429 
430     /** {@inheritDoc} */
431     public ExceptionPayload getExceptionPayload()
432     {
433         return adapter.getExceptionPayload();
434     }
435 
436     /** {@inheritDoc} */
437     public void setExceptionPayload(ExceptionPayload exceptionPayload)
438     {
439         adapter.setExceptionPayload(exceptionPayload);
440     }
441 
442     /** {@inheritDoc} */
443     public String toString()
444     {
445         return adapter.toString();
446     }
447 
448     /** {@inheritDoc} */
449     public void addAttachment(String name, DataHandler dataHandler) throws Exception
450     {
451         adapter.addAttachment(name, dataHandler);
452     }
453 
454     /** {@inheritDoc} */
455     public void removeAttachment(String name) throws Exception
456     {
457         adapter.removeAttachment(name);
458     }
459 
460     /** {@inheritDoc} */
461     public DataHandler getAttachment(String name)
462     {
463         return adapter.getAttachment(name);
464     }
465 
466     /** {@inheritDoc} */
467     public Set getAttachmentNames()
468     {
469         return adapter.getAttachmentNames();
470     }
471 
472     /** {@inheritDoc} */
473     public String getEncoding()
474     {
475         return adapter.getEncoding();
476     }
477 
478     /** {@inheritDoc} */
479     public void setEncoding(String encoding)
480     {
481         adapter.setEncoding(encoding);
482     }
483 
484     /** {@inheritDoc} */
485     public String getStringProperty(String name, String defaultValue)
486     {
487         return adapter.getStringProperty(name, defaultValue);
488     }
489 
490     /** {@inheritDoc} */
491     public void setStringProperty(String name, String value)
492     {
493         adapter.setStringProperty(name, value);
494     }
495 
496 
497     /** {@inheritDoc} */
498     public void addProperties(Map properties)
499     {
500         adapter.addProperties(properties);
501     }
502 
503     /** {@inheritDoc} */
504     public void addProperties(Map properties, PropertyScope scope)
505     {
506         adapter.addProperties(properties, scope);
507     }
508 
509     /** {@inheritDoc} */
510     public void clearProperties()
511     {
512         adapter.clearProperties();
513     }
514 
515     /** {@inheritDoc} */
516     public Object getPayload()
517     {
518         return adapter.getPayload();
519     }
520 
521     /** {@inheritDoc} */
522     public synchronized void setPayload(Object payload)
523     {
524         //TODO we may want to enforce stricter rules here, rather than silently wrapping the existing adapter
525         if (!(adapter instanceof MutableMessageAdapter))
526         {
527             adapter = new DefaultMessageAdapter(payload, adapter);
528         }
529         else
530         {
531             ((MutableMessageAdapter) adapter).setPayload(payload);
532         }
533         cache = null;
534     }
535 
536     /** {@inheritDoc} */
537     public void release()
538     {
539         adapter.release();
540         if (originalAdapter != null)
541         {
542             originalAdapter.release();
543         }
544         cache = null;
545         appliedTransformerHashCodes.clear();
546     }
547 
548     /** {@inheritDoc} */
549     public void applyTransformers(List transformers) throws TransformerException
550     {
551         applyTransformers(transformers, null);
552     }
553 
554     public void applyTransformers(List transformers, Class outputType) throws TransformerException
555     {
556         if (!transformers.isEmpty() && !appliedTransformerHashCodes.contains(new Integer(transformers.hashCode())))
557         {
558             applyAllTransformers(transformers);
559             appliedTransformerHashCodes.add(new Integer(transformers.hashCode()));
560         }
561 
562         if (null != outputType && !getPayload().getClass().isAssignableFrom(outputType))
563         {
564             setPayload(getPayload(outputType));
565         }
566     }
567 
568     protected void applyAllTransformers(List transformers) throws TransformerException
569     {
570         if (!transformers.isEmpty())
571         {
572 
573             Iterator iterator = transformers.iterator();
574             while (iterator.hasNext())
575             {
576                 Transformer transformer = (Transformer) iterator.next();
577 
578                 if (getPayload() == null)
579                 {
580                     if (transformer.isAcceptNull())
581                     {
582                         setPayload(NullPayload.getInstance());
583                     }
584                     else
585                     {
586                         if (logger.isDebugEnabled())
587                         {
588                             logger.debug("Transformer " + transformer +
589                                     " doesn't support the null payload, exiting from transformer chain.");
590                         }
591                         break;
592                     }
593                 }
594 
595                 Class srcCls = getPayload().getClass();
596                 if (transformer.isSourceTypeSupported(srcCls))
597                 {
598                     Object result = transformer.transform(this);
599 
600                     if (originalAdapter == null && MuleServer.getMuleContext().getConfiguration().isCacheMessageOriginalPayload())
601                     {
602                         originalAdapter = adapter;
603                     }
604                     //TODO RM*: Must make sure this works for all scenarios
605                     if (result instanceof MuleMessage)
606                     {
607                         synchronized (this)
608                         {
609                             adapter = ((MuleMessage) result).getAdapter();
610                         }
611                     }
612                     else
613                     {
614                         setPayload(result);
615                     }
616                 }
617                 else
618                 {
619                     if (logger.isDebugEnabled())
620                     {
621                         logger.debug("Transformer " + transformer + " doesn't support the source payload: " + srcCls);
622                     }
623                     if (!transformer.isIgnoreBadInput())
624                     {
625                         if (logger.isDebugEnabled())
626                         {
627                             logger.debug("Exiting from transformer chain (ignoreBadInput = false)");
628                         }
629                         break;
630                     }
631                 }
632             }
633         }
634     }
635 
636     //////////////////////////////// ThreadSafeAccess Impl ///////////////////////////////
637 
638     /** {@inheritDoc} */
639     public ThreadSafeAccess newThreadCopy()
640     {
641         if (adapter instanceof ThreadSafeAccess)
642         {
643             logger.debug("new copy of message for " + Thread.currentThread());
644             return new DefaultMuleMessage(((ThreadSafeAccess) adapter).newThreadCopy(), this);
645         }
646         else
647         {
648             // not much we can do here - streaming will have to handle things itself
649             return this;
650         }
651     }
652 
653     /** {@inheritDoc} */
654     public void resetAccessControl()
655     {
656         if (adapter instanceof AbstractMessageAdapter)
657         {
658             ((AbstractMessageAdapter) adapter).resetAccessControl();
659         }
660         if (originalAdapter instanceof AbstractMessageAdapter)
661         {
662             ((AbstractMessageAdapter) originalAdapter).resetAccessControl();
663         }
664     }
665 
666     /** {@inheritDoc} */
667     public void assertAccess(boolean write)
668     {
669         if (adapter instanceof AbstractMessageAdapter)
670         {
671             ((AbstractMessageAdapter) adapter).assertAccess(write);
672         }
673     }
674 }