/////////////////////////////////////////////////////////////////////////
//
// © University of Southampton IT Innovation Centre, 2004
//
// Copyright in this library belongs to the IT Innovation Centre of
// 2 Venture Road, Chilworth Science Park, Southampton, SO16 7NP, UK.
//
// This software may not be used, sold, licensed, transferred, copied
// or reproduced in whole or in part in any manner or form or in or
// on any media by any person other than in accordance with the terms
// of the Licence Agreement supplied with the software, or otherwise
// without the prior written consent of the copyright owners.
//
// This software is distributed WITHOUT ANY WARRANTY, without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE, except where stated in the Licence Agreement supplied with
// the software.
//
//      Created by:             Darren Marvin
//      Created date:           2003/09/20
//      Created for project:    GEMSS
//
/////////////////////////////////////////////////////////////////////////
//
//      Dependencies: None
//
/////////////////////////////////////////////////////////////////////////
//
//      Last commit info:       $Author: $
//                              $Date: $
//                              $Revision: $
//
/////////////////////////////////////////////////////////////////////////

package uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.codec.providers.soap;

import eu.gemss.components.transport.payload.*;
import eu.gemss.components.transport.servicedescription.*;
import eu.gemss.components.transport.servicedescription.interfacetypes.*;
import java.io.*;
import java.util.*;
import java.util.logging.*;
import javax.activation.*;
import javax.wsdl.*;
import javax.wsdl.factory.*;
import javax.xml.namespace.*;
import javax.xml.parsers.*;
import javax.xml.soap.*;
import javax.xml.soap.SOAPMessage;
import org.jdom.input.*;
import org.jdom.output.*;
import org.w3c.dom.*;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
import uk.ac.soton.itinnovation.gemss.transportmessaging.connection.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.Message;
import uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.codec.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.context.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.payload.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.payload.serialisation.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.payload.typedescriptions.*;
import uk.ac.soton.itinnovation.gemss.utils.xml.soap.*;
import org.apache.xml.utils.NameSpace;
import org.apache.axis.Constants;




/**
 * SOAPMessageCoDec encodes and decodes SOAP messages. It uses the JAXM interface
 * classes to do it.
 */
public class SOAPMessageCoDec implements MessageCoDec {

    private static Logger mLogger = Logger.getLogger("uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.codec.SOAPMessageCoDec");


    public Message encodeMessage(ServiceInterface intrface, Payload payload, MessageContext msgContext) throws MessageCoDecException, InvalidOperationException, InputArgumentException, ConnectionException {
	    Message message = null;
            Connection connection = null;
	    try{
		    //check that the service interface is a WSDL type
		    if(!(intrface instanceof WSDL11Interface)) {
			    mLogger.log(Level.SEVERE,"Trying to create a SOAP message without passing the correct interface type, this is probably a bug please send a bug report with all log files");
			    throw new MessageCoDecException("Trying to create a SOAP message without passing the correct interface type, this is probably a bug please send a bug report with all log files");
		    }
		    WSDL11Interface intf = (WSDL11Interface) intrface;
		    Document domDoc = null;

		    // check for cached WSDL
		    if(!intf.WSDLDocCached()) {
                      try{
                        //load and cache the WSDL file
                        ConnectionFactory connectionFactory = ConnectionFactory.getInstance();
                        connection = connectionFactory.createConnection(intf.getWSDLURI(),msgContext.getSecurityContext());
                        InputStream inStream = connection.getResourceInputStream();
                        DocumentBuilderFactory factoryDoc = DocumentBuilderFactory.newInstance();
                        factoryDoc.setNamespaceAware(true);//this is extremely important for WSDL validation using WSDL4J
                        factoryDoc.setValidating(false);
                        DocumentBuilder builder = factoryDoc.newDocumentBuilder();
                        domDoc = builder.parse(inStream);
                        //release connection
                        connection.releaseConnection();
                        intf.setWSDLDocument(domDoc);
                      }
                      catch(org.xml.sax.SAXException ex) {
                        mLogger.log(Level.SEVERE,"Problem reading WSDL file for request, this is most likely because the server has failed and is not returning a WSDL file",ex);
                        throw new MessageCoDecException("Problem reading WSDL file for request, this is most likely because the server has failed and is not returning a WSDL file");
                      }
                      catch(javax.xml.parsers.ParserConfigurationException ex) {
                        mLogger.log(Level.SEVERE,"Problem parsing WSDL file at " + intf.getWSDLURI(),ex);
                        throw new MessageCoDecException("Problem parsing WSDL file at " + intf.getWSDLURI());
                      }
		    }
		    else {
			    domDoc = intf.getWSDLDocument();
		    }

                    Definition wsdlDefinition = null;
		    // validate the WSDL
		    wsdlDefinition = validateWSDLRequest(domDoc,intf,payload);

                    //assume data handler for now.
                    message = generateDataHandlerMimeMessage(wsdlDefinition,intf,payload,msgContext);
                    return message;
	    }
            catch(java.io.IOException ex) {
                    mLogger.log(Level.SEVERE,"Problem creating SOAP message for request, you should send a bug report including all log files",ex);
                    throw new MessageCoDecException("Problem creating SOAP message for request, you should send a bug report including all log files");
            }
            catch(ServiceDescriptionException ex) {
                    mLogger.log(Level.SEVERE,ex.getMessage(),ex);
                    throw new MessageCoDecException(ex.getMessage());
            }
            finally {
                if(connection!=null)
                    connection.releaseConnection();
            }
    }


    public Payload decodeMessage(ServiceInterface intrface, Message message, Map fileHandleMap, ConnectionProperties connectionProps) throws MessageCoDecException, ServiceException {
        Payload payload = null;

	    try{

                List  dataItems = new ArrayList();
		    //create the message first


                uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.SOAPMessage soapMsg = (uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.SOAPMessage) message;
                    Document doc = null;

                    //check for fault contents
		    WSDL11Interface intf = (WSDL11Interface) intrface;


                    //assuming this is a SOAP message, is this wise????
                    validateWSDLResponse(intf,soapMsg.getSOAPWireMessage());


                    payload = generatePayloadFromDataHandlerMimeMessage(intf, soapMsg, fileHandleMap);


	    }
            catch(MessagingException ex) {

                throw new MessageCoDecException(ex.getMessage());
            }
	    return payload;
    }

    private Payload generatePayloadFromDataHandlerMimeMessage(WSDL11Interface intf, uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.SOAPMessage soapMsg, Map fileHandleMap) throws MessageCoDecException {
        Payload payload = null;
        try{

            //create a payload, not sure if following check is necessary really - need to look a RPC / DOC soap in bit more detail
            if(intf.isRPC()) {
                //pull off the attachments so that the basic soapMsg can be converted to a DOM object
                //convert to map of datahandlers and content ids
                List attachmentParts = new ArrayList();
                Iterator attachmentIterator = soapMsg.getSOAPWireMessage().getAttachments();


                Document doc = soapMsg.getDOMPart();

                //Element msgE = convertToDOMElement(soapMsg);
                Element msgE = doc.getDocumentElement();
                DOMBuilder dBuilder = new org.jdom.input.DOMBuilder();
                org.jdom.Element msgElement = dBuilder.build(msgE);


                List children = msgElement.getChildren();
                //retrieve the body child
                Iterator iterator = children.iterator();
                org.jdom.Element bodyE = null;
                while(iterator.hasNext()) {
                    Object obj = iterator.next();
                    if(obj instanceof org.jdom.Element) {
                        org.jdom.Element e = (org.jdom.Element) obj;
                        if(e.getName().equals("Body")) {
                            bodyE = e;
                        }
                    }
                }
                if(bodyE==null) {
                    mLogger.log(Level.SEVERE,"Could not obtain parts of the response message, please send a bug report including all log files");
                    throw new MessageCoDecException("Could not obtain parts of the response message, please send a bug report including all log files");
                }


                //retrieve the response message child
                children = bodyE.getChildren();
                iterator = children.iterator();
                org.jdom.Element respE = null;
                while(iterator.hasNext()) {
                    Object obj = iterator.next();
                    if(obj instanceof org.jdom.Element) {
                        org.jdom.Element e = (org.jdom.Element) obj;
                        respE = e;
                    }
                }
                if(respE==null) {
                    mLogger.log(Level.SEVERE,"Could not obtain parts of the response message, please send a bug report including all log files");
                    throw new MessageCoDecException("Could not obtain parts of the response message, please send a bug report including all log files");
                }

                List dataItemElements = new ArrayList();
                children = respE.getChildren();
                iterator = children.iterator();

                while(iterator.hasNext()) {
                    Object obj = iterator.next();
                    if(obj instanceof org.jdom.Element) {
                        org.jdom.Element e = (org.jdom.Element) obj;
                        dataItemElements.add(e);
                    }
                }
                if(dataItemElements.size()==0) {
                    //there might not be a return child if the call returns no output, previous validation should have
                    //confirmed this so if there is not return then simply create an empty Payload
                    //retrieve the return child
                    payload = new PayloadImpl();
                }
                else {
                    //there is a return child part

                    Iterator dataIterator = dataItemElements.iterator();
                    List dataItems = new ArrayList(dataItemElements.size());
                    while(dataIterator.hasNext()) {
                        org.jdom.Element retE = (org.jdom.Element) dataIterator.next();
                        Element w3cE = (new DOMOutputter()).output(retE);
                        String dataId = retE.getName();
                        //convert it

                        //is the return value only a reference e.g. to attachment
                        String hrefAttr = w3cE.getAttribute("href");
                        if(hrefAttr==null || hrefAttr.equals("")) {
                            //not a reference so should take value out of the body
                            //query the translator registry for a supporting translator
                            TranslatorRegistry registry = TranslatorRegistry.getInstance();
                            //get the actual node value, it is the first child node that is either a text node or element node
                            /*
                            NodeList partChildren = w3cE.getChildNodes();

                            //QName qName = new QName(w3cE.getLocalName(),w3cE.getNamespaceURI());
                            //first of all try to get any child elements
                            NodeList childEs = w3cE.getElementsByTagName("*");
                            Node selectedNode = null;
                            if(childEs.getLength()>0)  {
                              //should only be one.
                               selectedNode = childEs.item(0);
                            }
                            if(selectedNode==null) {
                              for(int i=0;i<partChildren.getLength();i++) {
                                Node nd = partChildren.item(i);
                                if(nd.getNodeType()==Node.TEXT_NODE) {
                                  selectedNode = nd;

                                }
                              }
                            }
                            if(selectedNode==null) {
                              mLogger.log(Level.SEVERE,"Unable to deserialize the returned data described as '" + retE.getName() + "'");
                              throw new MessageCoDecException("Unable to deserialize the returned data described as '" + retE.getName() + "'");
                            }
                            */
                            DataItemDOMTranslator translator = registry.getDataItemDOMTranslator(w3cE);
                            if(translator==null) {
                              mLogger.log(Level.SEVERE,"No translator exist to deserialize the returned data described as '" + retE.getName() + "'");
                              throw new MessageCoDecException("No translator exist to deserialize the returned data described as '" + retE.getName() + "'");
                            }
                            dBuilder = new org.jdom.input.DOMBuilder();
                            Attr typeAtt = w3cE.getAttributeNodeNS(Constants.URI_2001_SCHEMA_XSI,"type");
                            String typeStr = null;
                            if(typeAtt==null) {
                              typeStr = w3cE.getAttribute("type");
                            }
                            else {
                              typeStr = typeAtt.getValue();
                            }

                            QName qName = null;
                            if(typeStr!=null)
                               qName = new QName(Constants.URI_DEFAULT_SCHEMA_XSD, typeStr);
                            else {
                              //use the qname of the actual selected node
                              if(w3cE.getNodeType()==Node.ELEMENT_NODE) {
                                Element selE = (Element) w3cE;
                                qName = new QName(selE.getLocalName(),selE.getNamespaceURI());
                              }
                            }
                            DataItem dataItem = translator.outputDataItem(w3cE,qName);

                            dataItems.add(dataItem);

                        }
                        else {
                            //it is a reference have to grab the refered to item
                            //there should be a matching SOAP attachment with the same id
                            //find the attachment


                            while(attachmentIterator.hasNext()) {
                                AttachmentPart aP = (AttachmentPart) attachmentIterator.next();

                                String contentId = aP.getContentId();
                                if(contentId.equals(hrefAttr.substring(4))) {
                                    //create the dataItem
                                    //save the file out

                                    DataHandler dH = soapMsg.getDataHandlerForContentId(contentId);
                                    //find the correct file handle
                                    Set partKeys = fileHandleMap.keySet();
                                    Iterator keyIterator = partKeys.iterator();
                                    String fileHandle = null;
                                    while(keyIterator.hasNext()) {
                                        String key = (String) keyIterator.next();
                                        if(key.equals(dataId)) {
                                            fileHandle = (String) fileHandleMap.get(key);
                                        }
                                    }
                                    DataItem dataItem = null;
                                    if(fileHandle!=null) {

                                        BufferedOutputStream bufOut = new BufferedOutputStream(new FileOutputStream(new File(fileHandle)));
                                        dH.writeTo(bufOut);
                                        bufOut.flush();
                                        bufOut.close();
                                        dataItem = new DataItemImpl(retE.getName(),new XSDTypeDescription("String",XSDTypeDescription.XSD_STRING),fileHandle,true);
                                    }
                                    else {
                                        throw new MessageCoDecException("Received an attachment as a response for '" + dataId + "' but no matching destination file was provided");
                                    }
                                    if(dataItem!=null) {
                                        dataItems.add(dataItem);
                                    }

                                }
                            }
                        }
                    }

                    //generate payload
                    DataItem[] itemArray = new DataItem[dataItems.size()];
                    iterator = dataItems.iterator();
                    int index = 0;
                    while(iterator.hasNext()) {
                        itemArray[index] = (DataItem) iterator.next();
                        index++;
                    }
                    payload = new PayloadImpl(itemArray);
                }
            }
            else {
                //to implement some time in the future, probably using JAXM
                mLogger.log(Level.SEVERE,"Trying to use Document SOAP but it is not supported yet");
                throw new MessageCoDecException("Trying to use Document SOAP but it is not supported yet");
            }
            return payload;
        }
           catch(javax.xml.soap.SOAPException ex) {
                   mLogger.log(Level.SEVERE,"Problem reading response SOAP message, could be a configuration problem please check your configuration",ex);
                   throw new MessageCoDecException("Problem reading response SOAP message, could be a configuration problem please check your configuration");
           }
           catch(java.io.IOException ex) {
                   mLogger.log(Level.SEVERE,"Problem reading response SOAP message, you should send a bug report including all log files",ex);
                   throw new MessageCoDecException("Problem reading response SOAP message, you should send a bug report including all log files");
           }
           catch(PayloadException ex) {
                   mLogger.log(Level.SEVERE,ex.getMessage(),ex);
                   throw new MessageCoDecException(ex.getMessage());
           }
           catch(org.jdom.JDOMException ex) {
                   mLogger.log(Level.SEVERE,"Problem reading response SOAP message, you should send a bug report including all log files",ex);
                   throw new MessageCoDecException("Problem reading response SOAP message, you should send a bug report including all log files");
           }

           catch(javax.xml.parsers.ParserConfigurationException ex) {
                   mLogger.log(Level.SEVERE,"Problem reading response SOAP message, you should send a bug report including all log files",ex);
                   throw new MessageCoDecException("Problem reading response SOAP message, you should send a bug report including all log files");
           }
           catch(org.xml.sax.SAXException ex) {
                   mLogger.log(Level.SEVERE,"Problem reading response SOAP message, you should send a bug report including all log files",ex);
                   throw new MessageCoDecException("Problem reading response SOAP message, you should send a bug report including all log files");
           }

           catch(MessagingException ex) {
               mLogger.log(Level.SEVERE,"Problem reading response SOAP message, could be a configuration problem please check your configuration",ex);
               throw new MessageCoDecException(ex.getMessage());
            }
    }

    private Message generateDataHandlerMimeMessage(Definition wsdlDefinition, WSDL11Interface intf, Payload payload, MessageContext msgContext) throws MessageCoDecException {
        uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.SOAPMessage message = null;
        try{

            ConnectionProperties connectionProperties = new ConnectionProperties();
            //generate a SOAPMessage, at present the only implementation that supports the SAAJ 1.2 api is the reference
            //implementation and it is rubbish. I will go across to only assuming SAAJ1.2 API when Axis catches up.

            MessageFactory msgFactory = null;
            try{
              msgFactory = MessageFactory.newInstance();
            }
            catch(javax.xml.soap.SOAPException ex) {
              //this happens within the GEMSS component framework because Axis does not do class-loading correctly.
              //rather than:
              //classloader = Thread.currentThread().getContextClassLoader();
              //they should use:
              //classloader=this.getClass().getClassLoader();
              //this applies to all the SAAJ classes that use newInstance() methods.
              //try to directly use the axis one
              mLogger.log(Level.INFO,"Unable to dynamically load SOAP SAAJ Provider, going to try to load Axis directly",ex);
              try{
                msgFactory = new org.apache.axis.soap.MessageFactoryImpl();
              }
              catch(Exception e) {
                mLogger.log(Level.SEVERE,"Unable to load a suitable SOAP SAAJ Provider, please check the libraries used",e);
                throw new MessageCoDecException("Unable to load a suitable SOAP SAAJ Provider, please check the libraries used");
              }
              if(msgFactory==null) {
                mLogger.log(Level.SEVERE,"Unable to load a suitable SOAP SAAJ Provider, please check the libraries used");
                throw new MessageCoDecException("Unable to load a suitable SOAP SAAJ Provider, please check the libraries used");
              }
            }
            SOAPMessage soapMsg = msgFactory.createMessage();
            SOAPPart soapPart = soapMsg.getSOAPPart();

            SOAPEnvelope soapEnv = soapPart.getEnvelope();

            //have to set the namespace declarations for the definitions section of the WSDL file?
            //not sure where to get it from, targetNamespace??? WSDL4J might be able to help.
            //soapEnv.addNamespaceDeclaration();
            SOAPBody soapBody = soapEnv.getBody();
            SOAPFactory soapFactory = null;

            try{
              soapFactory = SOAPFactory.newInstance();
            }
            catch(javax.xml.soap.SOAPException ex) {
              //this happens within the GEMSS component framework because Axis does not do class-loading correctly.
              //rather than:
              //classloader = Thread.currentThread().getContextClassLoader();
              //they should use:
              //classloader=this.getClass().getClassLoader();
              //this applies to all the SAAJ classes that use newInstance() methods.
              //try to directly use the axis one
              mLogger.log(Level.INFO,"Unable to dynamically load SOAP SAAJ Provider, going to try to load Axis directly",ex);
              try{
                soapFactory = new org.apache.axis.soap.SOAPFactoryImpl();
              }
              catch(Exception e) {
                mLogger.log(Level.SEVERE,"Unable to load a suitable SOAP SAAJ Provider, please check the libraries used",e);
                throw new MessageCoDecException("Unable to load a suitable SOAP SAAJ Provider, please check the libraries used");
              }
              if(msgFactory==null) {
                mLogger.log(Level.SEVERE,"Unable to load a suitable SOAP SAAJ Provider, please check the libraries used");
                throw new MessageCoDecException("Unable to load a suitable SOAP SAAJ Provider, please check the libraries used");
              }
            }

            Name bodyName = soapFactory.createName(intf.getOperationName(),"m",wsdlDefinition.getTargetNamespace());
            SOAPBodyElement methodE = soapBody.addBodyElement(bodyName);

            //default is to assume no attachments, following will be overwritten if attachments exist
            connectionProperties.addProperty("Content-Type","text/xml");
            connectionProperties.addProperty("SOAPAction","\"\"");

            if(intf.isRPC()) {
              DataItem[] dataItems = payload.getDataItems();
              for(int i=0;i<dataItems.length;i++) {
                //check to see if the wsdl requires datahandler type
                if(useDataHandler(wsdlDefinition,intf,dataItems[i].getName())) {
                  //the value object can be supplied either as object of or as referenced file, dataitem descides this
                  if(dataItems[i].isValueFileReference()) {
                    //add a href attribute to the operation part
                    String dName = dataItems[i].getName();
                    AttachmentPart ap = soapMsg.createAttachmentPart(new DataHandler(new FileDataSource((String) dataItems[i].getValue())));
                    if(ap.getContentId()==null) {
                      ap.setContentId(dName);
                    }
                    Name partName = soapEnv.createName(dName,bodyName.getPrefix(),bodyName.getURI());
                    SOAPElement pE = methodE.addChildElement(partName);
                    //Name hrefAttName = soapEnv.createName("href",bodyName.getPrefix(),bodyName.getURI());
                    Name hrefAttName = soapEnv.createName("href");
                    pE.addAttribute(hrefAttName,"cid:" + ap.getContentId());
                    soapMsg.addAttachmentPart(ap);
                    soapMsg.saveChanges();

                  }
                  else {
                    //this is not too hard but not doing it quite yet.
                    throw new MessageCoDecException("Don't support value insertion into attachment yet");
                  }
                }
                else {
                  //the value object can be supplied either as object of or as referenced file, dataitem descides this
                  if(dataItems[i].isValueFileReference()) {
                    throw new MessageCoDecException("Don't support value references for direct inclusion in a message at the present time");
                  }
                  else {

                    DataItemDOMTranslator translator = dataItems[i].getDataItemDOMTranslator();

                    Node serNode = translator.outputDOM(dataItems[i]);

                    //add to registry just in case type pops up again
                    TranslatorRegistry registry = TranslatorRegistry.getInstance();
                    registry.addDataItemDOMTranslator(translator);

                    //now put the data value in the soap message


                    if(serNode.getNodeType()==Node.ELEMENT_NODE){
                      Element serE = (Element) serNode;

                      if(serE.getChildNodes().item(0)!=null && serE.getChildNodes().item(0).getNodeType()==Node.TEXT_NODE) {
                        /*
                        following should work but there is some cross talk in the GEMSS component framework classloading meaning
                        that it can't
                        */
                        //methodE.addChildElement(SAAJUtils.createSOAPElementFromDOMElement(soapFactory,soapEnv,serE));
                        /*
                         * Use following as workaround
                         */
                        Text txt = (Text) serE.getChildNodes().item(0);
                        Name partName = soapFactory.createName(dataItems[i].getName(),bodyName.getPrefix(),bodyName.getURI());
                        SOAPElement se = methodE.addChildElement(partName);
                        se.addTextNode(txt.getNodeValue());
                      }
                      else {
                        //System.out.println("Translator generated\n '" + org.apache.axis.utils.XMLUtils.ElementToString(serE) +"'");
                        Name partName = soapFactory.createName(dataItems[i].getName(),bodyName.getPrefix(),bodyName.getURI());
                        SOAPElement se = methodE.addChildElement(partName);
                        se.addChildElement(SAAJUtils.createSOAPElementFromDOMElement(soapFactory,soapEnv,serE));
                        //System.out.println("SAAJUtils generated SOAPMessage\n");
                        /*
                        try{
                          soapMsg.writeTo(new FileOutputStream("soap.out"));
                        }
                        catch(Exception ex) {

                        }
                        */
                      }
                    }
                    else {

                        mLogger.log(Level.SEVERE,"Serialiser generated an unsupported serialisation of an payload");
                        throw new MessageCoDecException("Unable to serialise a passed input payload");

                    }
                  }
                }
              }
              soapMsg.saveChanges();



                //make a Message object containing the SOAPEnvelope
                msgContext.setConnectionProperties(connectionProperties);
                message = new uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.SOAPMessage(soapMsg,msgContext);

            }
            else {
                //to implement some time in the future, probably using JAXM
                mLogger.log(Level.SEVERE,"Trying to use Document SOAP but it is not supported yet");
                throw new MessageCoDecException("Trying to use Document SOAP but it is not supported yet");
            }


            return message;
        }
        catch(MessagingException ex) {
            throw new MessageCoDecException(ex.getUserMessage(),ex.getSystemMessage(),ex.isFatal());
        }
        catch(javax.xml.soap.SOAPException ex) {
            mLogger.log(Level.SEVERE,"Problem creating SOAP message for request, you should send a bug report including all log files",ex);
            throw new MessageCoDecException("Problem creating SOAP message for request, you should send a bug report including all log files");
        }
        catch(PayloadException ex) {
            mLogger.log(Level.SEVERE,ex.getMessage(),ex);
            throw new MessageCoDecException(ex.getMessage());
        }

    }


    private boolean useDataHandler(Definition wsdlDefinition, WSDL11Interface intf, String partName) {
        try{
            Map pTMap = wsdlDefinition.getPortTypes();
            Set keys = pTMap.keySet();
            Iterator keyIterator = keys.iterator();
            while(keyIterator.hasNext()) {
                QName ptQname = (QName) keyIterator.next();
                if(ptQname.getLocalPart().equals(intf.getPortType())) {
                    PortType pT = wsdlDefinition.getPortType(ptQname);
                    List ops = pT.getOperations();
                    Iterator iterator = ops.iterator();
                    while(iterator.hasNext()) {
                        Operation op = (Operation) iterator.next();
                        if(op.getName().equals(intf.getOperationName())) {
                            //check for the correct input part
                            javax.wsdl.Message inputMsg = op.getInput().getMessage();
                            Part prt = inputMsg.getPart(partName);
                            if(prt!=null) {
                                QName type = prt.getTypeName();
                                if(type.getNamespaceURI().equals(org.apache.axis.Constants.MIME_DATA_HANDLER.getNamespaceURI()) &&
                                   type.getLocalPart().equals(org.apache.axis.Constants.MIME_DATA_HANDLER.getLocalPart()))
                                    return true;
                            }
                        }
                    }
                }
            }
        }
        catch(Exception ex) {
            //assume not so
            mLogger.log(Level.WARNING,"Unable to identify if a datahandler is need by the service from the supplied WSDL",ex);
        }
        return false;
    }





    private Definition validateWSDLRequest(org.w3c.dom.Document wsdlDoc,WSDL11Interface intf, Payload payload) throws InvalidOperationException, InputArgumentException {
	    Definition defn;
	    Binding binding;
	    PortType pT;
	    Operation op;
	    javax.wsdl.Message msg;
	    javax.wsdl.xml.WSDLReader wsdlReader;
	    Map partMap;
	    Input input;
	    Set keySet;
	    Iterator iterator;
	    DataItem[] dataItems;
	    boolean exist;
	    String name, partName, bindingPT;

	    try{
		    //need to check that the true service is not document soap!!!

		    //want to check that the interface has operation, portType and binding values that exist in the WSDL doc
		    //create a WSDL model
		    //Ignoring namespace uris for the following QNames - could be a problem, but don't actually know the namespace URI or how to get it.
		    wsdlReader = WSDLFactory.newInstance().newWSDLReader();
		    synchronized(this) {
                      defn = wsdlReader.readWSDL(null,wsdlDoc);
		    }

		    binding = defn.getBinding(new QName(defn.getTargetNamespace(),intf.getWSDLBinding()));
		    if(binding == null) {
			    mLogger.log(Level.SEVERE,"Binding '" + intf.getWSDLBinding() + "' within the WSDL service request does not exist in the associated WSDL definition.");
			    throw new InvalidOperationException("Binding '" + intf.getWSDLBinding() + "' within the WSDL service request does not exist in the associated WSDL definition.");
		    }
		    pT = binding.getPortType();
		    bindingPT = pT.getQName().getLocalPart();

		    if(!bindingPT.equals(intf.getPortType())) {
			    mLogger.log(Level.SEVERE,"PortType pointed to by Binding '" + intf.getWSDLBinding() + "' is '" + bindingPT + "' but request wanted portType '" + intf.getPortType() + "'");
			    throw new InvalidOperationException("PortType pointed to by Binding '" + intf.getWSDLBinding() + "' is '" + bindingPT + "' but request wanted portType '" + intf.getPortType() + "'");
		    }
		    op = pT.getOperation(intf.getOperationName(),null,null); //not interested in input and output names
		    if(op==null) {
			    mLogger.log(Level.SEVERE,"Operation '" + intf.getOperationName() + " within the WSDL service request does not exist in the requested PortType.");
			    throw new InvalidOperationException("Operation '" + intf.getOperationName() + " within the WSDL service request does not exist in the requested PortType.");
		    }

		    //want to check that all parts are represented within the payload
		    //retrieve the input message for the operation
		    input = op.getInput();
		    msg = input.getMessage();
		    partMap = msg.getParts();
		    keySet = partMap.keySet();
		    iterator = keySet.iterator();
		    exist = false;
		    while(iterator.hasNext()) {
			    partName = (String) iterator.next();
			    dataItems = payload.getDataItems();
			    for(int i=0;i<dataItems.length;i++) {
				    name = dataItems[i].getName();
				    if(name.equals(partName)) {
					    exist = true;
					    break;
				    }
			    }
			    if(!exist) {
				    mLogger.log(Level.SEVERE,"Operation for service request requires input part with name '" + partName + "' but no matching data item was supplied");
				    throw new InputArgumentException("Operation for service request requires input part with name '" + partName + "' but no matching data item was supplied");
			    }
		    }
		    return defn;
	    }
	    catch(javax.wsdl.WSDLException ex) {
		    mLogger.log(Level.SEVERE,"Unable to parse the WSDL document associated with the service, this may be a bug please send a bug report including all logs",ex);
                    throw new InvalidOperationException("Unable to parse the WSDL document associated with the service, this may be a bug please send a bug report including all logs", "Unable to parse the WSDL document associated with the service, this may be a bug please send a bug report including all logs. Exception message '" + ex.getMessage() + "'");
            }
            catch(NullPointerException ex) {
              mLogger.log(Level.SEVERE,"Unable to parse the WSDL document associated with the service, this may be a bug please send a bug report including all logs",ex);
              throw new InvalidOperationException("Unable to parse the WSDL document associated with the service, please check that it is correct", "Unable to parse the WSDL document associated with the service, please check that it is correct. Exception message '" + ex.getMessage() + "'");
            }
    }

    private void validateWSDLResponse(WSDL11Interface intf, SOAPMessage message) throws InvalidOperationException, ServiceException, MessagingException {
        SOAPPart soapPart = null;
        SOAPEnvelope  soapEnv = null;
        SOAPBody soapBody = null;
        SOAPFault soapFault;
        String faultStr, actorStr;

        try{

            //want to be able to handle SOAP faults much better!!!
            try{
              soapPart = message.getSOAPPart();
                if(soapPart==null) {
                    mLogger.log(Level.SEVERE,"Could not obtain a SOAP part from the SOAP message object");
                    throw new WSDLValidationException("A problem has occured while validating the response, please send a bug report including all log files");
                }
                soapEnv = soapPart.getEnvelope();
                if(soapEnv==null) {
                    mLogger.log(Level.SEVERE,"Could not obtain a SOAP envelope from the SOAP part object");
                    throw new WSDLValidationException("A problem has occured while validating the response, please send a bug report including all log files");
                }
                soapBody = soapEnv.getBody();
                if(soapBody==null) {
                    mLogger.log(Level.SEVERE,"Could not obtain a SOAP body from the SOAP part object");
                    throw new WSDLValidationException("A problem has occured while validating the response, please send a bug report including all log files");
                }
            }
            catch(ClassCastException ex) {
                //There seems to be a bug in the SUN Reference implementation when dealing with faults.
                mLogger.log(Level.SEVERE,"A problem has occured while generating a fault, it is most likely due to JAXM RI bug, no fault information available",ex);
                throw new WSDLValidationException("A problem occured while validating the service response, please send a bug report including all logfiles");
            }
            try{
                if(soapBody.hasFault()) {
                    soapFault = soapBody.getFault();
                    String faultCode = soapFault.getFaultCode();
                    String faultString = soapFault.getFaultString();
                    //Seems to be some sort of bug in handling fault details in Axis
                    //String faultDetail = soapFault.getDetail().toString();
                    mLogger.log(Level.WARNING,"SOAP fault code is '" + faultCode + "'");
                    mLogger.log(Level.WARNING,"SOAP fault string is '" + faultString + "'");
                    //mLogger.log(Level.WARNING,"SOAP fault detail is '" + faultDetail + "'");
                    throw new ServiceException(faultString,faultString,faultCode);
                }
                else {
                    //check that the service has returned the correct response message
       /*
       WSDL11Interface wsdlIntf = (WSDL11Interface) intf;
       org.w3c.dom.Document wsdlDoc = wsdlIntf.getWSDLDocument();
       javax.wsdl.xml.WSDLReader wsdlReader = WSDLFactory.newInstance().newWSDLReader();
       Definition defn = wsdlReader.readWSDL(null,wsdlDoc);
       Binding binding = defn.getBinding(new QName(defn.getTargetNamespace(),wsdlIntf.getWSDLBinding()));
       */
                    //actually this is not a very useful thing to do and slows down every call. It is useful and necessary
                    //since it will validate if a message should contain any return parts. But it also doesn't work well with Axis.
                }
            }
            catch(ClassCastException ex) {
                //try to get the soapfault manually
                ByteArrayOutputStream outStream = new ByteArrayOutputStream();
                message.writeTo(outStream);
                mLogger.log(Level.WARNING,"Service failure response message: " + new String(outStream.toByteArray()));
                Iterator iterator = soapBody.getChildElements();
                while(iterator.hasNext()) {
                    javax.xml.soap.Node node = (javax.xml.soap.Node) iterator.next();
                    mLogger.log(Level.WARNING,"Could not read following node: " + node.getValue());
                    if(node instanceof SOAPElement) {
                        SOAPElement soapE = (SOAPElement) node;
                        if(soapE.getElementName().equals("soapenv:Fault")) {
                            //get the reason
                            Iterator itor = soapE.getChildElements();
                            while(itor.hasNext()) {
                                SOAPElement sE = (SOAPElement) itor.next();
                                if(sE.getElementName().equals("faultstring")) {
                                    throw new WSDLValidationException("Remote service failed to complete action, reason given '" + sE.getValue() +"'");
                                }
                            }
                        }
                    }
                }
                //throw a less specific message
                throw new WSDLValidationException("Remote service failed to complete action, could not establish reason");
            }
        }
        catch(javax.xml.soap.SOAPException ex) {
            mLogger.log(Level.SEVERE,"Could not build SOAP message from service response, the web service might not be available",ex);
            throw new ServiceException("Could not build SOAP message from service response, the web service might not be available");
        }
        catch(Exception ex) {

            if(ex instanceof ServiceException) {
                throw (ServiceException) ex;
            }
            mLogger.log(Level.WARNING,"Unrecognised problem validating WSDL response",ex);
            throw new MessageCoDecException("A problem occured while validating the service response, please send a bug report including all logfiles");
        }
    }
}