Coverage Report - org.mule.transformer.AbstractTransformer
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractTransformer
0%
0/158
0%
0/80
0
 
 1  
 /*
 2  
  * $Id: AbstractTransformer.java 20080 2010-11-05 04:00:22Z dfeist $
 3  
  * --------------------------------------------------------------------------------------
 4  
  * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.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.transformer;
 12  
 
 13  
 import org.mule.api.MuleContext;
 14  
 import org.mule.api.MuleEvent;
 15  
 import org.mule.api.MuleException;
 16  
 import org.mule.api.MuleMessage;
 17  
 import org.mule.api.config.MuleProperties;
 18  
 import org.mule.api.context.notification.MuleContextNotificationListener;
 19  
 import org.mule.api.endpoint.ImmutableEndpoint;
 20  
 import org.mule.api.lifecycle.InitialisationException;
 21  
 import org.mule.api.transformer.DataType;
 22  
 import org.mule.api.transformer.Transformer;
 23  
 import org.mule.api.transformer.TransformerException;
 24  
 import org.mule.api.transformer.TransformerMessagingException;
 25  
 import org.mule.config.i18n.CoreMessages;
 26  
 import org.mule.config.i18n.Message;
 27  
 import org.mule.context.notification.MuleContextNotification;
 28  
 import org.mule.context.notification.NotificationException;
 29  
 import org.mule.transformer.types.DataTypeFactory;
 30  
 import org.mule.transformer.types.SimpleDataType;
 31  
 import org.mule.transport.NullPayload;
 32  
 import org.mule.util.ClassUtils;
 33  
 import org.mule.util.StringMessageUtils;
 34  
 import org.mule.util.StringUtils;
 35  
 
 36  
 import java.io.InputStream;
 37  
 import java.util.ArrayList;
 38  
 import java.util.Collections;
 39  
 import java.util.List;
 40  
 
 41  
 import javax.activation.MimeType;
 42  
 import javax.activation.MimeTypeParseException;
 43  
 import javax.xml.transform.stream.StreamSource;
 44  
 
 45  
 import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList;
 46  
 
 47  
 import org.apache.commons.logging.Log;
 48  
 import org.apache.commons.logging.LogFactory;
 49  
 
 50  
 /**
 51  
  * <code>AbstractTransformer</code> is a base class for all transformers.
 52  
  * Transformations transform one object into another.
 53  
  */
 54  
 
 55  0
 public abstract class AbstractTransformer implements Transformer, MuleContextNotificationListener<MuleContextNotification>
 56  
 {
 57  0
     public static final DataType<MuleMessage> MULE_MESSAGE_DATA_TYPE = new SimpleDataType<MuleMessage>(MuleMessage.class);
 58  
 
 59  
     protected MuleContext muleContext;
 60  
 
 61  0
     protected final Log logger = LogFactory.getLog(getClass());
 62  
 
 63  
     /**
 64  
      * The return type that will be returned by the {@link #transform} method is
 65  
      * called
 66  
      */
 67  0
     protected DataType<?> returnType = new SimpleDataType<Object>(Object.class);
 68  
 
 69  
     /**
 70  
      * The name that identifies this transformer. If none is set the class name of
 71  
      * the transformer is used
 72  
      */
 73  0
     protected String name = null;
 74  
 
 75  
     /**
 76  
      * The endpoint that this transformer instance is configured on
 77  
      */
 78  0
     protected ImmutableEndpoint endpoint = null;
 79  
 
 80  
     /**
 81  
      * A list of supported Class types that the source payload passed into this
 82  
      * transformer
 83  
      */
 84  0
     @SuppressWarnings("unchecked")
 85  
     protected final List<DataType<?>> sourceTypes = new CopyOnWriteArrayList/*<DataType>*/();
 86  
 
 87  
     /**
 88  
      * Determines whether the transformer will throw an exception if the message
 89  
      * passed is is not supported
 90  
      */
 91  0
     private boolean ignoreBadInput = false;
 92  
 
 93  
     /**
 94  
      * Allows a transformer to return a null result
 95  
      */
 96  0
     private boolean allowNullReturn = false;
 97  
 
 98  
     /*
 99  
      *  Mime type and encoding for transformer output
 100  
      */
 101  
     protected String mimeType;
 102  
     protected String encoding;
 103  
 
 104  
     /**
 105  
      * default constructor required for discovery
 106  
      */
 107  
     public AbstractTransformer()
 108  
     {
 109  0
         super();
 110  0
     }
 111  
 
 112  
     public MuleEvent process(MuleEvent event) throws MuleException
 113  
     {
 114  0
         if (event != null && event.getMessage() != null)
 115  
         {
 116  
             try
 117  
             {
 118  0
                 event.getMessage().applyTransformers(event, this);
 119  
             }
 120  0
             catch (TransformerException e)
 121  
             {
 122  0
                 throw new TransformerMessagingException(e.getI18nMessage(), event, this, e);
 123  0
             }
 124  
         }
 125  0
         return event;
 126  
     }
 127  
     
 128  
     protected Object checkReturnClass(Object object) throws TransformerException
 129  
     {
 130  
         //Null is a valid return type
 131  0
         if(object==null || object instanceof NullPayload && isAllowNullReturn())
 132  
         {
 133  0
             return object;
 134  
         }
 135  
 
 136  0
         if (returnType != null)
 137  
         {
 138  0
             DataType<?> dt = DataTypeFactory.create(object.getClass());
 139  0
             if (!returnType.isCompatibleWith(dt))
 140  
             {
 141  0
                 throw new TransformerException(
 142  
                         CoreMessages.transformUnexpectedType(dt, returnType),
 143  
                         this);
 144  
             }
 145  
         }
 146  
 
 147  0
         if (logger.isDebugEnabled())
 148  
         {
 149  0
             logger.debug("The transformed object is of expected type. Type is: " +
 150  
                     ClassUtils.getSimpleName(object.getClass()));
 151  
         }
 152  
 
 153  0
         return object;
 154  
     }
 155  
 
 156  
     /**
 157  
      * Register a supported data type with this transformer.  The will allow objects that match this data type to be
 158  
      * transformed by this transformer.
 159  
      *
 160  
      * @param aClass the source type to allow
 161  
      * @deprecated use registerSourceType(DataType)
 162  
      */
 163  
     @Deprecated
 164  
     protected void registerSourceType(Class<?> aClass)
 165  
     {
 166  0
         registerSourceType(new SimpleDataType<Object>(aClass));
 167  0
     }
 168  
 
 169  
     /**
 170  
      * Unregister a supported source type from this transformer
 171  
      *
 172  
      * @param aClass the type to remove
 173  
      * @deprecated use unregisterSourceType(DataType)
 174  
      */
 175  
     @Deprecated
 176  
     protected void unregisterSourceType(Class<?> aClass)
 177  
     {
 178  0
         unregisterSourceType(new SimpleDataType<Object>(aClass));
 179  0
     }
 180  
 
 181  
     /**
 182  
      * Register a supported data type with this transformer.  The will allow objects that match this data type to be
 183  
      * transformed by this transformer.
 184  
      *
 185  
      * @param dataType the source type to allow
 186  
      */
 187  
     protected void registerSourceType(DataType<?> dataType)
 188  
     {
 189  0
         if (!sourceTypes.contains(dataType))
 190  
         {
 191  0
             sourceTypes.add(dataType);
 192  
 
 193  0
             if (dataType.getType().equals(Object.class))
 194  
             {
 195  0
                 logger.debug("java.lang.Object has been added as source type for this transformer, there will be no source type checking performed");
 196  
             }
 197  
         }
 198  0
     }
 199  
 
 200  
     /**
 201  
      * Unregister a supported source type from this transformer
 202  
      *
 203  
      * @param dataType the type to remove
 204  
      */
 205  
     protected void unregisterSourceType(DataType<?> dataType)
 206  
     {
 207  0
         sourceTypes.remove(dataType);
 208  0
     }
 209  
 
 210  
     /**
 211  
      * @return transformer name
 212  
      */
 213  
     public String getName()
 214  
     {
 215  0
         if (name == null)
 216  
         {
 217  0
             name = this.generateTransformerName();
 218  
         }
 219  0
         return name;
 220  
     }
 221  
 
 222  
     /**
 223  
      * @param string
 224  
      */
 225  
     public void setName(String string)
 226  
     {
 227  0
         if (string == null)
 228  
         {
 229  0
             string = ClassUtils.getSimpleName(this.getClass());
 230  
         }
 231  
 
 232  0
         logger.debug("Setting transformer name to: " + string);
 233  0
         name = string;
 234  0
     }
 235  
 
 236  
     @Deprecated
 237  
     public Class<?> getReturnClass()
 238  
     {
 239  0
         return returnType.getType();
 240  
     }
 241  
 
 242  
     public void setReturnDataType(DataType<?> type)
 243  
     {
 244  0
         this.returnType = type.cloneDataType();
 245  0
         this.encoding = type.getEncoding();
 246  0
         this.mimeType = type.getMimeType();
 247  0
     }
 248  
 
 249  
     public DataType<?> getReturnDataType()
 250  
     {
 251  0
         return returnType;
 252  
     }
 253  
 
 254  
     @Deprecated
 255  
     public void setReturnClass(Class<?> newClass)
 256  
     {
 257  0
         DataType<?> tempReturnType = new SimpleDataType<Object>(newClass);
 258  0
         tempReturnType.setMimeType(mimeType);
 259  0
         tempReturnType.setEncoding(encoding);
 260  0
         setReturnDataType(tempReturnType);
 261  0
     }
 262  
 
 263  
     public void setMimeType(String mimeType) throws MimeTypeParseException
 264  
     {
 265  0
         if (mimeType == null)
 266  
         {
 267  0
             this.mimeType = null;
 268  
         }
 269  
         else
 270  
         {
 271  0
             MimeType mt = new MimeType(mimeType);
 272  0
             this.mimeType = mt.getPrimaryType() + "/" + mt.getSubType();
 273  
         }
 274  0
         if (returnType != null)
 275  
         {
 276  0
             returnType.setMimeType(mimeType);
 277  
         }
 278  0
     }
 279  
 
 280  
     public String getMimeType()
 281  
     {
 282  0
         return mimeType;
 283  
     }
 284  
 
 285  
     public String getEncoding()
 286  
     {
 287  0
         return encoding;
 288  
     }
 289  
 
 290  
     public void setEncoding(String encoding)
 291  
     {
 292  0
         this.encoding = encoding;
 293  0
         if (returnType != null)
 294  
         {
 295  0
             returnType.setEncoding(encoding);
 296  
         }
 297  0
     }
 298  
 
 299  
     public boolean isAllowNullReturn()
 300  
     {
 301  0
         return allowNullReturn;
 302  
     }
 303  
 
 304  
     public void setAllowNullReturn(boolean allowNullReturn)
 305  
     {
 306  0
         this.allowNullReturn = allowNullReturn;
 307  0
     }
 308  
 
 309  
     @Deprecated
 310  
     public boolean isSourceTypeSupported(Class<?> aClass)
 311  
     {
 312  0
         return isSourceDataTypeSupported(DataTypeFactory.create(aClass), false);
 313  
     }
 314  
 
 315  
     public boolean isSourceDataTypeSupported(DataType<?> dataType)
 316  
     {
 317  0
         return isSourceDataTypeSupported(dataType, false);
 318  
     }
 319  
 
 320  
     /**
 321  
      * Determines whether that data type passed in is supported by this transformer
 322  
      *
 323  
      * @param aClass     the type to check against
 324  
      * @param exactMatch if the source type on this transformer is open (can be anything) it will return true unless an
 325  
      *                   exact match is requested using this flag
 326  
      * @return true if the source type is supported by this transformer, false otherwise
 327  
      * @deprecated use {@link #isSourceDataTypeSupported(org.mule.api.transformer.DataType, boolean)}
 328  
      */
 329  
     @Deprecated
 330  
     public boolean isSourceTypeSupported(Class<MuleMessage> aClass, boolean exactMatch)
 331  
     {
 332  0
         return isSourceDataTypeSupported(new SimpleDataType<MuleMessage>(aClass), exactMatch);
 333  
     }
 334  
 
 335  
     /**
 336  
      * Determines whether that data type passed in is supported by this transformer
 337  
      *
 338  
      * @param dataType   the type to check against
 339  
      * @param exactMatch if set to true, this method will look for an exact match to the data type, if false it will look
 340  
      *                   for a compatible data type.
 341  
      * @return true if the source type is supported by this transformer, false otherwise
 342  
      */
 343  
     public boolean isSourceDataTypeSupported(DataType<?> dataType, boolean exactMatch)
 344  
     {
 345  0
         int numTypes = sourceTypes.size();
 346  
 
 347  0
         if (numTypes == 0)
 348  
         {
 349  0
             return !exactMatch;
 350  
         }
 351  
 
 352  0
         for (DataType<?> sourceType : sourceTypes)
 353  
         {
 354  0
             if (exactMatch)
 355  
             {
 356  0
                 if (sourceType.equals(dataType))
 357  
                 {
 358  0
                     return true;
 359  
                 }
 360  
             }
 361  
             else
 362  
             {
 363  0
                 if (sourceType.isCompatibleWith(dataType))
 364  
                 {
 365  0
                     return true;
 366  
                 }
 367  
             }
 368  
         }
 369  0
         return false;
 370  
     }
 371  
 
 372  
     public final Object transform(Object src) throws TransformerException
 373  
     {
 374  0
         return transform(src, getEncoding(src));
 375  
     }
 376  
 
 377  
     public Object transform(Object src, String enc) throws TransformerException
 378  
     {
 379  0
         Object payload = src;
 380  0
         if (src instanceof MuleMessage)
 381  
         {
 382  0
             MuleMessage message = (MuleMessage) src;
 383  0
             if ((!isSourceDataTypeSupported(MULE_MESSAGE_DATA_TYPE, true) &&
 384  
                  !(this instanceof AbstractMessageTransformer)))
 385  
             {
 386  0
                 src = ((MuleMessage) src).getPayload();
 387  0
                 payload = message.getPayload();
 388  
             }
 389  
         }
 390  
 
 391  0
         DataType<?> sourceType = DataTypeFactory.create(payload.getClass());
 392  
         //Once we support mime types, it should be possible to do this since we'll be able to discern the difference
 393  
         //between objects with the same type
 394  
 //        if(getReturnDataType().isCompatibleWith(sourceType))
 395  
 //        {
 396  
 //            logger.debug("Object is already of type: " + getReturnDataType() + " No transform to perform");
 397  
 //            return payload;
 398  
 //        }
 399  
 
 400  0
         if (!isSourceDataTypeSupported(sourceType))
 401  
         {
 402  0
             if (ignoreBadInput)
 403  
             {
 404  0
                 logger.debug("Source type is incompatible with this transformer and property 'ignoreBadInput' is set to true, so the transformer chain will continue.");
 405  0
                 return payload;
 406  
             }
 407  
             else
 408  
             {
 409  0
                 Message msg = CoreMessages.transformOnObjectUnsupportedTypeOfEndpoint(getName(),
 410  
                     payload.getClass(), endpoint);
 411  
                 /// FIXME
 412  0
                 throw new TransformerException(msg, this);
 413  
             }
 414  
         }
 415  
 
 416  0
         if (logger.isDebugEnabled())
 417  
         {
 418  0
             logger.debug(String.format("Applying transformer %s (%s)", getName(), getClass().getName()));
 419  0
             logger.debug(String.format("Object before transform: %s", StringMessageUtils.toString(payload)));
 420  
         }
 421  
 
 422  0
         Object result = doTransform(payload, enc);
 423  
 
 424  0
         if (result == null)
 425  
         {
 426  0
             result = NullPayload.getInstance();
 427  
         }
 428  
 
 429  0
         if (logger.isDebugEnabled())
 430  
         {
 431  0
             logger.debug(String.format("Object after transform: %s", StringMessageUtils.toString(result)));
 432  
         }
 433  
 
 434  0
         result = checkReturnClass(result);
 435  0
         return result;
 436  
     }
 437  
 
 438  
     protected String getEncoding(Object src)
 439  
     {
 440  0
         String enc = null;
 441  0
         if (src instanceof MuleMessage)
 442  
         {
 443  0
             enc = ((MuleMessage) src).getEncoding();
 444  
         }
 445  
 
 446  0
         if (enc == null && endpoint != null)
 447  
         {
 448  0
             enc = endpoint.getEncoding();
 449  
         }
 450  0
         else if (enc == null)
 451  
         {
 452  0
             enc = System.getProperty(MuleProperties.MULE_ENCODING_SYSTEM_PROPERTY);
 453  
         }
 454  0
         return enc;
 455  
     }
 456  
 
 457  
     protected boolean isConsumed(Class<?> srcCls)
 458  
     {
 459  0
         return InputStream.class.isAssignableFrom(srcCls) || StreamSource.class.isAssignableFrom(srcCls);
 460  
     }
 461  
 
 462  
     public ImmutableEndpoint getEndpoint()
 463  
     {
 464  0
         return endpoint;
 465  
     }
 466  
 
 467  
     public void setEndpoint(ImmutableEndpoint endpoint)
 468  
     {
 469  0
         this.endpoint = endpoint;
 470  0
     }
 471  
 
 472  
     protected abstract Object doTransform(Object src, String enc) throws TransformerException;
 473  
 
 474  
     /**
 475  
      * Template method where deriving classes can do any initialisation after the
 476  
      * properties have been set on this transformer
 477  
      *
 478  
      * @throws InitialisationException
 479  
      */
 480  
     public void initialise() throws InitialisationException
 481  
     {
 482  
         // do nothing, subclasses may override
 483  0
     }
 484  
 
 485  
     /**
 486  
      * Template method where deriving classes can do any clean up any resources or state
 487  
      * before the object is disposed.
 488  
      */
 489  
     public void dispose()
 490  
     {
 491  
         // do nothing, subclasses may override
 492  0
     }
 493  
 
 494  
     protected String generateTransformerName()
 495  
     {
 496  0
         String transformerName = ClassUtils.getSimpleName(this.getClass());
 497  0
         int i = transformerName.indexOf("To");
 498  0
         if (i > 0 && returnType != null)
 499  
         {
 500  0
             String target = ClassUtils.getSimpleName(returnType.getType());
 501  0
             if (target.equals("byte[]"))
 502  
             {
 503  0
                 target = "byteArray";
 504  
             }
 505  0
             transformerName = transformerName.substring(0, i + 2) + StringUtils.capitalize(target);
 506  
         }
 507  0
         return transformerName;
 508  
     }
 509  
 
 510  
     @Deprecated
 511  
     public List<Class<?>> getSourceTypes()
 512  
     {
 513  
         //A work around to support the legacy API
 514  0
         List<Class<?>> sourceClasses = new ArrayList<Class<?>>();
 515  0
         for (DataType<?> sourceType : sourceTypes)
 516  
         {
 517  0
             sourceClasses.add(sourceType.getType());
 518  
         }
 519  0
         return Collections.unmodifiableList(sourceClasses);
 520  
     }
 521  
 
 522  
     public List<DataType<?>> getSourceDataTypes()
 523  
     {
 524  0
         return Collections.unmodifiableList(sourceTypes);
 525  
     }
 526  
 
 527  
     public boolean isIgnoreBadInput()
 528  
     {
 529  0
         return ignoreBadInput;
 530  
     }
 531  
 
 532  
     public void setIgnoreBadInput(boolean ignoreBadInput)
 533  
     {
 534  0
         this.ignoreBadInput = ignoreBadInput;
 535  0
     }
 536  
 
 537  
     @Override
 538  
     public String toString()
 539  
     {
 540  0
         StringBuffer sb = new StringBuffer(80);
 541  0
         sb.append(ClassUtils.getSimpleName(this.getClass()));
 542  0
         sb.append("{this=").append(Integer.toHexString(System.identityHashCode(this)));
 543  0
         sb.append(", name='").append(name).append('\'');
 544  0
         sb.append(", ignoreBadInput=").append(ignoreBadInput);
 545  0
         sb.append(", returnClass=").append(returnType);
 546  0
         sb.append(", sourceTypes=").append(sourceTypes);
 547  0
         sb.append('}');
 548  0
         return sb.toString();
 549  
     }
 550  
 
 551  
     public boolean isAcceptNull()
 552  
     {
 553  0
         return false;
 554  
     }
 555  
 
 556  
     public void setMuleContext(MuleContext context)
 557  
     {
 558  0
         this.muleContext = context;
 559  
         try
 560  
         {
 561  0
             muleContext.registerListener(this);
 562  
         }
 563  0
         catch (NotificationException e)
 564  
         {
 565  0
             logger.error("failed to register context listener", e);
 566  0
         }
 567  0
     }
 568  
 
 569  
     public void onNotification(MuleContextNotification notification)
 570  
     {
 571  0
         if (notification.getAction() == MuleContextNotification.CONTEXT_DISPOSING)
 572  
         {
 573  0
             this.dispose();
 574  
         }
 575  0
     }
 576  
 }