Coverage Report - org.mule.transport.AbstractMessageAdapter
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractMessageAdapter
72%
151/211
72%
42/58
1.694
 
 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  1032
     protected MessagePropertiesContext properties = new MessagePropertiesContext();
 55  
 
 56  
     /** Collection of attachments associatated with this message */
 57  1032
     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  1032
     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  1032
     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  1032
     private transient AtomicReference ownerThread = null;
 71  
 
 72  1032
     private transient AtomicBoolean mutable = null;
 73  
 
 74  
     protected AbstractMessageAdapter()
 75  714
     {
 76  
         // usual access for subclasses
 77  714
         logger = LogFactory.getLog(getClass());
 78  714
     }
 79  
 
 80  
     /**
 81  
      * Creates a message adapter copying values from an existing one
 82  
      * @param template
 83  
      */
 84  
     protected AbstractMessageAdapter(MessageAdapter template)
 85  318
     {
 86  318
         logger = LogFactory.getLog(getClass());
 87  318
         if (null != template)
 88  
         {
 89  318
             Iterator propertyNames = template.getPropertyNames().iterator();
 90  420
             while (propertyNames.hasNext())
 91  
             {
 92  102
                 String key = (String) propertyNames.next();
 93  
                 try
 94  
                 {
 95  102
                     setProperty(key, template.getProperty(key));
 96  
                 }
 97  0
                 catch (Exception e)
 98  
                 {
 99  0
                     throw new MuleRuntimeException(CoreMessages.failedToReadPayload(), e);
 100  102
                 }
 101  102
             }
 102  318
             Iterator attachmentNames = template.getAttachmentNames().iterator();
 103  318
             while (attachmentNames.hasNext())
 104  
             {
 105  0
                 String key = (String) attachmentNames.next();
 106  
                 try
 107  
                 {
 108  0
                     addAttachment(key, template.getAttachment(key));
 109  
                 }
 110  0
                 catch (Exception e)
 111  
                 {
 112  0
                     throw new MuleRuntimeException(CoreMessages.failedToReadPayload(), e);
 113  0
                 }
 114  0
             }
 115  318
             encoding = template.getEncoding();
 116  318
             exceptionPayload = template.getExceptionPayload();
 117  
             
 118  
             try 
 119  
             {
 120  318
                 id = template.getUniqueId();
 121  
             }
 122  0
             catch (UniqueIdNotSupportedException e)
 123  
             {
 124  
                 // Don't copy the id if it's not supported.
 125  318
             }
 126  
         }
 127  318
     }
 128  
 
 129  
     //@Override
 130  
     public String toString()
 131  
     {
 132  8
         assertAccess(READ);
 133  8
         StringBuffer buf = new StringBuffer(120);
 134  8
         buf.append(getClass().getName());
 135  8
         buf.append("/" + super.toString());
 136  8
         buf.append('{');
 137  8
         buf.append("id=").append(getUniqueId());
 138  8
         buf.append(", payload=").append(getPayload().getClass().getName());
 139  8
         buf.append(", correlationId=").append(getCorrelationId());
 140  8
         buf.append(", correlationGroup=").append(getCorrelationGroupSize());
 141  8
         buf.append(", correlationSeq=").append(getCorrelationSequence());
 142  8
         buf.append(", encoding=").append(getEncoding());
 143  8
         buf.append(", exceptionPayload=").append(exceptionPayload);
 144  8
         if (logger.isDebugEnabled())
 145  
         {
 146  0
             buf.append(", properties=").append(properties);
 147  
         }
 148  8
         buf.append('}');
 149  8
         return buf.toString();
 150  
     }
 151  
 
 152  
     /** {@inheritDoc} */
 153  
     public void addProperties(Map props)
 154  
     {
 155  708
         addProperties(props, properties.getDefaultScope());
 156  708
         assertAccess(WRITE);
 157  708
         if (props != null)
 158  
         {
 159  352
             synchronized (props)
 160  
             {
 161  352
                 for (Iterator iter = props.entrySet().iterator(); iter.hasNext();)
 162  
                 {
 163  124
                     Map.Entry entry = (Map.Entry) iter.next();
 164  124
                     setProperty((String) entry.getKey(), entry.getValue());
 165  124
                 }
 166  352
             }
 167  
         }
 168  708
     }
 169  
 
 170  
     /** {@inheritDoc} */
 171  
     public void addProperties(Map props, PropertyScope scope)
 172  
     {
 173  708
         assertAccess(WRITE);
 174  708
         if (props != null)
 175  
         {
 176  352
             synchronized (props)
 177  
             {
 178  352
                 for (Iterator iter = props.entrySet().iterator(); iter.hasNext();)
 179  
                 {
 180  124
                     Map.Entry entry = (Map.Entry) iter.next();
 181  124
                     setProperty((String) entry.getKey(), entry.getValue(), scope);
 182  124
                 }
 183  352
             }
 184  
         }
 185  708
     }
 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  0
         properties.addInboundProperties(props);
 196  0
     }
 197  
 
 198  
     /** {@inheritDoc} */
 199  
     public void clearProperties()
 200  
     {
 201  0
         assertAccess(WRITE);
 202  0
         properties.clearProperties();
 203  0
     }
 204  
 
 205  
     /** {@inheritDoc} */
 206  
     public Object removeProperty(String key)
 207  
     {
 208  94
         assertAccess(WRITE);
 209  94
         return properties.removeProperty(key);
 210  
     }
 211  
 
 212  
     /** {@inheritDoc} */
 213  
     public Object getProperty(String key)
 214  
     {
 215  760
         assertAccess(READ);
 216  760
         return properties.getProperty(key);
 217  
     }
 218  
 
 219  
     /** {@inheritDoc} */
 220  
     public Set getPropertyNames()
 221  
     {
 222  932
         assertAccess(READ);
 223  932
         return properties.getPropertyNames();
 224  
     }
 225  
 
 226  
     /** {@inheritDoc} */
 227  
     public void setProperty(String key, Object value)
 228  
     {
 229  638
         assertAccess(WRITE);
 230  638
         if (key != null)
 231  
         {
 232  638
             if (value != null)
 233  
             {
 234  638
                 properties.setProperty(key, value);
 235  
             }
 236  
             else
 237  
             {
 238  0
                 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  0
                 properties.removeProperty(key);
 242  
             }
 243  
         }
 244  
         else
 245  
         {
 246  0
             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  638
     }
 251  
 
 252  
     /** {@inheritDoc} */
 253  
     public void setProperty(String key, Object value, PropertyScope scope)
 254  
     {
 255  124
         assertAccess(WRITE);
 256  124
         if (key != null)
 257  
         {
 258  124
             if (value != null)
 259  
             {
 260  124
                 properties.setProperty(key, value, scope);
 261  
             }
 262  
             else
 263  
             {
 264  0
                 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  0
                 properties.removeProperty(key);
 268  
             }
 269  
         }
 270  
         else
 271  
         {
 272  0
             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  124
     }
 277  
 
 278  
     /** {@inheritDoc} */
 279  
     public String getUniqueId()
 280  
     {
 281  0
         assertAccess(READ);
 282  0
         return id;
 283  
     }
 284  
 
 285  
     /** {@inheritDoc} */
 286  
     public Object getProperty(String name, Object defaultValue)
 287  
     {
 288  0
         assertAccess(READ);
 289  0
         return properties.getProperty(name, defaultValue);
 290  
     }
 291  
 
 292  
     /** {@inheritDoc} */
 293  
     public int getIntProperty(String name, int defaultValue)
 294  
     {
 295  96
         assertAccess(READ);
 296  96
         return properties.getIntProperty(name, defaultValue);
 297  
     }
 298  
 
 299  
     /** {@inheritDoc} */
 300  
     public long getLongProperty(String name, long defaultValue)
 301  
     {
 302  0
         assertAccess(READ);
 303  0
         return properties.getLongProperty(name, defaultValue);
 304  
     }
 305  
 
 306  
     /** {@inheritDoc} */
 307  
     public double getDoubleProperty(String name, double defaultValue)
 308  
     {
 309  0
         assertAccess(READ);
 310  0
         return properties.getDoubleProperty(name, defaultValue);
 311  
     }
 312  
 
 313  
     /** {@inheritDoc} */
 314  
     public boolean getBooleanProperty(String name, boolean defaultValue)
 315  
     {
 316  0
         assertAccess(READ);
 317  0
         return properties.getBooleanProperty(name, defaultValue);
 318  
     }
 319  
 
 320  
     /** {@inheritDoc} */
 321  
     public String getStringProperty(String name, String defaultValue)
 322  
     {
 323  0
         assertAccess(READ);
 324  0
         return properties.getStringProperty(name, defaultValue);
 325  
     }
 326  
 
 327  
     /** {@inheritDoc} */
 328  
     public void setBooleanProperty(String name, boolean value)
 329  
     {
 330  0
         assertAccess(WRITE);
 331  0
         setProperty(name, Boolean.valueOf(value));
 332  0
     }
 333  
 
 334  
     /** {@inheritDoc} */
 335  
     public void setIntProperty(String name, int value)
 336  
     {
 337  124
         assertAccess(WRITE);
 338  124
         setProperty(name, new Integer(value));
 339  124
     }
 340  
 
 341  
     public void setLongProperty(String name, long value)
 342  
     {
 343  0
         assertAccess(WRITE);
 344  0
         setProperty(name, new Long(value));
 345  0
     }
 346  
 
 347  
     public void setDoubleProperty(String name, double value)
 348  
     {
 349  0
         assertAccess(WRITE);
 350  0
         setProperty(name, new Double(value));
 351  0
     }
 352  
 
 353  
     /** {@inheritDoc} */
 354  
     public void setStringProperty(String name, String value)
 355  
     {
 356  0
         assertAccess(WRITE);
 357  0
         setProperty(name, value);
 358  0
     }
 359  
 
 360  
     /** {@inheritDoc} */
 361  
     public Object getReplyTo()
 362  
     {
 363  12
         assertAccess(READ);
 364  12
         return getProperty(MuleProperties.MULE_REPLY_TO_PROPERTY);
 365  
     }
 366  
 
 367  
     /** {@inheritDoc} */
 368  
     public void setReplyTo(Object replyTo)
 369  
     {
 370  12
         assertAccess(WRITE);
 371  12
         if (replyTo != null)
 372  
         {
 373  4
             setProperty(MuleProperties.MULE_REPLY_TO_PROPERTY, replyTo);
 374  
         }
 375  
         else
 376  
         {
 377  8
             removeProperty(MuleProperties.MULE_REPLY_TO_PROPERTY);
 378  
         }
 379  12
     }
 380  
 
 381  
     /** {@inheritDoc} */
 382  
     public String getCorrelationId()
 383  
     {
 384  266
         assertAccess(READ);
 385  266
         return (String) getProperty(MuleProperties.MULE_CORRELATION_ID_PROPERTY);
 386  
     }
 387  
 
 388  
 
 389  
     /** {@inheritDoc} */
 390  
     public void setCorrelationId(String correlationId)
 391  
     {
 392  104
         assertAccess(WRITE);
 393  104
         if (StringUtils.isNotBlank(correlationId))
 394  
         {
 395  104
             setProperty(MuleProperties.MULE_CORRELATION_ID_PROPERTY, correlationId);
 396  
         }
 397  
         else
 398  
         {
 399  0
             removeProperty(MuleProperties.MULE_CORRELATION_ID_PROPERTY);
 400  
         }
 401  104
     }
 402  
 
 403  
     /** {@inheritDoc} */
 404  
     public int getCorrelationSequence()
 405  
     {
 406  20
         assertAccess(READ);
 407  20
         return getIntProperty(MuleProperties.MULE_CORRELATION_SEQUENCE_PROPERTY, -1);
 408  
     }
 409  
 
 410  
     /** {@inheritDoc} */
 411  
     public void setCorrelationSequence(int sequence)
 412  
     {
 413  48
         assertAccess(WRITE);
 414  48
         setIntProperty(MuleProperties.MULE_CORRELATION_SEQUENCE_PROPERTY, sequence);
 415  48
     }
 416  
 
 417  
     /** {@inheritDoc} */
 418  
     public int getCorrelationGroupSize()
 419  
     {
 420  64
         assertAccess(READ);
 421  64
         return getIntProperty(MuleProperties.MULE_CORRELATION_GROUP_SIZE_PROPERTY, -1);
 422  
     }
 423  
 
 424  
     /** {@inheritDoc} */
 425  
     public void setCorrelationGroupSize(int size)
 426  
     {
 427  76
         assertAccess(WRITE);
 428  76
         setIntProperty(MuleProperties.MULE_CORRELATION_GROUP_SIZE_PROPERTY, size);
 429  76
     }
 430  
 
 431  
     public ExceptionPayload getExceptionPayload()
 432  
     {
 433  662
         assertAccess(READ);
 434  662
         return exceptionPayload;
 435  
     }
 436  
 
 437  
     /** {@inheritDoc} */
 438  
     public void setExceptionPayload(ExceptionPayload payload)
 439  
     {
 440  22
         assertAccess(WRITE);
 441  20
         exceptionPayload = payload;
 442  20
     }
 443  
 
 444  
     /** {@inheritDoc} */
 445  
     public void addAttachment(String name, DataHandler dataHandler) throws Exception
 446  
     {
 447  36
         assertAccess(WRITE);
 448  36
         attachments.put(name, dataHandler);
 449  36
     }
 450  
 
 451  
     /** {@inheritDoc} */
 452  
     public void removeAttachment(String name) throws Exception
 453  
     {
 454  0
         assertAccess(WRITE);
 455  0
         attachments.remove(name);
 456  0
     }
 457  
 
 458  
     /** {@inheritDoc} */
 459  
     public DataHandler getAttachment(String name)
 460  
     {
 461  32
         assertAccess(READ);
 462  32
         return (DataHandler) attachments.get(name);
 463  
     }
 464  
 
 465  
     /** {@inheritDoc} */
 466  
     public Set getAttachmentNames()
 467  
     {
 468  952
         assertAccess(READ);
 469  952
         return Collections.unmodifiableSet(attachments.keySet());
 470  
     }
 471  
 
 472  
     /** {@inheritDoc} */
 473  
     public String getEncoding()
 474  
     {
 475  988
         assertAccess(READ);
 476  988
         return encoding;
 477  
     }
 478  
 
 479  
     /** {@inheritDoc} */
 480  
     public void setEncoding(String encoding)
 481  
     {
 482  320
         assertAccess(WRITE);
 483  320
         this.encoding = encoding;
 484  320
     }
 485  
 
 486  
     /** {@inheritDoc} */
 487  
     public void release()
 488  
     {
 489  
         //TODO handle other stream types
 490  0
         if(getPayload() instanceof InputStream)
 491  
         {
 492  0
             IOUtils.closeQuietly((InputStream)getPayload());
 493  
         }
 494  0
         properties.clearProperties();
 495  0
         attachments.clear();
 496  0
     }
 497  
 
 498  
     ///////////////////////// ThreadSafeAccess impl /////////////////////////////////////
 499  
 
 500  
     /** {@inheritDoc} */
 501  
     public void assertAccess(boolean write)
 502  
     {
 503  8096
         if (AccessControl.isAssertMessageAccess())
 504  
         {
 505  8096
             initAccessControl();
 506  8096
             setOwner();
 507  8096
             checkMutable(write);
 508  
         }
 509  8070
     }
 510  
 
 511  
     private void setOwner()
 512  
     {
 513  8096
         if (null == ownerThread.get())
 514  
         {
 515  1732
             ownerThread.compareAndSet(null, Thread.currentThread());
 516  
         }
 517  8096
     }
 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  8096
         Thread currentThread = Thread.currentThread();
 542  8096
         if (currentThread.equals(ownerThread.get()))
 543  
         {
 544  7998
             if (write && !mutable.get())
 545  
             {
 546  0
                 if (isDisabled())
 547  
                 {
 548  0
                     logger.warn("Writing to immutable message (exception disabled)");
 549  
                 }
 550  
                 else
 551  
                 {
 552  0
                     throw newException("Cannot write to immutable message");
 553  
                 }
 554  
             }
 555  
         }
 556  
         else
 557  
         {
 558  98
             if (write)
 559  
             {
 560  28
                 if (isDisabled())
 561  
                 {
 562  2
                     logger.warn("Non-owner writing to message (exception disabled)");
 563  
                 }
 564  
                 else
 565  
                 {
 566  26
                     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  70
                 mutable.set(false);
 574  
             }
 575  
         }
 576  8070
     }
 577  
 
 578  
     protected IllegalStateException newException(String message)
 579  
     {
 580  26
         IllegalStateException exception = new IllegalStateException(message);
 581  26
         logger.warn("Message access violation", exception);
 582  26
         return exception;
 583  
     }
 584  
 
 585  
     protected boolean isDisabled()
 586  
     {
 587  28
         return !AccessControl.isFailOnMessageScribbling();
 588  
     }
 589  
 
 590  
     private synchronized void initAccessControl()
 591  
     {
 592  8096
         if (null == ownerThread)
 593  
         {
 594  1036
             ownerThread = new AtomicReference();
 595  
         }
 596  8096
         if (null == mutable)
 597  
         {
 598  1036
             mutable = new AtomicBoolean(true);
 599  
         }
 600  8096
     }
 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  1582
         if (ownerThread != null)
 608  
         {
 609  1322
             ownerThread.set(null);
 610  
         }
 611  1582
         if (mutable != null)
 612  
         {
 613  1322
             mutable.set(true);
 614  
         }
 615  1582
     }
 616  
 
 617  
     /** {@inheritDoc} */
 618  
     public ThreadSafeAccess newThreadCopy()
 619  
     {
 620  0
         if (logger.isInfoEnabled())
 621  
         {
 622  0
             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  0
         return this;
 627  
     }
 628  
 
 629  
 }