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