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