Coverage Report - org.mule.transformer.AbstractTransformer
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractTransformer
0%
0/157
0%
0/80
0
 
 1  
 /*
 2  
  * $Id: AbstractTransformer.java 19250 2010-08-30 16:53:14Z dirk.olmes $
 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
         returnType = new SimpleDataType<Object>(newClass);
 258  0
         returnType.setMimeType(mimeType);
 259  0
         returnType.setEncoding(encoding);
 260  0
     }
 261  
 
 262  
     public void setMimeType(String mimeType) throws MimeTypeParseException
 263  
     {
 264  0
         if (mimeType == null)
 265  
         {
 266  0
             this.mimeType = null;
 267  
         }
 268  
         else
 269  
         {
 270  0
             MimeType mt = new MimeType(mimeType);
 271  0
             this.mimeType = mt.getPrimaryType() + "/" + mt.getSubType();
 272  
         }
 273  0
         if (returnType != null)
 274  
         {
 275  0
             returnType.setMimeType(mimeType);
 276  
         }
 277  0
     }
 278  
 
 279  
     public String getMimeType()
 280  
     {
 281  0
         return mimeType;
 282  
     }
 283  
 
 284  
     public String getEncoding()
 285  
     {
 286  0
         return encoding;
 287  
     }
 288  
 
 289  
     public void setEncoding(String encoding)
 290  
     {
 291  0
         this.encoding = encoding;
 292  0
         if (returnType != null)
 293  
         {
 294  0
             returnType.setEncoding(encoding);
 295  
         }
 296  0
     }
 297  
 
 298  
     public boolean isAllowNullReturn()
 299  
     {
 300  0
         return allowNullReturn;
 301  
     }
 302  
 
 303  
     public void setAllowNullReturn(boolean allowNullReturn)
 304  
     {
 305  0
         this.allowNullReturn = allowNullReturn;
 306  0
     }
 307  
 
 308  
     @Deprecated
 309  
     public boolean isSourceTypeSupported(Class<?> aClass)
 310  
     {
 311  0
         return isSourceDataTypeSupported(DataTypeFactory.create(aClass), false);
 312  
     }
 313  
 
 314  
     public boolean isSourceDataTypeSupported(DataType<?> dataType)
 315  
     {
 316  0
         return isSourceDataTypeSupported(dataType, false);
 317  
     }
 318  
 
 319  
     /**
 320  
      * Determines whether that data type passed in is supported by this transformer
 321  
      *
 322  
      * @param aClass     the type to check against
 323  
      * @param exactMatch if the source type on this transformer is open (can be anything) it will return true unless an
 324  
      *                   exact match is requested using this flag
 325  
      * @return true if the source type is supported by this transformer, false otherwise
 326  
      * @deprecated use {@link #isSourceDataTypeSupported(org.mule.api.transformer.DataType, boolean)}
 327  
      */
 328  
     @Deprecated
 329  
     public boolean isSourceTypeSupported(Class<MuleMessage> aClass, boolean exactMatch)
 330  
     {
 331  0
         return isSourceDataTypeSupported(new SimpleDataType<MuleMessage>(aClass), exactMatch);
 332  
     }
 333  
 
 334  
     /**
 335  
      * Determines whether that data type passed in is supported by this transformer
 336  
      *
 337  
      * @param dataType   the type to check against
 338  
      * @param exactMatch if set to true, this method will look for an exact match to the data type, if false it will look
 339  
      *                   for a compatible data type.
 340  
      * @return true if the source type is supported by this transformer, false otherwise
 341  
      */
 342  
     public boolean isSourceDataTypeSupported(DataType<?> dataType, boolean exactMatch)
 343  
     {
 344  0
         int numTypes = sourceTypes.size();
 345  
 
 346  0
         if (numTypes == 0)
 347  
         {
 348  0
             return !exactMatch;
 349  
         }
 350  
 
 351  0
         for (DataType<?> sourceType : sourceTypes)
 352  
         {
 353  0
             if (exactMatch)
 354  
             {
 355  0
                 if (sourceType.equals(dataType))
 356  
                 {
 357  0
                     return true;
 358  
                 }
 359  
             }
 360  
             else
 361  
             {
 362  0
                 if (sourceType.isCompatibleWith(dataType))
 363  
                 {
 364  0
                     return true;
 365  
                 }
 366  
             }
 367  
         }
 368  0
         return false;
 369  
     }
 370  
 
 371  
     public final Object transform(Object src) throws TransformerException
 372  
     {
 373  0
         return transform(src, getEncoding(src));
 374  
     }
 375  
 
 376  
     public Object transform(Object src, String enc) throws TransformerException
 377  
     {
 378  0
         Object payload = src;
 379  0
         if (src instanceof MuleMessage)
 380  
         {
 381  0
             MuleMessage message = (MuleMessage) src;
 382  0
             if ((!isSourceDataTypeSupported(MULE_MESSAGE_DATA_TYPE, true) &&
 383  
                  !(this instanceof AbstractMessageTransformer)))
 384  
             {
 385  0
                 src = ((MuleMessage) src).getPayload();
 386  0
                 payload = message.getPayload();
 387  
             }
 388  
         }
 389  
 
 390  0
         DataType<?> sourceType = DataTypeFactory.create(payload.getClass());
 391  
         //Once we support mime types, it should be possible to do this since we'll be able to discern the difference
 392  
         //between objects with the same type
 393  
 //        if(getReturnDataType().isCompatibleWith(sourceType))
 394  
 //        {
 395  
 //            logger.debug("Object is already of type: " + getReturnDataType() + " No transform to perform");
 396  
 //            return payload;
 397  
 //        }
 398  
 
 399  0
         if (!isSourceDataTypeSupported(sourceType))
 400  
         {
 401  0
             if (ignoreBadInput)
 402  
             {
 403  0
                 logger.debug("Source type is incompatible with this transformer and property 'ignoreBadInput' is set to true, so the transformer chain will continue.");
 404  0
                 return payload;
 405  
             }
 406  
             else
 407  
             {
 408  0
                 Message msg = CoreMessages.transformOnObjectUnsupportedTypeOfEndpoint(getName(),
 409  
                     payload.getClass(), endpoint);
 410  
                 /// FIXME
 411  0
                 throw new TransformerException(msg, this);
 412  
             }
 413  
         }
 414  
 
 415  0
         if (logger.isDebugEnabled())
 416  
         {
 417  0
             logger.debug(String.format("Applying transformer %s (%s)", getName(), getClass().getName()));
 418  0
             logger.debug(String.format("Object before transform: %s", StringMessageUtils.toString(payload)));
 419  
         }
 420  
 
 421  0
         Object result = doTransform(payload, enc);
 422  
 
 423  0
         if (result == null)
 424  
         {
 425  0
             result = NullPayload.getInstance();
 426  
         }
 427  
 
 428  0
         if (logger.isDebugEnabled())
 429  
         {
 430  0
             logger.debug(String.format("Object after transform: %s", StringMessageUtils.toString(result)));
 431  
         }
 432  
 
 433  0
         result = checkReturnClass(result);
 434  0
         return result;
 435  
     }
 436  
 
 437  
     protected String getEncoding(Object src)
 438  
     {
 439  0
         String enc = null;
 440  0
         if (src instanceof MuleMessage)
 441  
         {
 442  0
             enc = ((MuleMessage) src).getEncoding();
 443  
         }
 444  
 
 445  0
         if (enc == null && endpoint != null)
 446  
         {
 447  0
             enc = endpoint.getEncoding();
 448  
         }
 449  0
         else if (enc == null)
 450  
         {
 451  0
             enc = System.getProperty(MuleProperties.MULE_ENCODING_SYSTEM_PROPERTY);
 452  
         }
 453  0
         return enc;
 454  
     }
 455  
 
 456  
     protected boolean isConsumed(Class<?> srcCls)
 457  
     {
 458  0
         return InputStream.class.isAssignableFrom(srcCls) || StreamSource.class.isAssignableFrom(srcCls);
 459  
     }
 460  
 
 461  
     public ImmutableEndpoint getEndpoint()
 462  
     {
 463  0
         return endpoint;
 464  
     }
 465  
 
 466  
     public void setEndpoint(ImmutableEndpoint endpoint)
 467  
     {
 468  0
         this.endpoint = endpoint;
 469  0
     }
 470  
 
 471  
     protected abstract Object doTransform(Object src, String enc) throws TransformerException;
 472  
 
 473  
     /**
 474  
      * Template method where deriving classes can do any initialisation after the
 475  
      * properties have been set on this transformer
 476  
      *
 477  
      * @throws InitialisationException
 478  
      */
 479  
     public void initialise() throws InitialisationException
 480  
     {
 481  
         // do nothing, subclasses may override
 482  0
     }
 483  
 
 484  
     /**
 485  
      * Template method where deriving classes can do any clean up any resources or state
 486  
      * before the object is disposed.
 487  
      */
 488  
     public void dispose()
 489  
     {
 490  
         // do nothing, subclasses may override
 491  0
     }
 492  
 
 493  
     protected String generateTransformerName()
 494  
     {
 495  0
         String transformerName = ClassUtils.getSimpleName(this.getClass());
 496  0
         int i = transformerName.indexOf("To");
 497  0
         if (i > 0 && returnType != null)
 498  
         {
 499  0
             String target = ClassUtils.getSimpleName(returnType.getType());
 500  0
             if (target.equals("byte[]"))
 501  
             {
 502  0
                 target = "byteArray";
 503  
             }
 504  0
             transformerName = transformerName.substring(0, i + 2) + StringUtils.capitalize(target);
 505  
         }
 506  0
         return transformerName;
 507  
     }
 508  
 
 509  
     @Deprecated
 510  
     public List<Class<?>> getSourceTypes()
 511  
     {
 512  
         //A work around to support the legacy API
 513  0
         List<Class<?>> sourceClasses = new ArrayList<Class<?>>();
 514  0
         for (DataType<?> sourceType : sourceTypes)
 515  
         {
 516  0
             sourceClasses.add(sourceType.getType());
 517  
         }
 518  0
         return Collections.unmodifiableList(sourceClasses);
 519  
     }
 520  
 
 521  
     public List<DataType<?>> getSourceDataTypes()
 522  
     {
 523  0
         return Collections.unmodifiableList(sourceTypes);
 524  
     }
 525  
 
 526  
     public boolean isIgnoreBadInput()
 527  
     {
 528  0
         return ignoreBadInput;
 529  
     }
 530  
 
 531  
     public void setIgnoreBadInput(boolean ignoreBadInput)
 532  
     {
 533  0
         this.ignoreBadInput = ignoreBadInput;
 534  0
     }
 535  
 
 536  
     @Override
 537  
     public String toString()
 538  
     {
 539  0
         StringBuffer sb = new StringBuffer(80);
 540  0
         sb.append(ClassUtils.getSimpleName(this.getClass()));
 541  0
         sb.append("{this=").append(Integer.toHexString(System.identityHashCode(this)));
 542  0
         sb.append(", name='").append(name).append('\'');
 543  0
         sb.append(", ignoreBadInput=").append(ignoreBadInput);
 544  0
         sb.append(", returnClass=").append(returnType);
 545  0
         sb.append(", sourceTypes=").append(sourceTypes);
 546  0
         sb.append('}');
 547  0
         return sb.toString();
 548  
     }
 549  
 
 550  
     public boolean isAcceptNull()
 551  
     {
 552  0
         return false;
 553  
     }
 554  
 
 555  
     public void setMuleContext(MuleContext context)
 556  
     {
 557  0
         this.muleContext = context;
 558  
         try
 559  
         {
 560  0
             muleContext.registerListener(this);
 561  
         }
 562  0
         catch (NotificationException e)
 563  
         {
 564  0
             logger.error("failed to register context listener", e);
 565  0
         }
 566  0
     }
 567  
 
 568  
     public void onNotification(MuleContextNotification notification)
 569  
     {
 570  0
         if (notification.getAction() == MuleContextNotification.CONTEXT_DISPOSING)
 571  
         {
 572  0
             this.dispose();
 573  
         }
 574  0
     }
 575  
 }