Package de.danet.an.workflow.tools.soapclient

Source Code of de.danet.an.workflow.tools.soapclient.SOAPClient

/*
* 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: SOAPClient.java 2904 2009-01-28 22:21:36Z mlipp $
*
* $Log$
* Revision 1.4  2005/09/05 09:41:49  drmlipp
* Synchronized with 1.3.2.
*
* Revision 1.3  2005/08/25 13:24:22  drmlipp
* Synchronized with 1.3.1p6.
*
* Revision 1.2  2005/04/24 18:59:43  mlipp
* Fixed bug with scalar result.
*
* Revision 1.1.1.4  2004/08/18 15:17:39  drmlipp
* Update to 1.2
*
* Revision 1.12  2004/04/16 07:56:19  lipp
* Make sure that we use Axis (even on WLS).
*
* Revision 1.11  2004/04/01 15:21:34  lipp
* Using new tool helpers.
*
* Revision 1.10  2004/03/21 21:30:22  lipp
* Added Document for absolut paths.
*
* Revision 1.9  2004/03/21 20:35:36  lipp
* Using exception signalling.
*
* Revision 1.8  2004/02/21 21:31:01  lipp
* Some more refactoring to resolve cyclic dependencies.
*
* Revision 1.7  2003/09/09 08:33:33  lipp
* Fixed loop bug.
*
* Revision 1.6  2003/06/27 12:46:06  lipp
* Got SOAPClient running.
*
* Revision 1.5  2003/06/27 08:51:44  lipp
* Fixed copyright/license information.
*
* Revision 1.4  2003/06/25 15:50:29  lipp
* New SOAP client that makes wsif obsolete.
*
* Revision 1.3  2003/06/24 22:41:43  lipp
* Fixed loop bug.
*
* Revision 1.2  2003/06/24 16:08:12  lipp
* Implemented invocation.
*
* Revision 1.1  2003/06/23 22:06:20  lipp
* Started alternate SOAP client.
*
*/
package de.danet.an.workflow.tools.soapclient;

import java.io.Serializable;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import java.net.ConnectException;
import java.rmi.RemoteException;

import javax.wsdl.Definition;
import javax.wsdl.Operation;
import javax.wsdl.Part;
import javax.wsdl.Port;
import javax.wsdl.PortType;
import javax.wsdl.Service;
import javax.wsdl.extensions.ExtensibilityElement;
import javax.wsdl.extensions.soap.SOAPAddress;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.xml.rpc.Call;
import javax.xml.rpc.ParameterMode;
import javax.xml.rpc.encoding.DeserializerFactory;
import javax.xml.rpc.encoding.SerializerFactory;
import javax.xml.rpc.encoding.TypeMapping;
import javax.xml.rpc.encoding.TypeMappingRegistry;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import org.jaxen.JaxenException;
import org.jaxen.XPath;
import org.jaxen.jdom.JDOMXPath;
import org.jdom.Document;

import de.danet.an.util.sax.NamespaceAttributesAdder;
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.ActivityUniqueKey;
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.ExceptionMappingProvider;
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 client for RPC style SOAP invocations.
*
* @author <a href="mailto:mnl@mnl.de">Michael N. Lipp</a>
* @version $Revision: 2904 $
*/

public class SOAPClient
    implements ToolAgent, XMLArgumentTypeProvider, ResultProvider,
         ExceptionMappingProvider, Serializable {

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

    private String method = null;
    // hold the mapping of return parameter name and eventually the XPath
    // expression
    private Map returnParamInfo = new HashMap();
    private Definition wsdlDef = null;
    private Service service = null;
    private Port port = null;
    private PortType portType = null;
    private String portLocation = null;
    private Operation operation = null;

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

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

    /**
     * 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;
    }

    /**
     * Set the definition of WSDL. It is used to invoke the defined method
     * dynamically.
     *
     * @param wsdlLocation the location of given WSDL definition.
     */
    public void setWSDL(String wsdlLocation) {
  try {
      if ((wsdlLocation == null) || wsdlLocation.equals("")) {
    return;
      }
      WSDLReader rdr = WSDLFactory.newInstance().newWSDLReader();
      wsdlDef = rdr.readWSDL(wsdlLocation);
  } catch (Exception e) {
      logger.error("Error setting WSDL: " + e.getMessage (), e);
  }
    }

    /**
     * Set the definition of WSDL. It is used to invoke the defined method
     * dynamically.
     *
     * @param wsdl the given WSDL definition in W3C DOM Element.
     */
    public void setWSDL(SAXEventBuffer wevts) {
  try {
      TransformerFactory tf = TransformerFactory.newInstance();
      if (!tf.getFeature(SAXTransformerFactory.FEATURE)) {
          String s = "JAXP transformer factory does not"
              + " support a SAX transformer!";
          logger.fatal (s);
          throw new IllegalStateException (s);
      }
      TransformerHandler th = ((SAXTransformerFactory)tf)
          .newTransformerHandler ();
            DOMResult out = new DOMResult ();
            th.setResult (out);
            wevts.emit(new NamespaceAttributesAdder(th));
            org.w3c.dom.Node node = out.getNode();
            if (node instanceof org.w3c.dom.Document) {
                node = ((org.w3c.dom.Document)node).getDocumentElement();
            }
      WSDLReader rdr = WSDLFactory.newInstance().newWSDLReader();
      wsdlDef = rdr.readWSDL(null, (Element)node);
  } catch (Exception e) {
      logger.error("Error setting WSDL: " + e.getMessage (), e);
  }
    }

    /**
     * Set the xml definition of output mappings. It is used to convert the
     * WSIF invocation response.
     *
     * @param outputMappings the given xml as JDOM Element.
     */
    public void setMappings(Element outputMappings) {
  try {
      // retrieve namespaces
      NodeList namespacesNodeList = outputMappings.getElementsByTagNameNS
    (XPDLUtil.XPDL_EXTN_NS, "Namespaces");
      if (namespacesNodeList.getLength() == 0) {
          namespacesNodeList = outputMappings.getElementsByTagNameNS
                    (XPDLUtil.XPDL_EXTN_V1_1_NS, "Namespaces");
      }
      NodeList nsNodeList = null;
      if (namespacesNodeList.getLength() > 0) {
    Element namespacesNode = (Element)namespacesNodeList.item(0);
    nsNodeList = namespacesNode.getElementsByTagNameNS
        (namespacesNode.getNamespaceURI(), "Namespace");
      }
      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++) {
    Element param = (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++) {
      Element ns = (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);
  }
    }

    /**
     * set the SOAP method to be called with WSIF.
     *
     * @param method the given method.
     */
    public void setMethod (String method) {
  this.method = method;
    }

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

    /* Comment copied from interface. */
    public void invoke(Activity activity, FormalParameter[] formPars, Map map)
  throws RemoteException, CannotExecuteException {
  // Do not attempt to perform any operation on the activity
  // except key() and uniqueKey() before WSIF dynamic
  // invocation. Else, the activity becomes part of the EJB
  // transaction and is locked, i.e. all accesses (even display
  // in the management client) are deferred until the invocation
  // has completed. The time this takes is not controllable.
  ActivityUniqueKey auk = null;
  try {
      if (wsdlDef == null) {
    throw new IllegalStateException("WSDL definition not defined");
      }
      Operation calledOp = operation;
      if (calledOp == null) {
    if (method == null) {
        calledOp = initOperation
      ((String)map.get(formPars[0].id()));
        FormalParameter[] fps
      = new FormalParameter[formPars.length - 1];
        System.arraycopy(formPars, 0, fps, 0, fps.length);
        formPars = fps;
    } else {
        calledOp = initOperation (method);
        operation = calledOp;
    }
      }
      auk = activity.uniqueKey();
      try {
    result.set(invokeOperation (calledOp, formPars, map));
      } catch (RemoteException e) {
          if (logger.isDebugEnabled()) {
              logger.debug ("Cannot invoke: " + e.getMessage (), e);
          }
    throw new CannotExecuteException
      ("Cannot invoke: " + e.getMessage(), e);
      }
  } finally {
      if (logger.isDebugEnabled()) {
    logger.debug ("Finished invocation of " + auk);
      }
  }
    }

    private Operation initOperation (String method)
  throws IllegalStateException {
  if (service == null) {
      Collection services = wsdlDef.getServices().values ();
      if (services.size () == 0) {
    throw new IllegalStateException ("No service defined.");
      }
      if (services.size () > 1) {
    throw new IllegalStateException
        ("More than one service defined.");
      }
      service = (Service)services.iterator().next();
      Collection ports = service.getPorts().values();
      if (ports.size () == 0) {
    throw new IllegalStateException ("No ports defined.");
      }
      if (ports.size () > 1) {
    throw new IllegalStateException("More than one port defined.");
      }
      port = (Port)ports.iterator().next();
      for (Iterator i = port.getExtensibilityElements().iterator();
     i.hasNext ();) {
    ExtensibilityElement ee = (ExtensibilityElement)i.next ();
    if (ee instanceof SOAPAddress) {
        portLocation = ((SOAPAddress)ee).getLocationURI();
    }
      }
      if (portLocation == null) {
    throw new IllegalStateException ("No port location defined.");
      }
      portType = port.getBinding().getPortType ();
  }
  String opName = method;
  String inName = null;
  String outName = null;
  int sepCol = opName.indexOf (':');
  if (sepCol >= 0) {
      inName = opName.substring(sepCol + 1);
      opName = opName.substring(0, sepCol);
      sepCol = inName.indexOf (':');
      if (sepCol >= 0) {
    outName = inName.substring(sepCol + 1);
    inName = inName.substring(0, sepCol);
      }
  }
  Operation operation = null;
  for (Iterator i = portType.getOperations().iterator(); i.hasNext();) {
      Operation op = (Operation) i.next();
      if (!opName.equals(op.getName())
    || (inName != null && !inName.equals (op.getInput().getName()))
    || (outName != null
        && !outName.equals (op.getOutput().getName()))) {
    continue;
      }
      if (operation != null) {
    operation = null;
    throw new IllegalStateException
        ("Operation '" + method + "' is overloaded. "
         + "Please specify the operation in the form "
         + "'operationName:inputMessageName:"
         + "outputMessageName' to distinguish it");
      }
      operation = op;
  }
  if (operation == null) {
      throw new IllegalStateException
    ("Operation \"" + method + "\" not found.");
  }
  return operation;
    }

    /**
     * Invokes the dedicated operation of web services. The Operation return
     * value as Map must have a key named <code>Result</code>, its value is
     * written in the new constructed process data using the key of the out
     * formal 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
  (Operation operation, FormalParameter[] formPars, Map map)
  throws CannotExecuteException, RemoteException {
  try {
//       javax.xml.rpc.Service svc
//     = javax.xml.rpc.ServiceFactory.newInstance()
//     .createService (service.getQName());
      javax.xml.rpc.Service svc
    = (new org.apache.axis.client.ServiceFactory())
    .createService (service.getQName());
      TypeMappingRegistry tmr = svc.getTypeMappingRegistry();
      TypeMapping tm = tmr.getDefaultTypeMapping();
      Call call = svc.createCall ();
      call.setTargetEndpointAddress(portLocation);
      call.setOperationName (new javax.xml.namespace.QName
           (wsdlDef.getTargetNamespace(),
            operation.getName()));
      SerializerFactory mySer = new SAXEventBufferSerializerFactory ();
      DeserializerFactory myDes = new JDOMDeserializerFactory ();
      Set fpns = new HashSet ();
      for (int i = 0; i < formPars.length; i++) {
    if (formPars[i].mode() != FormalParameter.Mode.OUT) {
        fpns.add (formPars[i].id());
    }
      }
      Map parts = operation.getInput().getMessage().getParts();
      List args = new ArrayList ();
      for (Iterator pi = parts.keySet().iterator (); pi.hasNext(); ) {
    String pn = (String)pi.next ();
    if (! fpns.contains(pn)) {
        throw new IllegalStateException
      ("Missing formal parameter for part \"" + pn + "\".");
    }
    fpns.remove (pn);
    Part part = (Part)parts.get (pn);
    Object value = map.get (pn);
    if (value instanceof SAXEventBufferImpl) {
        tm.register(SAXEventBufferImpl.class, part.getTypeName(),
        mySer, myDes);
    }
    call.addParameter (pn, part.getTypeName(), ParameterMode.IN);
    args.add (value);
      }
      if (fpns.size () > 0) {
    String param = (String)fpns.iterator().next();
    throw new IllegalStateException
        ("Extra formal parameter \"" + param + "\".");
      }
      Map outParts = operation.getOutput().getMessage().getParts();
      if (outParts.values().size () == 0) {
    call.setReturnType (org.apache.axis.encoding.XMLType.AXIS_VOID);
      } else {
    Part outPart = (Part)outParts.values().iterator().next();
    if (tm.getDeserializer(null, outPart.getTypeName()) == null) {
        tm.register(SAXEventBufferImpl.class, outPart.getTypeName(),
        mySer, myDes);
    }
    call.setReturnType (outPart.getTypeName());
      }
      Object res = call.invoke (args.toArray());
      // add root; else absolute XPath expressions don't work.
      // But leave res assigne to root element for backward
      // compatibility of relative paths.
      if (res instanceof org.jdom.Element) {
    new Document ((org.jdom.Element)res);
      }
      Map resData = new HashMap ();
      for (int i = 0; i < formPars.length; i++) {
    if (formPars[i].mode() == FormalParameter.Mode.IN) {
        continue;
    }
    XPath path = (XPath)returnParamInfo.get(formPars[i].id());
    if (path == null) {
        resData.put(formPars[i].id(), res);
    } else {
        if (formPars[i].type().equals (String.class)) {
      resData.put(formPars[i].id(), path.stringValueOf(res));
        } else if ((formPars[i].type() instanceof Class)
             && Number.class.isAssignableFrom
             ((Class)formPars[i].type())) {
      Number n = path.numberValueOf(res);
      if (formPars[i].type().equals (Long.class)
          && !(n instanceof Long)) {
          n = new Long (n.longValue());
      }
      resData.put(formPars[i].id(), n);
        } else {
      resData.put(formPars[i].id(), path.selectNodes(res));
        }
    }
      }     
      return resData;
  } catch (JaxenException e) {
      throw new CannotExecuteException
          ("Cannot execute: " + e.getMessage (), e);
  } catch (javax.xml.rpc.ServiceException e) {
      logger.error (e.getMessage (), e);
      throw new CannotExecuteException (e.getMessage ());
  }
    }

    /**
     * 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 () {
  Object res = result.get();
  result.set (null);
  return res;
    }
   
    /* (non-Javadoc)
     * @see ExceptionMappingProvider#exceptionMappings()
     */
    public Collection exceptionMappings() {
        Collection mappings = new ArrayList();
        mappings.add(new ExceptionMapping
                     (RemoteException.class, "RemoteException"));
        return mappings;
    }

    /* Comment copied from interface. */
    public void terminate(Activity activity)
  throws ApplicationNotStoppedException {
  throw new ApplicationNotStoppedException
      ("Terminate not implemented for WSIFDynamicInvocationTool.");
    }
   
}
TOP

Related Classes of de.danet.an.workflow.tools.soapclient.SOAPClient

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.