Home » synapse-1.2-src » org.apache.synapse.mediators.transform » [javadoc | source]

    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one
    3    *  or more contributor license agreements.  See the NOTICE file
    4    *  distributed with this work for additional information
    5    *  regarding copyright ownership.  The ASF licenses this file
    6    *  to you under the Apache License, Version 2.0 (the
    7    *  "License"); you may not use this file except in compliance
    8    *  with the License.  You may obtain a copy of the License at
    9    *
   10    *   http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    *  Unless required by applicable law or agreed to in writing,
   13    *  software distributed under the License is distributed on an
   14    *   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15    *  KIND, either express or implied.  See the License for the
   16    *  specific language governing permissions and limitations
   17    *  under the License.
   18    */
   19   
   20   package org.apache.synapse.mediators.transform;
   21   
   22   import org.apache.axiom.om.OMAbstractFactory;
   23   import org.apache.axiom.om.OMElement;
   24   import org.apache.axiom.om.OMNode;
   25   import org.apache.axiom.om.impl.builder.StAXOMBuilder;
   26   import org.apache.axiom.om.impl.dom.DOOMAbstractFactory;
   27   import org.apache.axiom.om.impl.dom.jaxp.DocumentBuilderFactoryImpl;
   28   import org.apache.axiom.om.util.ElementHelper;
   29   import org.apache.axiom.om.util.StAXUtils;
   30   import org.apache.axiom.soap.SOAP11Constants;
   31   import org.apache.axiom.soap.SOAP12Constants;
   32   import org.apache.axiom.soap.SOAPEnvelope;
   33   import org.apache.axiom.soap.impl.builder.StAXSOAPModelBuilder;
   34   import org.apache.axis2.AxisFault;
   35   import org.apache.synapse.MessageContext;
   36   import org.apache.synapse.SynapseException;
   37   import org.apache.synapse.config.Entry;
   38   import org.apache.synapse.config.SynapseConfigUtils;
   39   import org.apache.synapse.mediators.AbstractMediator;
   40   import org.apache.synapse.mediators.MediatorProperty;
   41   import org.apache.synapse.util.xpath.SynapseXPath;
   42   import org.apache.synapse.util.AXIOMUtils;
   43   import org.apache.synapse.util.TemporaryData;
   44   import org.apache.synapse.util.TextFileDataSource;
   45   import org.jaxen.JaxenException;
   46   import org.w3c.dom.Element;
   47   import org.w3c.dom.Node;
   48   
   49   import javax.xml.parsers.ParserConfigurationException;
   50   import javax.xml.stream.XMLStreamException;
   51   import javax.xml.stream.XMLStreamReader;
   52   import javax.xml.transform;
   53   import javax.xml.transform.dom.DOMResult;
   54   import javax.xml.transform.dom.DOMSource;
   55   import javax.xml.transform.stream.StreamResult;
   56   import java.io;
   57   import java.nio.charset.Charset;
   58   import java.util.ArrayList;
   59   import java.util.Iterator;
   60   import java.util.List;
   61   
   62   /**
   63    * The XSLT mediator performs an XSLT transformation requested, using
   64    * the current message. The source attribute (if available) specifies the source element
   65    * on which the transformation would be applied. It will default to the first child of
   66    * the messages' SOAP body, if it is omitted.
   67    *
   68    * Additional properties passed into this mediator would become parameters for XSLT.
   69    * Additional features passed into this mediator would become features except for
   70    * "http://ws.apache.org/ns/synapse/transform/feature/dom" for the Transformer Factory, which
   71    * is used to decide between using DOM and Streams during the transformation process. By default
   72    * this is turned on as an optimization, but should be set to false if issues are detected
   73    *
   74    *  Note: Set the TransformerFactory system property to generate and use translets
   75    *  -Djavax.xml.transform.TransformerFactory=org.apache.xalan.xsltc.trax.TransformerFactoryImpl
   76    * 
   77    */
   78   public class XSLTMediator extends AbstractMediator {
   79   
   80       /**
   81        * The feature for which deciding swiching between DOM and Stream during the
   82        * transformation process
   83        */
   84       public static final String USE_DOM_SOURCE_AND_RESULTS =
   85           "http://ws.apache.org/ns/synapse/transform/feature/dom";
   86       /**
   87        * The resource key/name which refers to the XSLT to be used for the transformation
   88        */
   89       private String xsltKey = null;
   90   
   91       /** Variable to hold source XPath string to use for debugging */
   92       private String sourceXPathString = null;
   93   
   94       /**
   95        * The (optional) XPath expression which yields the source element for a transformation
   96        */
   97       private SynapseXPath source = null;
   98   
   99       /**
  100        * The name of the message context property to store the transformation result  
  101        */
  102       private String targetPropertyName = null;
  103   
  104       /**
  105        * Any parameters which should be passed into the XSLT transformation
  106        */
  107       private List<MediatorProperty> properties = new ArrayList<MediatorProperty>();
  108   
  109       /**
  110        * Any features which should be set to the TransformerFactory by explicitly
  111        */
  112       private List<MediatorProperty> explicitFeatures = new ArrayList<MediatorProperty>();
  113   
  114       /**
  115        * The Template instance used to create a Transformer object. This is  thread-safe
  116        *
  117        * @see javax.xml.transform.Templates
  118        */
  119       private Templates cachedTemplates = null;
  120   
  121       /**
  122        * The TransformerFactory instance which use to create Templates...This is not thread-safe.
  123        * @see javax.xml.transform.TransformerFactory
  124        */
  125       private final TransformerFactory transFact = TransformerFactory.newInstance();
  126   
  127       /**
  128        * Lock used to ensure thread-safe creation and use of the above Transformer
  129        */
  130       private final Object transformerLock = new Object();
  131   
  132       /**
  133        *  Is it need to use DOMSource and DOMResult?
  134        */
  135       private boolean useDOMSourceAndResults = false;
  136   
  137       /**
  138        * Default XPath for the selection of the element for the evaluation of the XSLT over
  139        */
  140       public static final String DEFAULT_XPATH = "s11:Body/child::*[position()=1] | " +
  141               "s12:Body/child::*[position()=1]";
  142   
  143       public XSLTMediator() {
  144           // create the default XPath
  145           try {
  146               this.source = new SynapseXPath(DEFAULT_XPATH);
  147               this.source.addNamespace("s11", SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI);
  148               this.source.addNamespace("s12", SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI);
  149           } catch (JaxenException e) {
  150               String msg = "Error creating default source XPath expression : " + DEFAULT_XPATH;
  151               log.error(msg, e);
  152               throw new SynapseException(msg, e);
  153           }
  154       }
  155   
  156       /**
  157        * Transforms this message (or its element specified as the source) using the
  158        * given XSLT transformation
  159        *
  160        * @param synCtx the current message where the transformation will apply
  161        * @return true always
  162        */
  163       public boolean mediate(MessageContext synCtx) {
  164   
  165           boolean traceOn = isTraceOn(synCtx);
  166           boolean traceOrDebugOn = isTraceOrDebugOn(traceOn);
  167   
  168           if (traceOrDebugOn) {
  169               traceOrDebug(traceOn, "Start : XSLT mediator");
  170   
  171               if (traceOn && trace.isTraceEnabled()) {
  172                   trace.trace("Message : " + synCtx.getEnvelope());
  173               }
  174           }
  175   
  176           try {
  177               performXSLT(synCtx, traceOrDebugOn, traceOn);
  178   
  179           } catch (Exception e) {
  180               handleException("Unable to perform XSLT transformation using : " + xsltKey +
  181                   " against source XPath : " +
  182                   (sourceXPathString == null ? DEFAULT_XPATH : " source XPath : " +
  183                    sourceXPathString), e, synCtx);
  184   
  185           }
  186   
  187           if (traceOrDebugOn) {
  188               traceOrDebug(traceOn, "End : XSLT mediator");
  189           }
  190   
  191           return true;
  192       }
  193   
  194       /**
  195        * Perform actual XSLT transformation
  196        * @param synCtx current message
  197        * @param traceOrDebugOn is trace or debug on?
  198        * @param traceOn is trace on?
  199        */
  200       private void performXSLT(MessageContext synCtx, final boolean traceOrDebugOn,
  201           final boolean traceOn) {
  202   
  203           boolean reCreate = false;
  204           OMNode sourceNode = getTransformSource(synCtx);
  205           TemporaryData tempTargetData = null;
  206           OutputStream osForTarget;
  207           boolean isSoapEnvelope = (sourceNode == synCtx.getEnvelope());
  208           boolean isSoapBody = (sourceNode == synCtx.getEnvelope().getBody());
  209   
  210           if (traceOrDebugOn) {
  211               trace.trace("Transformation source : " + sourceNode.toString());
  212           }
  213   
  214           Source transformSrc;
  215           Result transformTgt = null;
  216   
  217           if (useDOMSourceAndResults) {
  218               if (traceOrDebugOn) {
  219                   traceOrDebug(traceOn, "Using a DOMSource for transformation");
  220               }
  221   
  222               // for fast transformations create a DOMSource - ** may not work always though **
  223               transformSrc = new DOMSource(
  224                   ((Element) ElementHelper.importOMElement((OMElement) sourceNode,
  225                   DOOMAbstractFactory.getOMFactory())).getOwnerDocument());
  226               DocumentBuilderFactoryImpl.setDOOMRequired(true);
  227   
  228               try {
  229                   transformTgt = new DOMResult(
  230                       DocumentBuilderFactoryImpl.newInstance().newDocumentBuilder().newDocument());
  231               } catch (ParserConfigurationException e) {
  232                   handleException("Error creating a DOMResult for the transformation," +
  233                       " Consider setting optimization feature : " + USE_DOM_SOURCE_AND_RESULTS +
  234                       " off", e, synCtx);
  235               }
  236   
  237           } else {
  238               if (traceOrDebugOn) {
  239                   traceOrDebug(traceOn, "Using byte array serialization for transformation");
  240               }
  241   
  242               transformSrc = AXIOMUtils.asSource(sourceNode);
  243               
  244               tempTargetData = synCtx.getEnvironment().createTemporaryData();
  245               osForTarget = tempTargetData.getOutputStream();
  246               transformTgt = new StreamResult(osForTarget);
  247           }
  248   
  249           if (transformTgt == null) {
  250               if (traceOrDebugOn) {
  251                   traceOrDebug(traceOn, "Was unable to get a javax.xml.transform.Result created");
  252               }
  253               return;
  254           }
  255   
  256           // build transformer - if necessary
  257           Entry dp = synCtx.getConfiguration().getEntryDefinition(xsltKey);
  258   
  259           // if the xsltKey refers to a dynamic resource
  260           if (dp != null && dp.isDynamic()) {
  261               if (!dp.isCached() || dp.isExpired()) {
  262                   reCreate = true;
  263               }
  264           }
  265   
  266           synchronized (transformerLock) {
  267               if (reCreate || cachedTemplates == null) {
  268                   try {
  269                       cachedTemplates = transFact.newTemplates(
  270                           SynapseConfigUtils.getStreamSource(synCtx.getEntry(xsltKey)));
  271                       if (cachedTemplates == null) {
  272                           handleException("Error compiling the XSLT with key : " + xsltKey, synCtx);
  273                       }
  274                   } catch (Exception e) {
  275                       handleException("Error creating XSLT transformer using : "
  276                           + xsltKey, e, synCtx);
  277                   }
  278               }
  279           }
  280   
  281           try {
  282               // perform transformation
  283               Transformer transformer = cachedTemplates.newTransformer();
  284               if (!properties.isEmpty()) {
  285                   // set the parameters which will pass to the Transformation
  286                   for (MediatorProperty prop : properties) {
  287                       if (prop != null) {
  288                           if (prop.getValue() != null) {
  289                               transformer.setParameter(prop.getName(), prop.getValue());
  290                           } else {
  291                               transformer.setParameter(prop.getName(),
  292                                       prop.getExpression().stringValueOf(synCtx));
  293                           }
  294                       }
  295                   }
  296               }
  297   
  298               transformer.setErrorListener(new ErrorListener() {
  299   
  300                   public void warning(TransformerException e) throws TransformerException {
  301   
  302                       if (traceOrDebugOn) {
  303                           traceOrDebugWarn(
  304                                   traceOn, "Warning encountered during transformation : " + e);
  305                       }
  306                   }
  307                   
  308                   public void error(TransformerException e) throws TransformerException {
  309                       log.error("Error occured in XSLT transformation : " + e);
  310                       throw e;
  311                   }
  312                   
  313                   public void fatalError(TransformerException e) throws TransformerException {
  314                       log.error("Fatal error occured in the XSLT transformation : " + e);
  315                       throw e;
  316                   }
  317               });
  318               
  319               transformer.transform(transformSrc, transformTgt);
  320   
  321               if (traceOrDebugOn) {
  322                   traceOrDebug(traceOn, "Transformation completed - processing result");
  323               }
  324   
  325               // get the result OMElement
  326               OMElement result = null;
  327               if (transformTgt instanceof DOMResult) {
  328   
  329                   Node node = ((DOMResult) transformTgt).getNode();
  330                   if (node == null) {
  331                       if (traceOrDebugOn) {
  332                           traceOrDebug(traceOn, ("Transformation result (DOMResult) was null"));
  333                       }
  334                       return;
  335                   }
  336   
  337                   Node resultNode = node.getFirstChild();
  338                   if (resultNode == null) {
  339                       if (traceOrDebugOn) {
  340                           traceOrDebug(traceOn, ("Transformation result (DOMResult) was empty"));
  341                       }
  342                       return;
  343                   }
  344   
  345                   result = ElementHelper.importOMElement(
  346                       (OMElement) resultNode, OMAbstractFactory.getOMFactory());
  347   
  348               } else {
  349   
  350                   String outputMethod = transformer.getOutputProperty(OutputKeys.METHOD);
  351                   String encoding = transformer.getOutputProperty(OutputKeys.ENCODING);
  352   
  353                   if (traceOrDebugOn) {
  354                       traceOrDebug(traceOn, "output method: " + outputMethod
  355                               + "; encoding: " + encoding);
  356                   }
  357                   
  358                   if ("text".equals(outputMethod)) {
  359                       result = handleNonXMLResult(tempTargetData, Charset.forName(encoding),
  360                                                   traceOrDebugOn, traceOn);
  361                   } else {
  362                       try {
  363                           XMLStreamReader reader = StAXUtils.createXMLStreamReader(
  364                               tempTargetData.getInputStream());
  365                           if (isSoapEnvelope) {
  366                               result = new StAXSOAPModelBuilder(reader).getSOAPEnvelope();
  367                           } else {
  368                               result = new StAXOMBuilder(reader).getDocumentElement();
  369                           }                        
  370       
  371                       } catch (XMLStreamException e) {
  372                           handleException(
  373                               "Error building result element from XSLT transformation", e, synCtx);
  374       
  375                       } catch (IOException e) {
  376                           handleException("Error reading temporary data", e, synCtx);
  377                       }
  378                   }
  379               }
  380   
  381               if (result == null) {
  382                   if (traceOrDebugOn) {
  383                       traceOrDebug(traceOn, "Transformation result was null");
  384                   }
  385                   return;
  386               } else {
  387                   if (traceOn && trace.isTraceEnabled()) {
  388                       trace.trace("Transformation result : " + result.toString());
  389                   }
  390               }
  391   
  392               if (targetPropertyName != null) {
  393                   // add result XML as a message context property to the message
  394                   if (traceOrDebugOn) {
  395                       traceOrDebug(traceOn, "Adding result as message context property : " +
  396                           targetPropertyName);
  397                   }
  398                   synCtx.setProperty(targetPropertyName, result);
  399               } else {
  400                   if (traceOrDebugOn) {
  401                       traceOrDebug(traceOn, "Replace " +
  402                           (isSoapEnvelope ? "SOAP envelope" : isSoapBody ? "SOAP body" : "node")
  403                           + " with result");
  404                   }
  405   
  406                   if (isSoapEnvelope) {
  407                       try {
  408                           synCtx.setEnvelope((SOAPEnvelope) result);
  409                       } catch (AxisFault ex) {
  410                           handleException("Unable to replace SOAP envelope with result", ex, synCtx);
  411                       }
  412   
  413                   } else if (isSoapBody) {
  414                       for (Iterator iter = synCtx.getEnvelope().getBody().getChildElements();
  415                           iter.hasNext(); ) {
  416                           OMElement child = (OMElement) iter.next();
  417                           child.detach();
  418                       }
  419   
  420                       for (Iterator iter = result.getChildElements(); iter.hasNext(); ) {
  421                           OMElement child = (OMElement) iter.next();
  422                           synCtx.getEnvelope().getBody().addChild(child);
  423                       }
  424   
  425                   } else {
  426                       sourceNode.insertSiblingAfter(result);
  427                       sourceNode.detach();
  428                   }
  429               }
  430   
  431           } catch (TransformerException e) {
  432               handleException("Error performing XSLT transformation using : " + xsltKey, e, synCtx);
  433           }
  434       }
  435   
  436       /**
  437        * Return the OMNode to be used for the transformation. If a source XPath is not specified,
  438        * this will default to the first child of the SOAP body i.e. - //*:Envelope/*:Body/child::*
  439        *
  440        * @param synCtx the message context
  441        * @return the OMNode against which the transformation should be performed
  442        */
  443       private OMNode getTransformSource(MessageContext synCtx) {
  444                                   
  445           try {
  446               Object o = source.evaluate(synCtx);
  447               if (o instanceof OMNode) {
  448                   return (OMNode) o;
  449               } else if (o instanceof List && !((List) o).isEmpty()) {
  450                   return (OMNode) ((List) o).get(0);  // Always fetches *only* the first
  451               } else {
  452                   handleException("The evaluation of the XPath expression "
  453                           + source + " did not result in an OMNode", synCtx);
  454               }
  455           } catch (JaxenException e) {
  456               handleException("Error evaluating XPath expression : " + source, e, synCtx);
  457           }
  458           return null;
  459       }
  460   
  461       public SynapseXPath getSource() {
  462           return source;
  463       }
  464   
  465       public void setSource(SynapseXPath source) {
  466           this.source = source;
  467       }
  468   
  469       public String getXsltKey() {
  470           return xsltKey;
  471       }
  472   
  473       public void setXsltKey(String xsltKey) {
  474           this.xsltKey = xsltKey;
  475       }
  476   
  477       public void addProperty(MediatorProperty p) {
  478           properties.add(p);
  479       }
  480       
  481       /**
  482        * to add a feature which need to set to the TransformerFactory
  483        * @param  featureName The name of the feature
  484        * @param isFeatureEnable should this feature enable?
  485        */
  486       
  487       public void addFeature(String featureName, boolean isFeatureEnable) {
  488           try {
  489               MediatorProperty mp = new MediatorProperty();
  490               mp.setName(featureName);
  491               if (isFeatureEnable) {
  492                   mp.setValue("true");
  493               } else {
  494                   mp.setValue("false");
  495               }
  496               explicitFeatures.add(mp);
  497               if (USE_DOM_SOURCE_AND_RESULTS.equals(featureName)) {
  498                   useDOMSourceAndResults = isFeatureEnable;
  499               } else {
  500                   transFact.setFeature(featureName, isFeatureEnable);
  501               }
  502           } catch (TransformerConfigurationException e) {
  503               String msg = "Error occured when setting features to the TransformerFactory";
  504               log.error(msg, e);
  505               throw new SynapseException(msg, e);
  506           }
  507       }
  508   
  509       /**
  510        * If the transformation results in a non-XML payload, use standard wrapper elements
  511        * to wrap the text payload so that other mediators could still process the result
  512        * @param tempData the encoded text payload
  513        * @param charset the encoding of the payload
  514        * @param traceOrDebugOn is tracing on debug logging on?
  515        * @param traceOn is tracing on?
  516        * @return an OMElement wrapping the text payload
  517        */
  518       private OMElement handleNonXMLResult(TemporaryData tempData, Charset charset,
  519           boolean traceOrDebugOn, boolean traceOn) {
  520   
  521           if (traceOrDebugOn) {
  522               traceOrDebug(traceOn, "Processing non SOAP/XML (text) transformation result");
  523           }
  524           if (traceOn && trace.isTraceEnabled()) {
  525               trace.trace("Wrapping text transformation result");
  526           }
  527   
  528           return TextFileDataSource.createOMSourcedElement(tempData, charset);
  529       }
  530   
  531       /**
  532        *
  533        * @return Returns the features explicitly  set to the TransformerFactory through this mediator
  534        */
  535       public List<MediatorProperty> getFeatures(){
  536           return explicitFeatures;
  537       }
  538   
  539       public void addAllProperties(List<MediatorProperty> list) {
  540           properties.addAll(list);
  541       }
  542   
  543       public List<MediatorProperty> getProperties() {
  544           return properties;
  545       }
  546   
  547       public void setSourceXPathString(String sourceXPathString) {
  548           this.sourceXPathString = sourceXPathString;
  549       }
  550   
  551       public String getTargetPropertyName() {
  552           return targetPropertyName;
  553       }
  554   
  555       public void setTargetPropertyName(String targetPropertyName) {
  556           this.targetPropertyName = targetPropertyName;
  557       }
  558       
  559   }
  560   
  561   	

Home » synapse-1.2-src » org.apache.synapse.mediators.transform » [javadoc | source]