Package de.danet.an.workflow.tools

Source Code of de.danet.an.workflow.tools.XSLTTool

/*
* This file is part of the WfMOpen project.
* Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* $Id: XSLTTool.java 2904 2009-01-28 22:21:36Z mlipp $
*
* $Log$
* Revision 1.5  2006/09/29 12:32:10  drmlipp
* Consistently using WfMOpen as projct name now.
*
* Revision 1.4  2006/03/08 14:46:44  drmlipp
* Synchronized with 1.3.3p5.
*
* Revision 1.3  2005/08/11 15:16:27  drmlipp
* Fixed problem with XSLTTool and namespaces.
*
* Revision 1.2.2.1  2005/08/09 15:46:10  drmlipp
* Fixed problem with XSLTTool and namespaces in output parameter
* mappings.
*
* Revision 1.2  2005/01/21 09:37:35  drmlipp
* Fixed log level.
*
* Revision 1.1.1.2  2004/08/18 15:17:38  drmlipp
* Update to 1.2
*
* Revision 1.28  2004/07/01 13:44:34  lipp
* Minor caching improvement.
*
* Revision 1.27  2004/03/31 19:36:20  lipp
* Completed implementation of Activity.abandon(String).
*
* Revision 1.26  2004/02/26 09:42:36  lipp
* Fixed startElement parameters.
*
* Revision 1.25  2004/02/20 09:53:01  lipp
* Fixed exception handling.
*
* Revision 1.24  2004/02/19 13:10:32  lipp
* Clarified start-/endDocument usage in SAXEventBuffers.
*
* Revision 1.23  2004/02/17 15:28:51  lipp
* Various improvements.
*
* Revision 1.22  2004/02/16 15:38:51  lipp
* Fixed handling of non-well formed result values.
*
* Revision 1.21  2004/02/13 10:01:34  lipp
* Changed result type for result provider to Map which is more
* appropriate.
*
* Revision 1.20  2004/01/28 14:55:09  lipp
* Minor corrections.
*
* Revision 1.19  2004/01/28 09:25:49  montag
* additional testcase added.
*
* Revision 1.18  2004/01/28 08:34:33  montag
* new functionality for merging multiple xml documents in one.
*
* Revision 1.17  2004/01/27 17:38:30  montag
* BodyFiller corrected (handling of missing endDocument event).
*
* Revision 1.16  2004/01/27 12:11:08  montag
* XSLTTool now implements ResultProvider interface.
*
* Revision 1.15  2003/07/14 09:01:19  montag
* error corrected setting the result type
* if there is no output mapping defined.
*
* Revision 1.14  2003/07/14 07:55:09  montag
* clean up the code.
*
* Revision 1.13  2003/07/11 15:08:45  montag
* create transformer during setting of xslt
* and cache it for repeated tool invocation.
*
* Revision 1.12  2003/07/11 14:35:04  montag
* remove code for file handling for
* the xslt parameter.
*
* Revision 1.11  2003/07/11 14:07:09  montag
* return SAXEventBufferImpl if no output
* mapping is defined.
*
* Revision 1.10  2003/07/10 14:42:13  montag
* xlst transformation als SAXEventBuffer.
*
* Revision 1.9  2003/07/04 09:09:09  montag
* debug logging inserted.
*
* Revision 1.8  2003/07/03 12:53:50  montag
* url handling corrected.
*
* Revision 1.7  2003/07/02 15:01:59  montag
* documentation completed.
*
* Revision 1.6  2003/07/02 13:50:09  montag
* Property XSLT no longer mandatory.
*
* Revision 1.5  2003/07/02 11:53:13  montag
* Support for additional IN parameters.
*
* Revision 1.4  2003/07/02 09:30:38  montag
* handling of complex out parameters
* with no defined mapping fixed.
*
* Revision 1.3  2003/07/02 07:33:50  montag
* first working version of the XSLTTool.
*
* Revision 1.2  2003/07/01 15:49:55  montag
* jaxen-dom for XSLTTool.
*
* Revision 1.1  2003/07/01 14:28:43  montag
* initial of XSLTTool.
*
*
*/
package de.danet.an.workflow.tools;

import java.io.Serializable;
import java.io.StringWriter;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TemplatesHandler;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.jaxen.JaxenException;
import org.jaxen.XPath;
import org.jaxen.jdom.JDOMXPath;
import org.jdom.Document;
import org.jdom.input.SAXHandler;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.XMLFilterImpl;

import de.danet.an.util.XMLUtil;
import de.danet.an.util.sax.BodyFilter;

import de.danet.an.workflow.util.SAXEventBufferImpl;
import de.danet.an.workflow.util.XPDLUtil;

import de.danet.an.workflow.api.Activity;
import de.danet.an.workflow.api.FormalParameter;
import de.danet.an.workflow.api.SAXEventBuffer;

import de.danet.an.workflow.spis.aii.ApplicationNotStoppedException;
import de.danet.an.workflow.spis.aii.CannotExecuteException;
import de.danet.an.workflow.spis.aii.ResultProvider;
import de.danet.an.workflow.spis.aii.ToolAgent;
import de.danet.an.workflow.spis.aii.XMLArgumentTypeProvider;

/**
* This class provides a tool that performs a transformation
* according to the JAXP 1.1 specification.
*
* @author <a href="mailto:montag@danet.de"></a>
* @version $Revision: 2904 $
*/

public class XSLTTool
    implements ToolAgent, XMLArgumentTypeProvider,
         ResultProvider, Serializable {

    private static final org.apache.commons.logging.Log logger
  = org.apache.commons.logging.LogFactory.getLog(XSLTTool.class);

    // hold the mapping of return parameter name and eventually the XPath
    // expression (intialized from property)
    private Map returnParamInfo = new HashMap();
    // holds the Transformer source property for the stylesheet
    private SAXEventBufferImpl xsltSource = null;

    // holds sax transformer factory
    private SAXTransformerFactory saxTransFactCache = null;

    // holds the parsed stylesheet
    private Templates templatesCache = null;

    /** The result container. */
    private ThreadLocal result = new ThreadLocal ();

    /**
     * A filter that suppresses any characters before the first
     * startElement. Normally there shouldn't be any, but somehow
     * sometimes there are.
     */
    private class BodyCleaner extends XMLFilterImpl {
  private boolean gotFirst = false;
  public BodyCleaner(ContentHandler contentHandler) {
      setContentHandler(contentHandler);
  }
  public void characters(char[] charArray, int start, int length)
      throws SAXException {
      if (gotFirst) {
    getContentHandler().characters (charArray, start, length);
      }
  }
  public void startElement(String nsUri, String lname, String qname,
         Attributes attributes) throws SAXException {
      gotFirst = true;
      getContentHandler().startElement (nsUri, lname, qname, attributes);
  }
    }


    /**
     * Creates an instance of <code>XSLTTool</code>
     * with all attributes initialized to default values.
     */
    public XSLTTool() { 
    }

    private SAXTransformerFactory saxTransFact ()
  throws TransformerConfigurationException {
  synchronized (result) {
      if (saxTransFactCache == null) {
    saxTransFactCache = (SAXTransformerFactory)
        TransformerFactory.newInstance();
      }
      return saxTransFactCache;
  }
    }

    /**
     * Set the definition of XSLT. It is used to invoke the stylesheet
     * dynamically.
     * on windows, something like
     * "file:/C:/Entwicklung/WfMOpen/systemtest/tools/testXSLT.xsl"
     * on unix, something like "file:/usr/dir/test.xsl"
     *
     * @param xsltLocation the location of given XSL as an URL.
     */
    public void setXSLT(String xsltLocation) {
  try {
      if ((xsltLocation == null) || xsltLocation.equals("")) {
    return;
      }
      // check for URL
      URL url = null;
      try {
    url = new URL(xsltLocation);
      } catch (MalformedURLException mue) {
    // url == null
      }
      if (url != null) {
    String systemID = url.toExternalForm();
    if (logger.isDebugEnabled()) {
        logger.debug("systemID = " + systemID);
    }
    xsltSource
        = convertToSAXEventBuffer(new StreamSource(systemID));
      } else {
    logger.error("Error setting xslt: "
           + xsltLocation + "is no valid url");
      }
  } catch (Exception e) {
      logger.error("Error setting xslt: " + e.getMessage (), e);
  }
    }

    /**
     * Create a SaxBufferEventImpl from a given source.
     * @param source The source.
     * @return SaxBufferEventImpl for the source
     * @throws TransformerConfigurationException
     * @throws TransformerException
     */
    private SAXEventBufferImpl convertToSAXEventBuffer(Source source)
  throws TransformerConfigurationException, TransformerException {
  SAXEventBufferImpl b = new SAXEventBufferImpl();
  SAXResult result = new SAXResult(b);
  TransformerFactory.newInstance().newTransformer()
      .transform(source, result);
  return b;
    }
   
    /**
     * Set the definition of XSLT.
     *
     * @param xslt the given XSLT definition
     */
    public void setXSLT(SAXEventBuffer xslt) {
  xsltSource = (SAXEventBufferImpl)xslt;
    }

    /**
     * Get a transformer handler for this tool.
     */
    private Templates templates() throws CannotExecuteException {
  try {
      synchronized (result) {
    if (templatesCache == null && xsltSource != null) {
        TemplatesHandler templHand
      = saxTransFact().newTemplatesHandler();
        xsltSource.emit(templHand);
        // Now the TemplatesHandler contains the xslt information
        templatesCache = templHand.getTemplates();
    }
      }
      return templatesCache;
  } catch (TransformerConfigurationException e) {
      String msg = "Error creating TransformerHandler: " + e.getMessage();
      logger.error(msg, e);
      throw new CannotExecuteException (msg);
  } catch (SAXException e) {
      String msg = "Error creating TransformerHandler: " + e.getMessage();
      logger.error(msg, e);
      throw new CannotExecuteException (msg);
  }
    }

    /**
     * Set the xml definition of output mappings. It is used to convert the
     * result of the transformation process.
     *
     * @param outputMappings the given xml as JDOM Element.
     */
    public void setMappings(org.w3c.dom.Element outputMappings) {
  try {
      // retrieve namespaces
      org.w3c.dom.NodeList namespacesNodeList = outputMappings
    .getElementsByTagNameNS (XPDLUtil.XPDL_EXTN_NS, "Namespaces");
      if (namespacesNodeList.getLength() == 0) {
          namespacesNodeList = outputMappings.getElementsByTagNameNS
              (XPDLUtil.XPDL_EXTN_V1_1_NS, "Namespaces");         
      }
      org.w3c.dom.NodeList nsNodeList = null;
      if (namespacesNodeList.getLength() > 0) {
    org.w3c.dom.Element namespacesNode
        = (org.w3c.dom.Element)namespacesNodeList.item(0);
    nsNodeList = namespacesNode.getElementsByTagNameNS
        (namespacesNode.getNamespaceURI(), "Namespace");
      }
      org.w3c.dom.NodeList paramNodeList = outputMappings
    .getElementsByTagNameNS (XPDLUtil.XPDL_EXTN_NS, "Parameter");
      if (paramNodeList.getLength() == 0) {
          paramNodeList = outputMappings.getElementsByTagNameNS
              (XPDLUtil.XPDL_EXTN_V1_1_NS, "Parameter");
      }
      for (int i = 0; i < paramNodeList.getLength(); i++) {
    org.w3c.dom.Element param
        = (org.w3c.dom.Element)paramNodeList.item(i);
    String name = param.getAttribute("Name");
    XPath xpath = new JDOMXPath(param.getAttribute("Select"));
    if (nsNodeList != null) {
        for (int j = 0; j < nsNodeList.getLength(); j++) {
      org.w3c.dom.Element ns
          = (org.w3c.dom.Element)nsNodeList.item(j);
      String prefix = ns.getAttribute("Prefix");
      String uri = ns.getAttribute("Uri");
      xpath.addNamespace(prefix, uri);
        }
    }
    returnParamInfo.put(name, xpath);
      }
  } catch (Exception e) {
      // if any error ocurred, outputMappings is still null.
      logger.error("error in setting XML for output mappings!", e);
  }
    }

    // Implementation of de.danet.an.workflow.spis.aii.XMLArgumentTypeProvider

    /**
     * Return the requested type for XML arguments.
     * @return one of <code>XML_AS_W3C_DOM</code>,
     * <code>XML_AS_JDOM</code> or <code>XML_AS_SAX</code>
     */
    public int requestedXMLArgumentType() {
  return XMLArgumentTypeProvider.XML_AS_SAX;
    }

    // Implementation of de.danet.an.workflow.spis.aii.ToolAgent

    /**
     * Describe <code>invoke</code> method here.
     *
     * @param activity a <code>WfActivity</code> value
     * @param formPars the formal parameters.
     * @param map a <code>Map</code> value
     * @throws CannotExecuteException if an error occurs
     * @throws RemoteException if a system level error occurs
     */
    public void invoke(Activity activity, FormalParameter[] formPars, Map map)
  throws CannotExecuteException, RemoteException {
  try {
      result.set (invokeOperation(formPars, map));
  } catch (JaxenException je) {
      logger.error (je.getMessage());
      logger.debug (je.getMessage(), je);
      throw new CannotExecuteException (je.getMessage());
  } finally {
      if (logger.isDebugEnabled()) {
    logger.debug ("Finished invocation of " + activity.uniqueKey());
      }
  }
    }
   
    /**
     * Invoke the transformation through the JAXP 1.1 API.
     * The first input paramater of type <code>SAXEventBufferImpl</code>
     * will be treaten as the content to be transformed; additional input
     * parameters will be handled as additional parameter to the
     * transformation process.<p>
     * If no stylesheet is explicitly set, the transformation simply "echoes"
     * the source to the result.<p>
     * The result of the transformation is of type <code>DOMResult</code>.
     * After the transformation, the output parameters are filled with the
     * result, possibly applying a mapping afterward. The return values are
     * written in the new constructed process data using the key of the out
     * formal parameters.
     * If there is at least one mapping defined, any return value of complex
     * type will be filled with an element object. If there is no output
     * mapping defined, then there must at least one output parameter with
     * a complex type; in this case an object of type SAXEventBufferImpl is
     * created and the result is set with tihs object.
     *
     * @param formPars formal parameters
     * @param map actual parameters
     * @return the new process data with the result of web services operation
     * included.
     * @throws WSIFDynamicInvokerException if any errors in invoking web
     * service occurred.
     */
    private Map invokeOperation(FormalParameter[] formPars, Map map)
  throws JaxenException, CannotExecuteException {
  // Determine the content to be transformed
  boolean firstInFound = false;
  String newRootTag = null;
  // Holds all additional parameter for the transformation
  Map transformParameters = new HashMap();
  // Holds all content for the transformation
  List contentParameters = new ArrayList();
  for (int i = 0; i < formPars.length; i++) {
      if (formPars[i].mode() != FormalParameter.Mode.OUT) {
    // IN or INOUT
    String key = formPars[i].id();
    Object value = map.get(key);
    // check first IN parameter for operational mode
    if (!firstInFound) {
        if (value instanceof String) {
      newRootTag = (String)value;
        }
        firstInFound = true;
    }
    if (value instanceof SAXEventBuffer) {
        // add new content
        contentParameters.add(value);
    } else {
        // add new parameter
        transformParameters.put(key, value);
    }
      }
  }

  SAXEventBufferImpl seb = new SAXEventBufferImpl ();
  try {
      SAXResult saxResult = new SAXResult (new BodyCleaner(seb));
      Templates tmplts = templates ();
      TransformerHandler th = null;
      if (tmplts == null) {
    th = saxTransFact().newTransformerHandler();
      } else {
    th = saxTransFact().newTransformerHandler(tmplts);
      }
      setTransformerParameters(th.getTransformer(), transformParameters);
      th.setResult(saxResult);
      if (newRootTag == null) {
    // simple input tree
    ((SAXEventBuffer)contentParameters.get(0)).emit(th);
      } else {
    // create new start element
    th.startDocument();
    th.startElement
        ("", newRootTag, newRootTag, new AttributesImpl());
    // content
    Iterator it = contentParameters.iterator();
    while (it.hasNext()) {
        SAXEventBuffer c = (SAXEventBuffer)it.next();
        c.emit(new BodyFilter(th));
    }
    th.endElement("", newRootTag, newRootTag);
    th.endDocument();
      }
      seb.pack();
  } catch (TransformerConfigurationException e) {
      String msg = "Error creating TransformerHandler: " + e.getMessage();
      logger.error(msg, e);
      throw new CannotExecuteException (msg);
  } catch (SAXException se) {
      String msg = "Error during transformation: " + se.getMessage();
      logger.error (msg, se);
      throw new CannotExecuteException (msg);
  }
  if (logger.isDebugEnabled()) {
      logTransformationResult (seb);
  }
 
  // assemble the result, do additional mapping
        Map resData = new HashMap ();
  Document jdomRes = null;
  for (int i = 0; i < formPars.length; i++) {
      if (logger.isDebugEnabled()) {
    logger.debug("formPars[i].id() = " + formPars[i].id());
    logger.debug("formPars[i].type() = " + formPars[i].type());
    logger.debug("formPars[i].mode() = " + formPars[i].mode());
      }
      if (formPars[i].mode() == FormalParameter.Mode.IN) {
    continue;
      }
      XPath path = (XPath)returnParamInfo.get(formPars[i].id());
      if (logger.isDebugEnabled()) {
    logger.debug("path = " + path);
      }
      if (path == null) {
    resData.put(formPars[i].id(), seb);
    continue;
      }
      // Handle path expression
      if (jdomRes == null) {
    try {
        SAXHandler hdlr = new SAXHandler ();
        seb.emit (hdlr);
        jdomRes = hdlr.getDocument();
    } catch (SAXException e) {
        String msg = "Problem converting SAX to JDOM: "
      + e.getMessage ();
        logger.error (msg, e);
        throw new CannotExecuteException (msg);
    }
      }
      if (formPars[i].type().equals(String.class)) {
    String value = path.stringValueOf(jdomRes);
    if (logger.isDebugEnabled()) {
        logger.debug("value = " + value);
    }
    resData.put(formPars[i].id(), value);
            } else if (formPars[i].type().equals(Date.class)) {
                String sval = path.stringValueOf(jdomRes);
                try {
                    Date value = XMLUtil.parseXsdDateTime(sval);
                    if (logger.isDebugEnabled()) {
                        logger.debug("value = " + value);
                    }
                    resData.put(formPars[i].id(), value);
                } catch (ParseException e) {
                    throw (CannotExecuteException)
                        (new CannotExecuteException
                         ("Problem parsing " + sval + " as xsd datetime: "
                          + e.getMessage())).initCause(e);
                }
            } else if (formPars[i].type().equals(Double.class)) {
                String sval = path.stringValueOf(jdomRes);
                try {
                    double value = XMLUtil.parseXsdDouble(sval);
                    if (logger.isDebugEnabled()) {
                        logger.debug("value = " + value);
                    }
                    resData.put(formPars[i].id(), new Double(value));
                } catch (NumberFormatException e) {
                    throw (CannotExecuteException)
                        (new CannotExecuteException
                         ("Problem parsing " + sval + " as xsd double: "
                          + e.getMessage())).initCause(e);
                }
            } else if (formPars[i].type().equals(Boolean.class)) {
                String sval = path.stringValueOf(jdomRes);
                try {
                    boolean value = XMLUtil.parseXsdBoolean(sval);
                    if (logger.isDebugEnabled()) {
                        logger.debug("value = " + value);
                    }
                    resData.put(formPars[i].id(), new Boolean(value));
                } catch (ParseException e) {
                    throw (CannotExecuteException)
                        (new CannotExecuteException
                         ("Problem parsing " + sval + " as xsd boolean: "
                          + e.getMessage())).initCause(e);
                }
      } else if ((formPars[i].type() instanceof Class)
           && Number.class.isAssignableFrom
           ((Class)formPars[i].type())) {
    Number n = path.numberValueOf(jdomRes);
    if (formPars[i].type().equals (Long.class)
        && !(n instanceof Long)) {
        n = new Long (n.longValue());
    }
    resData.put(formPars[i].id(), n);
      } else {
    List selected = path.selectNodes (jdomRes);
    resData.put(formPars[i].id(), selected);
      }
  }     
  return resData;
    }

    /**
     * Sets the additional parameter for the transformation process.
     * @param transformer the transformer
     * @param transformParameters the additional transform parameters
     */
    private void setTransformerParameters(Transformer transformer,
            Map transformParameters) {
  // set additional parameters
  transformer.clearParameters();
  if (!transformParameters.isEmpty()) {
      Iterator it = transformParameters.keySet().iterator();
      while(it.hasNext()) {
    String key = (String)it.next();
    Object value = transformParameters.get(key);
    if (logger.isDebugEnabled()) {
        logger.debug("Set additional transformer parameter: "
         + " (" + key + ", " + value + ")");
    }
    transformer.setParameter(key, value);
      }
  }
    }

    /**
     * Describe <code>terminate</code> method here.
     *
     * @param activity a <code>WfActivity</code> value
     * @throws ApplicationNotStoppedException if the application could
     * not be terminated.
     */
    public void terminate(Activity activity)
  throws ApplicationNotStoppedException {
  throw new ApplicationNotStoppedException
      ("Terminate not implemented for XSLTTool.");
    }

    /**
     * Return the result evaluated during {@link ToolAgent#invoke
     * <code>invoke</code>}. The method will only be called once after
     * each invoke, i.e. the attribute holding the result be be
     * cleared in this method.
     *
     * @return the result data or <code>null</code> if the invocation
     * does not return any data.
     */
    public Object result () {
  Map res = (Map)result.get();
  result.set (null);
  return res;
    }

    /**
     * Log the result of the transformtion to System.out.
     * @param domResult the dom result
     */
    private void logTransformationResult(SAXEventBuffer seb) {
  try {
      StringWriter sw = new StringWriter();
      StreamResult streamResult = new StreamResult(sw);
      SAXTransformerFactory tf
    = (SAXTransformerFactory)TransformerFactory.newInstance();
      TransformerHandler serializer = tf.newTransformerHandler();
      serializer.getTransformer()
    .setOutputProperty(OutputKeys.ENCODING,"ISO-8859-1");
      serializer.getTransformer()
    .setOutputProperty(OutputKeys.INDENT,"yes");
      serializer.setResult (streamResult);
      seb.emit (serializer);
      logger.debug("TransformationResult:\n" + sw);
  } catch (Exception e) {
      logger.warn(e);
  }
    }

}
TOP

Related Classes of de.danet.an.workflow.tools.XSLTTool

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.