View Javadoc

1   /*
2    * $Id: AbstractMessageAdapter.java 11764 2008-05-15 12:12:16Z dirk.olmes $
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.transport;
12  
13  import org.mule.api.ExceptionPayload;
14  import org.mule.api.MuleRuntimeException;
15  import org.mule.api.ThreadSafeAccess;
16  import org.mule.api.config.MuleProperties;
17  import org.mule.api.transport.MessageAdapter;
18  import org.mule.api.transport.PropertyScope;
19  import org.mule.api.transport.UniqueIdNotSupportedException;
20  import org.mule.config.MuleManifest;
21  import org.mule.config.i18n.CoreMessages;
22  import org.mule.util.FileUtils;
23  import org.mule.util.IOUtils;
24  import org.mule.util.StringUtils;
25  import org.mule.util.UUID;
26  
27  import java.io.InputStream;
28  import java.util.Collections;
29  import java.util.Iterator;
30  import java.util.Map;
31  import java.util.Set;
32  
33  import javax.activation.DataHandler;
34  
35  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
36  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentMap;
37  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
38  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicReference;
39  
40  import org.apache.commons.logging.Log;
41  import org.apache.commons.logging.LogFactory;
42  
43  /**
44   * <code>AbstractMessageAdapter</code> provides a base implementation for simple
45   * message types that maybe don't normally allow for meta information, such as a File
46   * or TCP.
47   */
48  public abstract class AbstractMessageAdapter implements MessageAdapter, ThreadSafeAccess
49  {
50      /** logger used by this class */
51      protected static transient Log logger;
52  
53      /** Scoped properties for this message */
54      protected MessagePropertiesContext properties = new MessagePropertiesContext();
55  
56      /** Collection of attachments associatated with this message */
57      protected ConcurrentMap attachments = new ConcurrentHashMap();
58  
59      /** The encoding used by this message. This is usually used when working with String representations of the message payload */
60      protected String encoding = FileUtils.DEFAULT_ENCODING;
61  
62      /** If an excpetion occurs while processing this message an exception payload will be attached here */
63      protected ExceptionPayload exceptionPayload;
64  
65      /** the default UUID for the message. If the underlying transport has the notion of a message id, this uuid will be ignorred */
66      protected String id = UUID.getUUID();
67  
68      // these are transient because serisalisation generates a new instance
69      // so we allow mutation again (and we can't serialize threads anyway)
70      private transient AtomicReference ownerThread = null;
71  
72      private transient AtomicBoolean mutable = null;
73  
74      protected AbstractMessageAdapter()
75      {
76          // usual access for subclasses
77          logger = LogFactory.getLog(getClass());
78      }
79  
80      /**
81       * Creates a message adapter copying values from an existing one
82       * @param template
83       */
84      protected AbstractMessageAdapter(MessageAdapter template)
85      {
86          logger = LogFactory.getLog(getClass());
87          if (null != template)
88          {
89              Iterator propertyNames = template.getPropertyNames().iterator();
90              while (propertyNames.hasNext())
91              {
92                  String key = (String) propertyNames.next();
93                  try
94                  {
95                      setProperty(key, template.getProperty(key));
96                  }
97                  catch (Exception e)
98                  {
99                      throw new MuleRuntimeException(CoreMessages.failedToReadPayload(), e);
100                 }
101             }
102             Iterator attachmentNames = template.getAttachmentNames().iterator();
103             while (attachmentNames.hasNext())
104             {
105                 String key = (String) attachmentNames.next();
106                 try
107                 {
108                     addAttachment(key, template.getAttachment(key));
109                 }
110                 catch (Exception e)
111                 {
112                     throw new MuleRuntimeException(CoreMessages.failedToReadPayload(), e);
113                 }
114             }
115             encoding = template.getEncoding();
116             exceptionPayload = template.getExceptionPayload();
117             
118             try 
119             {
120                 id = template.getUniqueId();
121             }
122             catch (UniqueIdNotSupportedException e)
123             {
124                 // Don't copy the id if it's not supported.
125             }
126         }
127     }
128 
129     //@Override
130     public String toString()
131     {
132         assertAccess(READ);
133         StringBuffer buf = new StringBuffer(120);
134         buf.append(getClass().getName());
135         buf.append("/" + super.toString());
136         buf.append('{');
137         buf.append("id=").append(getUniqueId());
138         buf.append(", payload=").append(getPayload().getClass().getName());
139         buf.append(", correlationId=").append(getCorrelationId());
140         buf.append(", correlationGroup=").append(getCorrelationGroupSize());
141         buf.append(", correlationSeq=").append(getCorrelationSequence());
142         buf.append(", encoding=").append(getEncoding());
143         buf.append(", exceptionPayload=").append(exceptionPayload);
144         if (logger.isDebugEnabled())
145         {
146             buf.append(", properties=").append(properties);
147         }
148         buf.append('}');
149         return buf.toString();
150     }
151 
152     /** {@inheritDoc} */
153     public void addProperties(Map props)
154     {
155         addProperties(props, properties.getDefaultScope());
156         assertAccess(WRITE);
157         if (props != null)
158         {
159             synchronized (props)
160             {
161                 for (Iterator iter = props.entrySet().iterator(); iter.hasNext();)
162                 {
163                     Map.Entry entry = (Map.Entry) iter.next();
164                     setProperty((String) entry.getKey(), entry.getValue());
165                 }
166             }
167         }
168     }
169 
170     /** {@inheritDoc} */
171     public void addProperties(Map props, PropertyScope scope)
172     {
173         assertAccess(WRITE);
174         if (props != null)
175         {
176             synchronized (props)
177             {
178                 for (Iterator iter = props.entrySet().iterator(); iter.hasNext();)
179                 {
180                     Map.Entry entry = (Map.Entry) iter.next();
181                     setProperty((String) entry.getKey(), entry.getValue(), scope);
182                 }
183             }
184         }
185     }
186     /**
187      * A convenience method for extending classes to Set inbound scoped properties on the message
188      * properties that arrive on the inbound message should be set as inbound-scoped properties. These are
189      * read-only
190      * @param props the properties to set
191      * @see org.mule.api.transport.PropertyScope
192      */
193     protected void addInboundProperties(Map props)
194     {
195         properties.addInboundProperties(props);
196     }
197 
198     /** {@inheritDoc} */
199     public void clearProperties()
200     {
201         assertAccess(WRITE);
202         properties.clearProperties();
203     }
204 
205     /** {@inheritDoc} */
206     public Object removeProperty(String key)
207     {
208         assertAccess(WRITE);
209         return properties.removeProperty(key);
210     }
211 
212     /** {@inheritDoc} */
213     public Object getProperty(String key)
214     {
215         assertAccess(READ);
216         return properties.getProperty(key);
217     }
218 
219     /** {@inheritDoc} */
220     public Set getPropertyNames()
221     {
222         assertAccess(READ);
223         return properties.getPropertyNames();
224     }
225 
226     /** {@inheritDoc} */
227     public void setProperty(String key, Object value)
228     {
229         assertAccess(WRITE);
230         if (key != null)
231         {
232             if (value != null)
233             {
234                 properties.setProperty(key, value);
235             }
236             else
237             {
238                 logger.warn("setProperty(key, value) called with null value; removing key: " + key
239                         + "; please report the following stack trace to " + MuleManifest.getDevListEmail(),
240                         new Throwable());
241                 properties.removeProperty(key);
242             }
243         }
244         else
245         {
246             logger.warn("setProperty(key, value) ignored because of null key for object: " + value
247                     + "; please report the following stack trace to " + MuleManifest.getDevListEmail(),
248                     new Throwable());
249         }
250     }
251 
252     /** {@inheritDoc} */
253     public void setProperty(String key, Object value, PropertyScope scope)
254     {
255         assertAccess(WRITE);
256         if (key != null)
257         {
258             if (value != null)
259             {
260                 properties.setProperty(key, value, scope);
261             }
262             else
263             {
264                 logger.warn("setProperty(key, value) called with null value; removing key: " + key
265                         + "; please report the following stack trace to " + MuleManifest.getDevListEmail(),
266                         new Throwable());
267                 properties.removeProperty(key);
268             }
269         }
270         else
271         {
272             logger.warn("setProperty(key, value) ignored because of null key for object: " + value
273                     + "; please report the following stack trace to " + MuleManifest.getDevListEmail(),
274                     new Throwable());
275         }
276     }
277 
278     /** {@inheritDoc} */
279     public String getUniqueId()
280     {
281         assertAccess(READ);
282         return id;
283     }
284 
285     /** {@inheritDoc} */
286     public Object getProperty(String name, Object defaultValue)
287     {
288         assertAccess(READ);
289         return properties.getProperty(name, defaultValue);
290     }
291 
292     /** {@inheritDoc} */
293     public int getIntProperty(String name, int defaultValue)
294     {
295         assertAccess(READ);
296         return properties.getIntProperty(name, defaultValue);
297     }
298 
299     /** {@inheritDoc} */
300     public long getLongProperty(String name, long defaultValue)
301     {
302         assertAccess(READ);
303         return properties.getLongProperty(name, defaultValue);
304     }
305 
306     /** {@inheritDoc} */
307     public double getDoubleProperty(String name, double defaultValue)
308     {
309         assertAccess(READ);
310         return properties.getDoubleProperty(name, defaultValue);
311     }
312 
313     /** {@inheritDoc} */
314     public boolean getBooleanProperty(String name, boolean defaultValue)
315     {
316         assertAccess(READ);
317         return properties.getBooleanProperty(name, defaultValue);
318     }
319 
320     /** {@inheritDoc} */
321     public String getStringProperty(String name, String defaultValue)
322     {
323         assertAccess(READ);
324         return properties.getStringProperty(name, defaultValue);
325     }
326 
327     /** {@inheritDoc} */
328     public void setBooleanProperty(String name, boolean value)
329     {
330         assertAccess(WRITE);
331         setProperty(name, Boolean.valueOf(value));
332     }
333 
334     /** {@inheritDoc} */
335     public void setIntProperty(String name, int value)
336     {
337         assertAccess(WRITE);
338         setProperty(name, new Integer(value));
339     }
340 
341     public void setLongProperty(String name, long value)
342     {
343         assertAccess(WRITE);
344         setProperty(name, new Long(value));
345     }
346 
347     public void setDoubleProperty(String name, double value)
348     {
349         assertAccess(WRITE);
350         setProperty(name, new Double(value));
351     }
352 
353     /** {@inheritDoc} */
354     public void setStringProperty(String name, String value)
355     {
356         assertAccess(WRITE);
357         setProperty(name, value);
358     }
359 
360     /** {@inheritDoc} */
361     public Object getReplyTo()
362     {
363         assertAccess(READ);
364         return getProperty(MuleProperties.MULE_REPLY_TO_PROPERTY);
365     }
366 
367     /** {@inheritDoc} */
368     public void setReplyTo(Object replyTo)
369     {
370         assertAccess(WRITE);
371         if (replyTo != null)
372         {
373             setProperty(MuleProperties.MULE_REPLY_TO_PROPERTY, replyTo);
374         }
375         else
376         {
377             removeProperty(MuleProperties.MULE_REPLY_TO_PROPERTY);
378         }
379     }
380 
381     /** {@inheritDoc} */
382     public String getCorrelationId()
383     {
384         assertAccess(READ);
385         return (String) getProperty(MuleProperties.MULE_CORRELATION_ID_PROPERTY);
386     }
387 
388 
389     /** {@inheritDoc} */
390     public void setCorrelationId(String correlationId)
391     {
392         assertAccess(WRITE);
393         if (StringUtils.isNotBlank(correlationId))
394         {
395             setProperty(MuleProperties.MULE_CORRELATION_ID_PROPERTY, correlationId);
396         }
397         else
398         {
399             removeProperty(MuleProperties.MULE_CORRELATION_ID_PROPERTY);
400         }
401     }
402 
403     /** {@inheritDoc} */
404     public int getCorrelationSequence()
405     {
406         assertAccess(READ);
407         return getIntProperty(MuleProperties.MULE_CORRELATION_SEQUENCE_PROPERTY, -1);
408     }
409 
410     /** {@inheritDoc} */
411     public void setCorrelationSequence(int sequence)
412     {
413         assertAccess(WRITE);
414         setIntProperty(MuleProperties.MULE_CORRELATION_SEQUENCE_PROPERTY, sequence);
415     }
416 
417     /** {@inheritDoc} */
418     public int getCorrelationGroupSize()
419     {
420         assertAccess(READ);
421         return getIntProperty(MuleProperties.MULE_CORRELATION_GROUP_SIZE_PROPERTY, -1);
422     }
423 
424     /** {@inheritDoc} */
425     public void setCorrelationGroupSize(int size)
426     {
427         assertAccess(WRITE);
428         setIntProperty(MuleProperties.MULE_CORRELATION_GROUP_SIZE_PROPERTY, size);
429     }
430 
431     public ExceptionPayload getExceptionPayload()
432     {
433         assertAccess(READ);
434         return exceptionPayload;
435     }
436 
437     /** {@inheritDoc} */
438     public void setExceptionPayload(ExceptionPayload payload)
439     {
440         assertAccess(WRITE);
441         exceptionPayload = payload;
442     }
443 
444     /** {@inheritDoc} */
445     public void addAttachment(String name, DataHandler dataHandler) throws Exception
446     {
447         assertAccess(WRITE);
448         attachments.put(name, dataHandler);
449     }
450 
451     /** {@inheritDoc} */
452     public void removeAttachment(String name) throws Exception
453     {
454         assertAccess(WRITE);
455         attachments.remove(name);
456     }
457 
458     /** {@inheritDoc} */
459     public DataHandler getAttachment(String name)
460     {
461         assertAccess(READ);
462         return (DataHandler) attachments.get(name);
463     }
464 
465     /** {@inheritDoc} */
466     public Set getAttachmentNames()
467     {
468         assertAccess(READ);
469         return Collections.unmodifiableSet(attachments.keySet());
470     }
471 
472     /** {@inheritDoc} */
473     public String getEncoding()
474     {
475         assertAccess(READ);
476         return encoding;
477     }
478 
479     /** {@inheritDoc} */
480     public void setEncoding(String encoding)
481     {
482         assertAccess(WRITE);
483         this.encoding = encoding;
484     }
485 
486     /** {@inheritDoc} */
487     public void release()
488     {
489         //TODO handle other stream types
490         if(getPayload() instanceof InputStream)
491         {
492             IOUtils.closeQuietly((InputStream)getPayload());
493         }
494         properties.clearProperties();
495         attachments.clear();
496     }
497 
498     ///////////////////////// ThreadSafeAccess impl /////////////////////////////////////
499 
500     /** {@inheritDoc} */
501     public void assertAccess(boolean write)
502     {
503         if (AccessControl.isAssertMessageAccess())
504         {
505             initAccessControl();
506             setOwner();
507             checkMutable(write);
508         }
509     }
510 
511     private void setOwner()
512     {
513         if (null == ownerThread.get())
514         {
515             ownerThread.compareAndSet(null, Thread.currentThread());
516         }
517     }
518 
519     private void checkMutable(boolean write)
520     {
521 
522         // IF YOU SEE AN EXCEPTION THAT IS RAISED FROM WITHIN THIS CODE
523         // ============================================================
524         //
525         // First, understand that the exception here is not the "real" problem.  These exceptions
526         // give early warning of a much more serious issue that results in unreliable and unpredictable
527         // code - more than one thread is attempting to change the contents of a message.
528         //
529         // Having said that, you can disable these exceptions by defining
530         // MuleProperties.MULE_THREAD_UNSAFE_MESSAGES_PROPERTY (mule.disable.threadsafemessages)
531         // (ie by adding -Dmule.disable.threadsafemessages=true to the java command line).
532         //
533         // To remove the underlying cause, however, you probably need to do one of:
534         //
535         // - make sure that the message adapter you are using correclty implements the
536         // ThreadSafeAccess interface
537         //
538         // - make sure that dispatcher and receiver classes copy ThreadSafeAccess instances when
539         // they are passed between threads
540 
541         Thread currentThread = Thread.currentThread();
542         if (currentThread.equals(ownerThread.get()))
543         {
544             if (write && !mutable.get())
545             {
546                 if (isDisabled())
547                 {
548                     logger.warn("Writing to immutable message (exception disabled)");
549                 }
550                 else
551                 {
552                     throw newException("Cannot write to immutable message");
553                 }
554             }
555         }
556         else
557         {
558             if (write)
559             {
560                 if (isDisabled())
561                 {
562                     logger.warn("Non-owner writing to message (exception disabled)");
563                 }
564                 else
565                 {
566                     throw newException("Only owner thread can write to message: "
567                             + ownerThread.get() + "/" + Thread.currentThread());
568                 }
569             }
570             else
571             {
572                 // access by another thread
573                 mutable.set(false);
574             }
575         }
576     }
577 
578     protected IllegalStateException newException(String message)
579     {
580         IllegalStateException exception = new IllegalStateException(message);
581         logger.warn("Message access violation", exception);
582         return exception;
583     }
584 
585     protected boolean isDisabled()
586     {
587         return !AccessControl.isFailOnMessageScribbling();
588     }
589 
590     private synchronized void initAccessControl()
591     {
592         if (null == ownerThread)
593         {
594             ownerThread = new AtomicReference();
595         }
596         if (null == mutable)
597         {
598             mutable = new AtomicBoolean(true);
599         }
600     }
601 
602     /** {@inheritDoc} */
603     public synchronized void resetAccessControl()
604     {
605         // just reset the internal state here as this method is explicitly intended not to
606         // be used from the outside
607         if (ownerThread != null)
608         {
609             ownerThread.set(null);
610         }
611         if (mutable != null)
612         {
613             mutable.set(true);
614         }
615     }
616 
617     /** {@inheritDoc} */
618     public ThreadSafeAccess newThreadCopy()
619     {
620         if (logger.isInfoEnabled())
621         {
622             logger.info("The newThreadCopy method in AbstractMessageAdapter is being used directly. "
623                     + "This code may be susceptible to 'scribbling' issues with messages. "
624                     + "Please consider implementing the ThreadSafeAccess interface in the message adapter.");
625         }
626         return this;
627     }
628 
629 }