Coverage Report - org.mule.transformers.xml.XsltTransformer
 
Classes in this File Line Coverage Branch Coverage Complexity
XsltTransformer
85%
75/88
64%
18/28
2.423
XsltTransformer$1
N/A
N/A
2.423
XsltTransformer$DefaultErrorListener
42%
5/12
50%
1/2
2.423
XsltTransformer$LocalURIResolver
50%
2/4
N/A
2.423
XsltTransformer$PooledXsltTransformerFactory
100%
8/8
100%
2/2
2.423
 
 1  
 /*
 2  
  * $Id: XsltTransformer.java 11728 2008-05-13 07:31:11Z 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.impl.RequestContext;
 15  
 import org.mule.umo.UMOEventContext;
 16  
 import org.mule.umo.lifecycle.InitialisationException;
 17  
 import org.mule.umo.transformer.TransformerException;
 18  
 import org.mule.umo.transformer.UMOTransformer;
 19  
 import org.mule.util.ClassUtils;
 20  
 import org.mule.util.IOUtils;
 21  
 import org.mule.util.StringUtils;
 22  
 
 23  
 import java.io.IOException;
 24  
 import java.io.InputStream;
 25  
 import java.io.StringReader;
 26  
 import java.util.Iterator;
 27  
 import java.util.Map;
 28  
 import java.util.Map.Entry;
 29  
 
 30  
 import javax.xml.transform.ErrorListener;
 31  
 import javax.xml.transform.OutputKeys;
 32  
 import javax.xml.transform.Source;
 33  
 import javax.xml.transform.Transformer;
 34  
 import javax.xml.transform.TransformerFactory;
 35  
 import javax.xml.transform.URIResolver;
 36  
 import javax.xml.transform.stream.StreamSource;
 37  
 
 38  
 import org.apache.commons.jxpath.JXPathContext;
 39  
 import org.apache.commons.pool.BasePoolableObjectFactory;
 40  
 import org.apache.commons.pool.impl.GenericObjectPool;
 41  
 
 42  
 /**
 43  
  * <code>XsltTransformer</code> performs an XSLT transform on a DOM (or other XML-ish)
 44  
  * object.
 45  
  */
 46  
 
 47  70
 public class XsltTransformer extends AbstractXmlTransformer
 48  
 {
 49  
     // keep at least 1 XSLT Transformer ready by default
 50  
     private static final int MIN_IDLE_TRANSFORMERS = 1;
 51  
     // keep max. 32 XSLT Transformers around by default
 52  
     private static final int MAX_IDLE_TRANSFORMERS = 32;
 53  
     // MAX_IDLE is also the total limit
 54  
     private static final int MAX_ACTIVE_TRANSFORMERS = MAX_IDLE_TRANSFORMERS;
 55  
     // Prefix to use in a parameter to specify it is an expression that must be evaluated
 56  
     private static final String PARAM_EVAL_TOKEN = "#";
 57  
 
 58  
     protected final GenericObjectPool transformerPool;
 59  
 
 60  
     private volatile String xslTransformerFactoryClassName;
 61  
     private volatile String xslFile;
 62  
     private volatile String xslt;
 63  
     private volatile Map transformParameters;
 64  
     private URIResolver uriResolver;
 65  
 
 66  
     public XsltTransformer()
 67  
     {
 68  32
         super();
 69  32
         transformerPool = new GenericObjectPool(new PooledXsltTransformerFactory());
 70  32
         transformerPool.setMinIdle(MIN_IDLE_TRANSFORMERS);
 71  32
         transformerPool.setMaxIdle(MAX_IDLE_TRANSFORMERS);
 72  32
         transformerPool.setMaxActive(MAX_ACTIVE_TRANSFORMERS);
 73  32
         uriResolver = new LocalURIResolver();
 74  32
     }
 75  
 
 76  
     /**
 77  
      * @see org.mule.umo.lifecycle.Initialisable#initialise()
 78  
      */
 79  
     // @Override
 80  
     public void initialise() throws InitialisationException
 81  
     {
 82  
         try
 83  
         {
 84  34
             transformerPool.addObject();
 85  
         }
 86  2
         catch (Throwable te)
 87  
         {
 88  2
             throw new InitialisationException(te, this);
 89  32
         }
 90  32
     }
 91  
 
 92  
     /**
 93  
      * Transform, using XSLT, a XML String to another String.
 94  
      *
 95  
      * @param src The source XML (String, byte[], DOM, etc.)
 96  
      * @return The result String (or DOM)
 97  
      */
 98  
     public Object doTransform(Object src, String encoding) throws TransformerException
 99  
     {
 100  
         try
 101  
         {
 102  4016
             Source sourceDoc = this.getXmlSource(src);
 103  4016
             if (sourceDoc == null)
 104  
             {
 105  0
                 return null;
 106  
             }
 107  
 
 108  4016
             ResultHolder holder = getResultHolder(returnClass);
 109  4016
             if (holder == null)
 110  
             {
 111  4014
                 holder = getResultHolder(src.getClass());
 112  
             }
 113  
 
 114  4016
             DefaultErrorListener errorListener = new DefaultErrorListener(this);
 115  4015
             Transformer transformer = null;
 116  
             Object result;
 117  
 
 118  
             try
 119  
             {
 120  4016
                 transformer = (Transformer) transformerPool.borrowObject();
 121  
 
 122  4016
                 transformer.setErrorListener(errorListener);
 123  4016
                 transformer.setOutputProperty(OutputKeys.ENCODING, encoding);
 124  
 
 125  
                 // set transformation parameters
 126  4016
                 if (transformParameters != null)
 127  
                 {
 128  4
                     for (Iterator i = transformParameters.entrySet().iterator(); i.hasNext();)
 129  
                     {
 130  4
                         Map.Entry parameter = (Entry) i.next();
 131  4
                         String key = (String) parameter.getKey();
 132  4
                         transformer.setParameter(key, evaluateTransformParameter(key, parameter.getValue()));
 133  4
                     }
 134  
                 }
 135  
 
 136  4016
                 transformer.transform(sourceDoc, holder.getResult());
 137  4011
                 result = holder.getResultObject();
 138  
 
 139  4015
                 if (errorListener.isError())
 140  
                 {
 141  0
                     throw errorListener.getException();
 142  
                 }
 143  
             }
 144  
             finally
 145  
             {
 146  4016
                 if (transformer != null)
 147  
                 {
 148  
                     // clear transformation parameters before returning transformer to the
 149  
                     // pool
 150  4016
                     transformer.clearParameters();
 151  
 
 152  4016
                     transformerPool.returnObject(transformer);
 153  
                 }
 154  
             }
 155  
 
 156  4016
             return result;
 157  
         }
 158  0
         catch (Exception e)
 159  
         {
 160  0
             throw new TransformerException(this, e);
 161  
         }
 162  
     }
 163  
 
 164  
     /**
 165  
      * Returns the name of the currently configured javax.xml.transform.Transformer
 166  
      * factory class used to create XSLT Transformers.
 167  
      *
 168  
      * @return a TransformerFactory class name or <code>null</code> if none has been
 169  
      *         configured
 170  
      */
 171  
     public String getXslTransformerFactory()
 172  
     {
 173  78
         return xslTransformerFactoryClassName;
 174  
     }
 175  
 
 176  
     /**
 177  
      * Configures the javax.xml.transform.Transformer factory class
 178  
      *
 179  
      * @param xslTransformerFactory the name of the TransformerFactory class to use
 180  
      */
 181  
     public void setXslTransformerFactory(String xslTransformerFactory)
 182  
     {
 183  10
         this.xslTransformerFactoryClassName = xslTransformerFactory;
 184  10
     }
 185  
 
 186  
     /**
 187  
      * @return Returns the xslFile.
 188  
      */
 189  
     public String getXslFile()
 190  
     {
 191  6
         return xslFile;
 192  
     }
 193  
 
 194  
     /**
 195  
      * @param xslFile The xslFile to set.
 196  
      */
 197  
     public void setXslFile(String xslFile)
 198  
     {
 199  22
         this.xslFile = xslFile;
 200  22
     }
 201  
 
 202  
     public String getXslt()
 203  
     {
 204  6
         return xslt;
 205  
     }
 206  
 
 207  
     public void setXslt(String xslt)
 208  
     {
 209  16
         this.xslt = xslt;
 210  16
     }
 211  
 
 212  
     /**
 213  
      * Returns the StreamSource corresponding to xslFile
 214  
      *
 215  
      * @return The StreamSource
 216  
      */
 217  
     protected StreamSource getStreamSource() throws InitialisationException
 218  
     {
 219  72
         if (xslt != null)
 220  
         {
 221  12
             return new StreamSource(new StringReader(xslt));
 222  
         }
 223  
 
 224  60
         if (xslFile == null)
 225  
         {
 226  0
             throw new InitialisationException(CoreMessages.objectIsNull("xslFile"), this);
 227  
         }
 228  
 
 229  
         InputStream is;
 230  
         try
 231  
         {
 232  60
             is = IOUtils.getResourceAsStream(xslFile, getClass());
 233  
         }
 234  0
         catch (IOException e)
 235  
         {
 236  0
             throw new InitialisationException(e, this);
 237  60
         }
 238  60
         if (is != null)
 239  
         {
 240  60
             return new StreamSource(is);
 241  
         }
 242  
         else
 243  
         {
 244  0
             throw new InitialisationException(CoreMessages.failedToLoad(xslFile), this);
 245  
         }
 246  
     }
 247  
 
 248  
     // @Override
 249  
     public Object clone() throws CloneNotSupportedException
 250  
     {
 251  6
         XsltTransformer clone = (XsltTransformer) super.clone();
 252  
 
 253  
         try
 254  
         {
 255  6
             if (clone.nextTransformer == null)
 256  
             {
 257  6
                 clone.initialise();
 258  
             }
 259  
         }
 260  0
         catch (Exception e)
 261  
         {
 262  0
             throw new CloneNotSupportedException(e.getMessage());
 263  6
         }
 264  
 
 265  6
         return clone;
 266  
     }
 267  
 
 268  32
     protected class PooledXsltTransformerFactory extends BasePoolableObjectFactory
 269  
     {
 270  
         public Object makeObject() throws Exception
 271  
         {
 272  72
             StreamSource source = XsltTransformer.this.getStreamSource();
 273  72
             String factoryClassName = XsltTransformer.this.getXslTransformerFactory();
 274  
             TransformerFactory factory;
 275  
 
 276  72
             if (StringUtils.isNotEmpty(factoryClassName))
 277  
             {
 278  2
                 factory = (TransformerFactory) ClassUtils.instanciateClass(factoryClassName,
 279  
                         ClassUtils.NO_ARGS, this.getClass());
 280  
             }
 281  
             else
 282  
             {
 283  
                 // fall back to JDK default
 284  70
                 factory = TransformerFactory.newInstance();
 285  
             }
 286  
 
 287  70
             factory.setURIResolver(uriResolver);
 288  70
             return factory.newTransformer(source);
 289  
         }
 290  
     }
 291  
 
 292  
     protected class DefaultErrorListener implements ErrorListener
 293  
     {
 294  4016
         private TransformerException e = null;
 295  
         private final UMOTransformer trans;
 296  
 
 297  
         public DefaultErrorListener(UMOTransformer trans)
 298  4016
         {
 299  4016
             this.trans = trans;
 300  4016
         }
 301  
 
 302  
         public TransformerException getException()
 303  
         {
 304  0
             return e;
 305  
         }
 306  
 
 307  
         public boolean isError()
 308  
         {
 309  4016
             return e != null;
 310  
         }
 311  
 
 312  
         public void error(javax.xml.transform.TransformerException exception)
 313  
                 throws javax.xml.transform.TransformerException
 314  
         {
 315  0
             e = new TransformerException(trans, exception);
 316  0
         }
 317  
 
 318  
         public void fatalError(javax.xml.transform.TransformerException exception)
 319  
                 throws javax.xml.transform.TransformerException
 320  
         {
 321  0
             e = new TransformerException(trans, exception);
 322  0
         }
 323  
 
 324  
         public void warning(javax.xml.transform.TransformerException exception)
 325  
                 throws javax.xml.transform.TransformerException
 326  
         {
 327  0
             logger.warn(exception.getMessage());
 328  0
         }
 329  
     }
 330  
 
 331  
     /**
 332  
      * @return The current maximum number of allowable idle transformer objects in the
 333  
      *         pool
 334  
      */
 335  
     public int getMaxActiveTransformers()
 336  
     {
 337  10
         return transformerPool.getMaxActive();
 338  
     }
 339  
 
 340  
     /**
 341  
      * Sets the the current maximum number of idle transformer objects allowed in the pool
 342  
      *
 343  
      * @param maxActiveTransformers New maximum size to set
 344  
      */
 345  
     public void setMaxActiveTransformers(int maxActiveTransformers)
 346  
     {
 347  12
         transformerPool.setMaxActive(maxActiveTransformers);
 348  12
     }
 349  
 
 350  
     /**
 351  
      * @return The current maximum number of allowable idle transformer objects in the
 352  
      *         pool
 353  
      */
 354  
     public int getMaxIdleTransformers()
 355  
     {
 356  6
         return transformerPool.getMaxIdle();
 357  
     }
 358  
 
 359  
     /**
 360  
      * Sets the the current maximum number of idle transformer objects allowed in the pool
 361  
      *
 362  
      * @param maxIdleTransformers New maximum size to set
 363  
      */
 364  
     public void setMaxIdleTransformers(int maxIdleTransformers)
 365  
     {
 366  6
         transformerPool.setMaxIdle(maxIdleTransformers);
 367  6
     }
 368  
 
 369  
     /**
 370  
      * Gets the parameters to be used when applying the transformation
 371  
      *
 372  
      * @return a map of the parameter names and associated values
 373  
      * @see javax.xml.transform.Transformer#setParameter(java.lang.String,
 374  
      *      java.lang.Object)
 375  
      */
 376  
     public Map getTransformParameters()
 377  
     {
 378  6
         return transformParameters;
 379  
     }
 380  
 
 381  
     /**
 382  
      * Sets the parameters to be used when applying the transformation
 383  
      *
 384  
      * @param transformParameters a map of the parameter names and associated values
 385  
      * @see javax.xml.transform.Transformer#setParameter(java.lang.String,
 386  
      *      java.lang.Object)
 387  
      */
 388  
     public void setTransformParameters(Map transformParameters)
 389  
     {
 390  10
         this.transformParameters = transformParameters;
 391  10
     }
 392  
 
 393  
     /**
 394  
      * <p>
 395  
      * Returns the value to be set for the parameter. This method is called for each
 396  
      * parameter before it is set on the transformer. The purpose of this method is to
 397  
      * allow dynamic parameters related to the event (usually message properties) to be
 398  
      * used. Any attribute of the current UMOEventContext can be accessed using JXPath.
 399  
      * </p>
 400  
      * <p>
 401  
      * For example: If the current event's message has a property named "myproperty", to
 402  
      * pass this in you would set the transform parameter's value to be
 403  
      * "#getProperty(message,'myproperty')".
 404  
      * </p>
 405  
      * <p>
 406  
      * Example Configuration:
 407  
      * </p>
 408  
      * <p/>
 409  
      * <pre>
 410  
      *  &lt;transformer name=&quot;MyXsltTransformer&quot; className=&quot;org.mule.transformers.xml.XsltTransformer&quot;&amp;gt
 411  
      *      &lt;properties&gt;
 412  
      *          &lt;property name=&quot;xslFile&quot; value=&quot;myXslFile.xsl&quot;/&amp;gt
 413  
      *          &lt;map name=&quot;transformParameters&quot;&amp;gt
 414  
      *              &lt;property name=&quot;myParameter&quot; value=&quot;#getProperty(message,'myproperty')&quot;/&amp;gt
 415  
      *          &lt;/map&amp;gt
 416  
      *      &lt;/properties&amp;gt
 417  
      *  &lt;/transformer&amp;gt
 418  
      * </pre>
 419  
      * <p/>
 420  
      * <p>
 421  
      * Only parameter values that begin with # are evalued in this maner. Values that do
 422  
      * not start with # are returned as is. Values that start with ## are returned as is
 423  
      * starting from the second character. For example "##myparameter" would be passed
 424  
      * into the transformer as "#myparameter"
 425  
      * </p>
 426  
      * <p>
 427  
      * This method may be overloaded by a sub class to provide a different dynamic
 428  
      * parameter implementation.
 429  
      * </p>
 430  
      *
 431  
      * @param name  the name of the parameter
 432  
      * @param value the value of the paramter
 433  
      * @return the object to be set as the parameter value
 434  
      * @throws TransformerException
 435  
      */
 436  
     protected Object evaluateTransformParameter(String name, Object value) throws TransformerException
 437  
     {
 438  4
         if (value instanceof String)
 439  
         {
 440  4
             String stringValue = (String) value;
 441  
 
 442  4
             if (!stringValue.startsWith(PARAM_EVAL_TOKEN))
 443  
             {
 444  2
                 return stringValue;
 445  
 
 446  
             }
 447  2
             else if (stringValue.startsWith(PARAM_EVAL_TOKEN + PARAM_EVAL_TOKEN))
 448  
             {
 449  0
                 return stringValue.substring(1);
 450  
 
 451  
             }
 452  
             else
 453  
             {
 454  
 
 455  2
                 UMOEventContext context = RequestContext.getEventContext();
 456  
 
 457  2
                 if (context == null)
 458  
                 {
 459  0
                     throw new TransformerException(CoreMessages.noCurrentEventForTransformer(), this);
 460  
                 }
 461  
 
 462  2
                 JXPathContext jxpathContext = JXPathContext.newContext(context);
 463  2
                 return jxpathContext.getValue(stringValue.substring(1));
 464  
             }
 465  
         }
 466  
 
 467  0
         return value;
 468  
     }
 469  
 
 470  64
     private class LocalURIResolver implements URIResolver
 471  
     {
 472  
         public Source resolve(String href, String base)
 473  
                 throws javax.xml.transform.TransformerException
 474  
         {
 475  
             try
 476  
             {
 477  58
                 return new StreamSource(IOUtils.getResourceAsStream(href, getClass()));
 478  
             }
 479  0
             catch (IOException e)
 480  
             {
 481  0
                 throw new javax.xml.transform.TransformerException(e);
 482  
             }
 483  
         }
 484  
     }
 485  
 }