View Javadoc

1   /*
2    * $Id: XsltTransformer.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.transformers.xml;
12  
13  import org.mule.config.i18n.CoreMessages;
14  import org.mule.umo.lifecycle.InitialisationException;
15  import org.mule.umo.transformer.TransformerException;
16  import org.mule.umo.transformer.UMOTransformer;
17  import org.mule.util.ClassUtils;
18  import org.mule.util.IOUtils;
19  import org.mule.util.StringUtils;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.StringReader;
24  
25  import javax.xml.transform.ErrorListener;
26  import javax.xml.transform.OutputKeys;
27  import javax.xml.transform.Source;
28  import javax.xml.transform.Transformer;
29  import javax.xml.transform.TransformerFactory;
30  import javax.xml.transform.URIResolver;
31  import javax.xml.transform.stream.StreamSource;
32  
33  import org.apache.commons.pool.BasePoolableObjectFactory;
34  import org.apache.commons.pool.impl.GenericObjectPool;
35  
36  /**
37   * <code>XsltTransformer</code> performs an XSLT transform on a DOM (or other
38   * XML-ish) object.
39   */
40  
41  public class XsltTransformer extends AbstractXmlTransformer
42  {
43      // keep at least 1 XSLT Transformer ready by default
44      private static final int MIN_IDLE_TRANSFORMERS = 1;
45      // keep max. 32 XSLT Transformers around by default
46      private static final int MAX_IDLE_TRANSFORMERS = 32;
47      // MAX_IDLE is also the total limit
48      private static final int MAX_ACTIVE_TRANSFORMERS = MAX_IDLE_TRANSFORMERS;
49  
50      protected final GenericObjectPool transformerPool;
51  
52      private volatile String xslTransformerFactoryClassName;
53      private volatile String xslFile;
54      private volatile String xslt;
55  
56      public XsltTransformer()
57      {
58          super();
59          transformerPool = new GenericObjectPool(new PooledXsltTransformerFactory());
60          transformerPool.setMinIdle(MIN_IDLE_TRANSFORMERS);
61          transformerPool.setMaxIdle(MAX_IDLE_TRANSFORMERS);
62          transformerPool.setMaxActive(MAX_ACTIVE_TRANSFORMERS);
63      }
64  
65      /**
66       * @see org.mule.umo.lifecycle.Initialisable#initialise()
67       */
68      // @Override
69      public void initialise() throws InitialisationException
70      {
71          try
72          {
73              transformerPool.addObject();
74          }
75          catch (Throwable te)
76          {
77              throw new InitialisationException(te, this);
78          }
79      }
80  
81      /**
82       * Transform, using XSLT, a XML String to another String.
83       * 
84       * @param src The source XML (String, byte[], DOM, etc.)
85       * @return The result String (or DOM)
86       */
87      public Object doTransform(Object src, String encoding) throws TransformerException
88      {
89          try
90          {
91              Source sourceDoc = this.getXmlSource(src);
92              if (sourceDoc == null)
93              {
94                  return null;
95              }
96  
97              ResultHolder holder = getResultHolder(returnClass);
98              if (holder == null)
99              {
100                 holder = getResultHolder(src.getClass());
101             }
102 
103             DefaultErrorListener errorListener = new DefaultErrorListener(this);
104             Transformer transformer = null;
105             Object result;
106 
107             try
108             {
109                 transformer = (Transformer) transformerPool.borrowObject();
110 
111                 transformer.setErrorListener(errorListener);
112                 transformer.setOutputProperty(OutputKeys.ENCODING, encoding);
113 
114                 transformer.transform(sourceDoc, holder.getResult());
115                 result = holder.getResultObject();
116 
117                 if (errorListener.isError())
118                 {
119                     throw errorListener.getException();
120                 }
121             }
122             finally
123             {
124                 if (transformer != null)
125                 {
126                     transformerPool.returnObject(transformer);
127                 }
128             }
129 
130             return result;
131         }
132         catch (Exception e)
133         {
134             throw new TransformerException(this, e);
135         }
136     }
137 
138     /**
139      * Returns the name of the currently configured javax.xml.transform.Transformer
140      * factory class used to create XSLT Transformers.
141      * 
142      * @return a TransformerFactory class name or <code>null</code> if none has
143      *         been configured
144      */
145     public String getXslTransformerFactory()
146     {
147         return xslTransformerFactoryClassName;
148     }
149 
150     /**
151      * Configures the javax.xml.transform.Transformer factory class
152      * 
153      * @param xslTransformerFactory the name of the TransformerFactory class to use
154      */
155     public void setXslTransformerFactory(String xslTransformerFactory)
156     {
157         this.xslTransformerFactoryClassName = xslTransformerFactory;
158     }
159 
160     /**
161      * @return Returns the xslFile.
162      */
163     public String getXslFile()
164     {
165         return xslFile;
166     }
167 
168     /**
169      * @param xslFile The xslFile to set.
170      */
171     public void setXslFile(String xslFile)
172     {
173         this.xslFile = xslFile;
174     }
175 
176     public String getXslt()
177     {
178         return xslt;
179     }
180 
181     public void setXslt(String xslt)
182     {
183         this.xslt = xslt;
184     }
185 
186     /**
187      * Returns the StreamSource corresponding to xslFile
188      * 
189      * @return The StreamSource
190      * @throws InitialisationException
191      */
192     protected StreamSource getStreamSource() throws InitialisationException
193     {
194         if (xslt != null)
195         {
196             return new StreamSource(new StringReader(xslt));
197         }
198 
199         if (xslFile == null)
200         {
201             throw new InitialisationException(CoreMessages.objectIsNull("xslFile"), this);
202         }
203 
204         InputStream is;
205         try
206         {
207             is = IOUtils.getResourceAsStream(xslFile, getClass());
208         }
209         catch (IOException e)
210         {
211             throw new InitialisationException(e, this);
212         }
213         if (is != null)
214         {
215             return new StreamSource(is);
216         }
217         else
218         {
219             throw new InitialisationException(CoreMessages.failedToLoad(xslFile), this);
220         }
221     }
222 
223     // @Override
224     public Object clone() throws CloneNotSupportedException
225     {
226         XsltTransformer clone = (XsltTransformer) super.clone();
227 
228         try
229         {
230             if (clone.nextTransformer == null)
231             {
232                 clone.initialise();
233             }
234         }
235         catch (Exception e)
236         {
237             throw new CloneNotSupportedException(e.getMessage());
238         }
239 
240         return clone;
241     }
242 
243     protected class PooledXsltTransformerFactory extends BasePoolableObjectFactory
244     {
245         public Object makeObject() throws Exception
246         {
247             StreamSource source = XsltTransformer.this.getStreamSource();
248             String factoryClassName = XsltTransformer.this.getXslTransformerFactory();
249             TransformerFactory factory = null;
250 
251             if (StringUtils.isNotEmpty(factoryClassName))
252             {
253                 factory = (TransformerFactory) ClassUtils.instanciateClass(factoryClassName,
254                     ClassUtils.NO_ARGS, this.getClass());
255             }
256             else
257             {
258                 // fall back to JDK default
259                 factory = TransformerFactory.newInstance();
260             }
261 
262             factory.setURIResolver(new URIResolver()
263             {
264                 public Source resolve(String href, String base)
265                     throws javax.xml.transform.TransformerException
266                 {
267                     try
268                     {
269                         return new StreamSource(IOUtils.getResourceAsStream(href, getClass()));
270                     }
271                     catch (IOException e)
272                     {
273                         throw new javax.xml.transform.TransformerException(e);
274                     }
275                 }
276             });
277             return factory.newTransformer(source);
278         }
279     }
280 
281     protected class DefaultErrorListener implements ErrorListener
282     {
283         private TransformerException e = null;
284         private final UMOTransformer trans;
285 
286         public DefaultErrorListener(UMOTransformer trans)
287         {
288             this.trans = trans;
289         }
290 
291         public TransformerException getException()
292         {
293             return e;
294         }
295 
296         public boolean isError()
297         {
298             return e != null;
299         }
300 
301         public void error(javax.xml.transform.TransformerException exception)
302             throws javax.xml.transform.TransformerException
303         {
304             e = new TransformerException(trans, exception);
305         }
306 
307         public void fatalError(javax.xml.transform.TransformerException exception)
308             throws javax.xml.transform.TransformerException
309         {
310             e = new TransformerException(trans, exception);
311         }
312 
313         public void warning(javax.xml.transform.TransformerException exception)
314             throws javax.xml.transform.TransformerException
315         {
316             logger.warn(exception.getMessage());
317         }
318     }
319 
320     /**
321      * @return The current maximum number of allowable idle transformer objects in
322      *         the pool
323      */
324     public int getMaxActiveTransformers()
325     {
326         return transformerPool.getMaxActive();
327     }
328 
329     /**
330      * Sets the the current maximum number of idle transformer objects allowed in the
331      * pool
332      * 
333      * @param maxIdleTransformers New maximum size to set
334      */
335     public void setMaxActiveTransformers(int maxActiveTransformers)
336     {
337         transformerPool.setMaxActive(maxActiveTransformers);
338     }
339 
340     /**
341      * @return The current maximum number of allowable idle transformer objects in
342      *         the pool
343      */
344     public int getMaxIdleTransformers()
345     {
346         return transformerPool.getMaxIdle();
347     }
348 
349     /**
350      * Sets the the current maximum number of idle transformer objects allowed in the
351      * pool
352      * 
353      * @param maxIdleTransformers New maximum size to set
354      */
355     public void setMaxIdleTransformers(int maxIdleTransformers)
356     {
357         transformerPool.setMaxIdle(maxIdleTransformers);
358     }
359 
360 }