eu.planets_project.tb.gui.backing.admin.wsclient.util.ComponentBuilder.java Source code

Java tutorial

Introduction

Here is the source code for eu.planets_project.tb.gui.backing.admin.wsclient.util.ComponentBuilder.java

Source

/*******************************************************************************
 * Copyright (c) 2007, 2010 The Planets Project Partners.
 *
 * All rights reserved. This program and the accompanying 
 * materials are made available under the terms of the 
 * Apache License, Version 2.0 which accompanies 
 * this distribution, and is available at 
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *******************************************************************************/
package eu.planets_project.tb.gui.backing.admin.wsclient.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.wsdl.Binding;
import javax.wsdl.BindingInput;
import javax.wsdl.BindingOperation;
import javax.wsdl.Definition;
import javax.wsdl.Input;
import javax.wsdl.Message;
import javax.wsdl.Operation;
import javax.wsdl.Output;
import javax.wsdl.Part;
import javax.wsdl.Port;
import javax.wsdl.Service;
import javax.wsdl.extensions.ExtensibilityElement;
import javax.wsdl.extensions.soap.SOAPAddress;
import javax.wsdl.extensions.soap.SOAPBinding;
import javax.wsdl.extensions.soap.SOAPBody;
import javax.wsdl.extensions.soap.SOAPOperation;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.xml.namespace.QName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.exolab.castor.xml.schema.ComplexType;
import org.exolab.castor.xml.schema.ElementDecl;
import org.exolab.castor.xml.schema.Group;
import org.exolab.castor.xml.schema.Particle;
import org.exolab.castor.xml.schema.Schema;
import org.exolab.castor.xml.schema.SimpleTypesFactory;
import org.exolab.castor.xml.schema.Structure;
import org.exolab.castor.xml.schema.XMLType;
import org.jdom.Element;
import org.jdom.input.DOMBuilder;

//import com.ibm.wsdl.extensions.schema.SchemaImpl;

/**
 * This class defines methods for building components to invoke a web service
 * by analyzing a WSDL contract document.
 *
 * @author Markus Reis, ARC
 */

public class ComponentBuilder {

    private Log log = LogFactory.getLog(ComponentBuilder.class);

    /** JWSDL Factory instance */
    WSDLFactory wsdlFactory = null;

    /** Cator simple types factory */
    SimpleTypesFactory simpleTypesFactory = null;

    /** WSDL type schema */
    private Schema wsdlTypes = null;

    /** The default SOAP encoding to use. */
    public final static String DEFAULT_SOAP_ENCODING_STYLE = "http://schemas.xmlsoap.org/soap/encoding/";

    /**
     * Constructor
     */
    public ComponentBuilder() {
        try {
            wsdlFactory = WSDLFactory.newInstance();
            simpleTypesFactory = new SimpleTypesFactory();
        }

        catch (Throwable t) {
            log.error(t.getMessage());
        }
    }

    /**
     * Builds a List of ServiceInfo components for each Service defined in a WSDL Document
     *
     * @param wsdlURI A URI that points to a WSDL XML Definition. Can be a filename or URL.
     *
     * @return A List of SoapComponent objects populated for each service defined
     *         in a WSDL document. A null is returned if the document can't be read.
     */
    public List buildComponents(String wsdlURI) throws Exception {
        // The list of components that will be returned
        List serviceList = Collections.synchronizedList(new ArrayList());
        // Create the WSDL Reader object
        WSDLReader reader = wsdlFactory.newWSDLReader();
        // Read the WSDL and get the top-level Definition object
        Definition def = reader.readWSDL(null, wsdlURI);
        // Create a castor schema from the types element defined in WSDL
        // This method will return null if there are types defined in the WSDL
        wsdlTypes = createSchemaFromTypes(def);
        // Get the services defined in the document
        Map services = def.getServices();
        if (services != null) {
            // Create a component for each service defined
            Iterator svcIter = services.values().iterator();

            for (int i = 0; svcIter.hasNext(); i++) {
                // Create a new ServiceInfo component for each service found
                ServiceInfo serviceInfo = new ServiceInfo();

                // Populate the new component from the WSDL Definition read
                populateComponent(serviceInfo, (Service) svcIter.next());

                // Add the new component to the List to be returned
                serviceList.add(serviceInfo);
            }
        }
        // return the List of services we created
        return serviceList;
    }

    /**
     * Creates a castor schema based on the types defined by a WSDL document
     *
     * @param   wsdlDefinition    The WSDL4J instance of a WSDL definition.
     *
     * @return  A castor schema is returned if the WSDL definition contains
     *          a types element. null is returned otherwise.
     */
    protected Schema createSchemaFromTypes(Definition wsdlDefinition) {
        // Get the schema element from the WSDL definition
        org.w3c.dom.Element schemaElement = null;

        if (wsdlDefinition.getTypes() != null) {
            ExtensibilityElement schemaExtElem = findExtensibilityElement(
                    wsdlDefinition.getTypes().getExtensibilityElements(), "schema");

            //if(schemaExtElem != null && schemaExtElem instanceof UnknownExtensibilityElement)
            if (schemaExtElem != null && schemaExtElem instanceof javax.wsdl.extensions.schema.Schema) {
                log.debug(schemaExtElem.getClass().getCanonicalName());
                schemaElement = ((javax.wsdl.extensions.schema.Schema) schemaExtElem).getElement();
            }

            /*if(schemaExtElem != null && schemaExtElem instanceof ExtensibilityElement)
            {
               schemaElement = ((UnknownExtensibilityElement)schemaExtElem).getElement();
            } */
        }

        if (schemaElement == null) {
            // No schema to read
            log.error("Unable to find schema extensibility element in WSDL");
            return null;
        }

        // Convert from DOM to JDOM
        DOMBuilder domBuilder = new DOMBuilder();
        org.jdom.Element jdomSchemaElement = domBuilder.build(schemaElement);
        ;

        if (jdomSchemaElement == null) {
            log.error("Unable to read schema defined in WSDL");
            return null;
        }

        // Add namespaces from the WSDL
        Map namespaces = wsdlDefinition.getNamespaces();

        if (namespaces != null && !namespaces.isEmpty()) {
            Iterator nsIter = namespaces.keySet().iterator();

            while (nsIter.hasNext()) {
                String nsPrefix = (String) nsIter.next();
                String nsURI = (String) namespaces.get(nsPrefix);

                if (nsPrefix != null && nsPrefix.length() > 0) {
                    org.jdom.Namespace nsDecl = org.jdom.Namespace.getNamespace(nsPrefix, nsURI);
                    jdomSchemaElement.addNamespaceDeclaration(nsDecl);
                }
            }
        }

        // Make sure that the types element is not processed
        jdomSchemaElement.detach();

        // Convert it into a Castor schema instance
        Schema schema = null;

        try {
            schema = XMLSupport.convertElementToSchema(jdomSchemaElement);
        }

        catch (Exception e) {
            log.error(e.getMessage());
        }

        // Return it
        return schema;
    }

    /**
     * Populates a ServiceInfo instance from the specified Service definiition
     *
     * @param   component      The component to populate
     * @param   service        The Service to populate from
     *
     * @return The populated component is returned representing the Service parameter
     */
    private ServiceInfo populateComponent(ServiceInfo component, Service service) {
        // Get the qualified service name information
        QName qName = service.getQName();

        // Get the service's namespace URI
        String namespace = qName.getNamespaceURI();

        // Use the local part of the qualified name for the component's name
        String name = qName.getLocalPart();

        // Set the name
        component.setName(name);
        // Get the defined ports for this service
        Map ports = service.getPorts();

        // Use the Ports to create OperationInfos for all request/response messages defined
        Iterator portIter = ports.values().iterator();

        while (portIter.hasNext()) {
            // Get the next defined port
            Port port = (Port) portIter.next();

            // Get the Port's Binding
            Binding binding = port.getBinding();

            // Now we will create operations from the Binding information
            List<?> operations = buildOperations(binding);

            // Process objects built from the binding information
            Iterator<?> operIter = operations.iterator();

            while (operIter.hasNext()) {
                OperationInfo operation = (OperationInfo) operIter.next();

                // Set the namespace URI for the operation.
                operation.setNamespaceURI(namespace);

                // Find the SOAP target URL
                ExtensibilityElement addrElem = findExtensibilityElement(port.getExtensibilityElements(),
                        "address");

                if (addrElem != null && addrElem instanceof SOAPAddress) {
                    // Set the SOAP target URL
                    SOAPAddress soapAddr = (SOAPAddress) addrElem;
                    operation.setTargetURL(soapAddr.getLocationURI());
                }

                // Add the operation info to the component
                component.addOperation(operation);
            }
        }

        return component;
    }

    /**
     * Creates Info objects for each Binding Operation defined in a Port Binding
     *
     * @param binding The Binding that defines Binding Operations used to build info objects from
     *
     * @return A List of built and populated OperationInfos is returned for each Binding Operation
     */
    private List<OperationInfo> buildOperations(Binding binding) {
        // Create the array of info objects to be returned
        List<OperationInfo> operationInfos = new ArrayList<OperationInfo>();

        // Get the list of Binding Operations from the passed binding
        List<?> operations = binding.getBindingOperations();

        if (operations != null && !operations.isEmpty()) {
            // Determine encoding
            ExtensibilityElement soapBindingElem = findExtensibilityElement(binding.getExtensibilityElements(),
                    "binding");
            String style = "document"; // default

            if (soapBindingElem != null && soapBindingElem instanceof SOAPBinding) {
                SOAPBinding soapBinding = (SOAPBinding) soapBindingElem;
                style = soapBinding.getStyle();
            }

            // For each binding operation, create a new OperationInfo
            Iterator<?> opIter = operations.iterator();

            while (opIter.hasNext()) {
                BindingOperation oper = (BindingOperation) opIter.next();

                // We currently only support soap:operation bindings
                // filter out http:operations for now until we can dispatch them properly
                ExtensibilityElement operElem = findExtensibilityElement(oper.getExtensibilityElements(),
                        "operation");

                if (operElem != null && operElem instanceof SOAPOperation) {
                    // Create a new operation info
                    OperationInfo operationInfo = new OperationInfo(style);

                    // Populate it from the Binding Operation
                    buildOperation(operationInfo, oper);

                    // Add to the return list
                    operationInfos.add(operationInfo);
                }
            }
        }

        return operationInfos;
    }

    /**
     * Populates an OperationInfo from the specified Binding Operation
     *
     * @param   operationInfo      The component to populate
     * @param   bindingOper        A Binding Operation to define the OperationInfo from
     *
     * @return The populated OperationInfo object is returned.
     */
    private OperationInfo buildOperation(OperationInfo operationInfo, BindingOperation bindingOper) {
        // Get the operation
        Operation oper = bindingOper.getOperation();

        // Set the name using the operation name
        operationInfo.setTargetMethodName(oper.getName());

        // Set the action URI
        ExtensibilityElement operElem = findExtensibilityElement(bindingOper.getExtensibilityElements(),
                "operation");

        if (operElem != null && operElem instanceof SOAPOperation) {
            SOAPOperation soapOperation = (SOAPOperation) operElem;
            operationInfo.setSoapActionURI(soapOperation.getSoapActionURI());
        }

        // Get the Binding Input
        BindingInput bindingInput = bindingOper.getBindingInput();

        // Get the Binding Output
        bindingOper.getBindingOutput();

        // Get the SOAP Body
        ExtensibilityElement bodyElem = findExtensibilityElement(bindingInput.getExtensibilityElements(), "body");

        if (bodyElem != null && bodyElem instanceof SOAPBody) {
            SOAPBody soapBody = (SOAPBody) bodyElem;

            // The SOAP Body contains the encoding styles
            List<?> styles = soapBody.getEncodingStyles();
            String encodingStyle = null;

            if (styles != null) {
                // Use the first in the list
                encodingStyle = styles.get(0).toString();
            }

            if (encodingStyle == null) {
                // An ecoding style was not found, give it a default
                encodingStyle = DEFAULT_SOAP_ENCODING_STYLE;
            }

            // Assign the encoding style value
            operationInfo.setEncodingStyle(encodingStyle.toString());

            // The SOAP Body contains the target object's namespace URI.
            operationInfo.setTargetObjectURI(soapBody.getNamespaceURI());
        }

        // Get the Operation's Input definition
        Input inDef = oper.getInput();

        if (inDef != null) {
            // Build input parameters
            Message inMsg = inDef.getMessage();

            if (inMsg != null) {
                // Set the name of the operation's input message
                operationInfo.setInputMessageName(inMsg.getQName().getLocalPart());

                // Set the body of the operation's input message
                operationInfo.setInputMessageText(buildMessageText(operationInfo, inMsg));
            }
        }

        // Get the Operation's Output definition
        Output outDef = oper.getOutput();

        if (outDef != null) {
            // Build output parameters
            Message outMsg = outDef.getMessage();

            if (outMsg != null) {
                // Set the name of the output message
                operationInfo.setOutputMessageName(outMsg.getQName().getLocalPart());

                // Set the body of the operation's output message
                operationInfo.setOutputMessageText(buildMessageText(operationInfo, outMsg));
            }
        }

        // Finished, return the populated object
        return operationInfo;
    }

    /**
     * Builds and adds parameters to the supplied info object
     * given a SOAP Message definition (from WSDL)
     *
     * @param   operationInfo   The component to build message text for
     * @param   msg    The SOAP Message definition that has parts to defined parameters for
     */
    private String buildMessageText(OperationInfo operationInfo, Message msg) {
        // Create the root message element
        Element rootElem = null;
        if (msg.getQName() != null) {
            if (msg.getQName().getNamespaceURI() != null) {
                if (msg.getQName().getNamespaceURI().length() > 0) {
                    rootElem = new Element(operationInfo.getTargetMethodName(), "ns1",
                            msg.getQName().getNamespaceURI());
                } else
                    new Element(operationInfo.getTargetMethodName());
            } else
                new Element(operationInfo.getTargetMethodName());
        } else
            new Element(operationInfo.getTargetMethodName());

        // Get the message parts
        List<?> msgParts = msg.getOrderedParts(null);

        // Process each part
        Iterator<?> iter = msgParts.iterator();

        while (iter.hasNext()) {
            // Get each part
            Part part = (Part) iter.next();

            // Add content for each message part
            String partName = part.getName();

            if (partName != null) {
                // Determine if this message part's type is complex
                XMLType xmlType = getXMLType(part);

                if (xmlType != null && xmlType.isComplexType()) {
                    // Build the message structure
                    buildComplexPart((ComplexType) xmlType, rootElem);
                } else {
                    // Build the element that will be added to the message
                    Element partElem;
                    if (part.getElementName() != null) {
                        if ((part.getElementName().getLocalPart() != null)
                                && (part.getElementName().getNamespaceURI() != null))
                            partElem = new Element(part.getElementName().getLocalPart(),
                                    part.getElementName().getNamespaceURI());
                        else
                            partElem = new Element(partName);
                    } else
                        partElem = new Element(partName);

                    // Add some default content as just a place holder
                    partElem.addContent("?");

                    if (operationInfo.getStyle().equalsIgnoreCase("rpc")) {
                        // If this is an RPC style operation, we need to include some type information
                        partElem.setAttribute("type", part.getTypeName().getLocalPart());
                    }

                    // Add this message part
                    rootElem.addContent(partElem);
                }
            }
        }

        return XMLSupport.outputString(rootElem);
    }

    /**
     * Populate a JDOM element using the complex XML type passed in
     *
     * @param   complexType  The complex XML type to build the element for
     * @param   partElem     The JDOM element to build content for
     */
    protected void buildComplexPart(ComplexType complexType, Element partElem) {
        // Find the group
        Enumeration<?> particleEnum = complexType.enumerate();
        Group group = null;

        while (particleEnum.hasMoreElements()) {
            Particle particle = (Particle) particleEnum.nextElement();

            if (particle instanceof Group) {
                group = (Group) particle;
                break;
            }
        }

        if (group != null) {
            Enumeration<?> groupEnum = group.enumerate();

            while (groupEnum.hasMoreElements()) {
                Structure item = (Structure) groupEnum.nextElement();

                if (item.getStructureType() == Structure.ELEMENT) {
                    ElementDecl elementDecl = (ElementDecl) item;
                    Element childElem;
                    if (elementDecl.getSchema() != null) {
                        if (elementDecl.getSchema().getElementFormDefault() != null) {
                            if (elementDecl.getSchema().getElementFormDefault().isQualified()) {
                                childElem = new Element(elementDecl.getName(), "ns1", partElem.getNamespaceURI());
                            } else
                                childElem = new Element(elementDecl.getName());
                        } else
                            childElem = new Element(elementDecl.getName());
                    } else
                        childElem = new Element(elementDecl.getName());

                    XMLType xmlType = elementDecl.getType();

                    if (xmlType != null && xmlType.isComplexType()) {
                        buildComplexPart((ComplexType) xmlType, childElem);
                    } else {
                        childElem.addContent("?");
                    }

                    partElem.addContent(childElem);
                }
            }
        }
    }

    /**
     * Gets an XML Type from a SOAP Message Part read from WSDL
     *
     * @param   part     The SOAP Message part
     *
     * @return The corresponding XML Type is returned.
     *         null is returned if not found or if a simple type
     */
    protected XMLType getXMLType(Part part) {
        if (wsdlTypes == null) {
            // No defined types, Nothing to do
            return null;
        }

        // Find the XML type
        XMLType xmlType = null;

        // First see if there is a defined element
        if (part.getElementName() != null) {
            // Get the element name
            String elemName = part.getElementName().getLocalPart();

            // Find the element declaration
            ElementDecl elemDecl = wsdlTypes.getElementDecl(elemName);

            if (elemDecl != null) {
                // From the element declaration get the XML type
                xmlType = elemDecl.getType();
            }
        }

        return xmlType;
    }

    /**
     * Returns the desired ExtensibilityElement if found in the List
     *
     * @param   extensibilityElements   The list of extensibility elements to search
     * @param   elementType             The element type to find
     *
     * @return  Returns the first matching element of type found in the list
     */
    private static ExtensibilityElement findExtensibilityElement(List extensibilityElements, String elementType) {
        if (extensibilityElements != null) {
            Iterator iter = extensibilityElements.iterator();

            while (iter.hasNext()) {
                ExtensibilityElement element = (ExtensibilityElement) iter.next();

                if (element.getElementType().getLocalPart().equalsIgnoreCase(elementType)) {
                    // Found it
                    return element;
                }
            }
        }

        return null;
    }
}