View Javadoc

1   /*
2    * $Id: AbstractTransformer.java 11517 2008-03-31 21:34:19Z 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.transformer;
12  
13  import org.mule.api.MuleMessage;
14  import org.mule.api.endpoint.ImmutableEndpoint;
15  import org.mule.api.lifecycle.InitialisationException;
16  import org.mule.api.transformer.Transformer;
17  import org.mule.api.transformer.TransformerException;
18  import org.mule.api.transport.MessageAdapter;
19  import org.mule.config.i18n.CoreMessages;
20  import org.mule.transport.NullPayload;
21  import org.mule.util.ClassUtils;
22  import org.mule.util.FileUtils;
23  import org.mule.util.StringMessageUtils;
24  import org.mule.util.StringUtils;
25  
26  import java.io.InputStream;
27  import java.util.Collections;
28  import java.util.List;
29  
30  import javax.xml.transform.stream.StreamSource;
31  
32  import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  
37  /**
38   * <code>AbstractTransformer</code> is a base class for all transformers.
39   * Transformations transform one object into another.
40   */
41  
42  public abstract class AbstractTransformer implements Transformer
43  {
44      protected static final int DEFAULT_TRUNCATE_LENGTH = 200;
45  
46      protected final Log logger = LogFactory.getLog(getClass());
47  
48      /**
49       * The return type that will be returned by the {@link #transform} method is
50       * called
51       */
52      protected Class returnClass = Object.class;
53  
54      /**
55       * The name that identifies this transformer. If none is set the class name of
56       * the transformer is used
57       */
58      protected String name = null;
59  
60      /** The endpoint that this transformer instance is configured on */
61      protected ImmutableEndpoint endpoint = null;
62  
63      /**
64       * A list of supported Class types that the source payload passed into this
65       * transformer
66       */
67      protected final List sourceTypes = new CopyOnWriteArrayList();
68  
69      /**
70       * Determines whether the transformer will throw an exception if the message
71       * passed is is not supported or the return tye is incorrect
72       */
73      private boolean ignoreBadInput = false;
74  
75      /** default constructor required for discovery */
76      public AbstractTransformer()
77      {
78          super();
79      }
80  
81      protected Object checkReturnClass(Object object) throws TransformerException
82      {
83          if (returnClass != null)
84          {
85              if (!returnClass.isInstance(object))
86              {
87                  throw new TransformerException(
88                          CoreMessages.transformUnexpectedType(object.getClass(), returnClass),
89                          this);
90              }
91          }
92  
93          if (logger.isDebugEnabled())
94          {
95              logger.debug("The transformed object is of expected type. Type is: " +
96                      ClassUtils.getSimpleName(object.getClass()));
97          }
98  
99          return object;
100     }
101 
102     protected void registerSourceType(Class aClass)
103     {
104         if (!sourceTypes.contains(aClass))
105         {
106             sourceTypes.add(aClass);
107 
108             if (aClass.equals(Object.class))
109             {
110                 logger.debug("java.lang.Object has been added as source type for this transformer, there will be no source type checking performed");
111             }
112         }
113     }
114 
115     protected void unregisterSourceType(Class aClass)
116     {
117         sourceTypes.remove(aClass);
118     }
119 
120     /** @return transformer name */
121     public String getName()
122     {
123         if (name == null)
124         {
125             name = this.generateTransformerName();
126         }
127         return name;
128     }
129 
130     /** @param string  */
131     public void setName(String string)
132     {
133         if (string == null)
134         {
135             string = ClassUtils.getSimpleName(this.getClass());
136         }
137 
138         logger.debug("Setting transformer name to: " + string);
139         name = string;
140     }
141 
142     /*
143      * (non-Javadoc)
144      *
145      * @see org.mule.transformer.Transformer#getReturnClass()
146      */
147     public Class getReturnClass()
148     {
149         return returnClass;
150     }
151 
152     /*
153      * (non-Javadoc)
154      *
155      * @see org.mule.transformer.Transformer#setReturnClass(java.lang.String)
156      */
157     public void setReturnClass(Class newClass)
158     {
159         returnClass = newClass;
160     }
161 
162     public boolean isSourceTypeSupported(Class aClass)
163     {
164         return isSourceTypeSupported(aClass, false);
165     }
166 
167     public boolean isSourceTypeSupported(Class aClass, boolean exactMatch)
168     {
169         int numTypes = sourceTypes.size();
170 
171         if (numTypes == 0)
172         {
173             return !exactMatch;
174         }
175 
176         for (int i = 0; i < numTypes; i++)
177         {
178             Class anotherClass = (Class) sourceTypes.get(i);
179             if (exactMatch)
180             {
181                 if (anotherClass.equals(aClass))
182                 {
183                     return true;
184                 }
185             }
186             else if (anotherClass.isAssignableFrom(aClass))
187             {
188                 return true;
189             }
190         }
191 
192         return false;
193     }
194 
195     /**
196      * Transforms the object.
197      *
198      * @param src The source object to transform.
199      * @return The transformed object
200      */
201     public final Object transform(Object src) throws TransformerException
202     {
203         String encoding = null;
204 
205         Object payload = src;
206         MessageAdapter adapter;
207         if (src instanceof MessageAdapter)
208         {
209             encoding = ((MessageAdapter) src).getEncoding();
210             adapter = (MessageAdapter) src;
211             if ((!isSourceTypeSupported(MessageAdapter.class, true)
212                     && !isSourceTypeSupported(MuleMessage.class, true)
213                     && !(this instanceof AbstractMessageAwareTransformer))
214                     )
215             {
216                 src = ((MessageAdapter) src).getPayload();
217                 payload = adapter.getPayload();
218             }
219         }
220 
221         if (encoding == null && endpoint != null)
222         {
223             encoding = endpoint.getEncoding();
224         }
225         else if (encoding == null)
226         {
227             encoding = FileUtils.DEFAULT_ENCODING;
228         }
229 
230         Class srcCls = src.getClass();
231         if (!isSourceTypeSupported(srcCls))
232         {
233             if (ignoreBadInput)
234             {
235                 logger.debug("Source type is incompatible with this transformer and property 'ignoreBadInput' is set to true, so the transformer chain will continue.");
236                 return payload;
237             }
238             else
239             {
240                 throw new TransformerException(
241                         CoreMessages.transformOnObjectUnsupportedTypeOfEndpoint(this.getName(),
242                                 payload.getClass(), endpoint), this);
243             }
244         }
245 
246         if (logger.isDebugEnabled())
247         {
248             logger.debug("Applying transformer " + getName() + " (" + getClass().getName() + ")");
249             logger.debug("Object before transform: "
250                     + StringMessageUtils.truncate(StringMessageUtils.toString(payload), DEFAULT_TRUNCATE_LENGTH, false));
251         }
252 
253         Object result;
254         result = doTransform(payload, encoding);
255         // }
256         if (result == null)
257         {
258             result = NullPayload.getInstance();
259         }
260 
261         if (logger.isDebugEnabled())
262         {
263             logger.debug("Object after transform: "
264                     + StringMessageUtils.truncate(StringMessageUtils.toString(result), DEFAULT_TRUNCATE_LENGTH, false));
265         }
266 
267         result = checkReturnClass(result);
268         return result;
269     }
270 
271     protected boolean isConsumed(Class srcCls)
272     {
273         return InputStream.class.isAssignableFrom(srcCls) || StreamSource.class.isAssignableFrom(srcCls);
274     }
275 
276     public ImmutableEndpoint getEndpoint()
277     {
278         return endpoint;
279     }
280 
281     /*
282      * (non-Javadoc)
283      *
284      * @see org.mule.api.transformer.Transformer#setConnector(org.mule.api.transport.Connector)
285      */
286     public void setEndpoint(ImmutableEndpoint endpoint)
287     {
288         this.endpoint = endpoint;
289     }
290 
291     protected abstract Object doTransform(Object src, String encoding) throws TransformerException;
292 
293     /**
294      * Template method where deriving classes can do any initialisation after the
295      * properties have been set on this transformer
296      *
297      * @throws InitialisationException
298      */
299     public void initialise() throws InitialisationException
300     {
301         // do nothing, subclasses may override
302     }
303 
304     protected String generateTransformerName()
305     {
306         String name = ClassUtils.getSimpleName(this.getClass());
307         int i = name.indexOf("To");
308         if (i > 0 && returnClass != null)
309         {
310             String target = ClassUtils.getSimpleName(returnClass);
311             if (target.equals("byte[]"))
312             {
313                 target = "byteArray";
314             }
315             name = name.substring(0, i + 2) + StringUtils.capitalize(target);
316         }
317         return name;
318     }
319 
320     public List getSourceTypes()
321     {
322         return Collections.unmodifiableList(sourceTypes);
323     }
324 
325     public boolean isIgnoreBadInput()
326     {
327         return ignoreBadInput;
328     }
329 
330     public void setIgnoreBadInput(boolean ignoreBadInput)
331     {
332         this.ignoreBadInput = ignoreBadInput;
333     }
334 
335     // @Override
336     public String toString()
337     {
338         StringBuffer sb = new StringBuffer(80);
339         sb.append(ClassUtils.getSimpleName(this.getClass()));
340         sb.append("{this=").append(Integer.toHexString(System.identityHashCode(this)));
341         sb.append(", name='").append(name).append('\'');
342         sb.append(", ignoreBadInput=").append(ignoreBadInput);
343         sb.append(", returnClass=").append(returnClass);
344         sb.append(", sourceTypes=").append(sourceTypes);
345         sb.append('}');
346         return sb.toString();
347     }
348 
349     public boolean isAcceptNull()
350     {
351         return false;
352     }
353 
354 }