org.globus.wsrf.tools.wsdl.WSDLPreprocessor.java Source code

Java tutorial

Introduction

Here is the source code for org.globus.wsrf.tools.wsdl.WSDLPreprocessor.java

Source

/*
 * Copyright 1999-2006 University of Chicago
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.globus.wsrf.tools.wsdl;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Vector;
import java.net.URLDecoder;

import javax.wsdl.Definition;
import javax.wsdl.Fault;
import javax.wsdl.Import;
import javax.wsdl.Input;
import javax.wsdl.Message;
import javax.wsdl.Operation;
import javax.wsdl.Output;
import javax.wsdl.PortType;
import javax.wsdl.Types;
import javax.wsdl.extensions.AttributeExtensible;
import javax.wsdl.extensions.ExtensionRegistry;
import javax.wsdl.extensions.UnknownExtensibilityElement;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.wsdl.xml.WSDLWriter;
import javax.xml.namespace.QName;

import org.apache.xerces.dom.DOMInputImpl;
import org.apache.xerces.impl.xs.XMLSchemaLoader;
import org.apache.xerces.impl.xs.XSComplexTypeDecl;
import org.apache.xerces.xs.StringList;
import org.apache.xerces.xs.XSElementDeclaration;
import org.apache.xerces.xs.XSModel;
import org.apache.xerces.xs.XSModelGroup;
import org.apache.xerces.xs.XSNamespaceItem;
import org.apache.xerces.xs.XSNamespaceItemList;
import org.apache.xerces.xs.XSObjectList;
import org.apache.xerces.xs.XSParticle;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.ls.LSInput;

import static org.globus.wsrf.tools.wsdl.WSDLPreprocessorConstants.*;
import org.globus.wsrf.utils.XmlUtils;

// TODO: need to i18n
public class WSDLPreprocessor {
    private static Log log = LogFactory.getLog(WSDLPreprocessor.class.getName());

    private boolean wsaImport = false;
    private boolean quiet = false;
    private int nameSpaceCounter = 0;

    private String inFileName;
    private String outFileName;
    private QName portTypeName;

    private static final String ENC = "UTF-8";

    public void setQuiet(boolean quiet) {
        this.quiet = quiet;
    }

    public void setInputFile(String file) {
        this.inFileName = file;
    }

    public void setOutputFile(String file) {
        this.outFileName = file;
    }

    public void setPortTypeName(String name) {
        this.portTypeName = new QName(name);
    }

    public void execute() throws Exception {
        if (this.inFileName == null || this.inFileName.length() == 0) {
            throw new Exception("Input file not specified");
        }

        WSDLFactory factory = WSDLFactory.newInstance();

        WSDLReader reader = factory.newWSDLReader();
        WSDLWriter writer = factory.newWSDLWriter();

        // Don't import docs for now
        reader.setFeature("javax.wsdl.verbose", false);
        reader.setFeature("javax.wsdl.importDocuments", false);

        Definition definition = null;

        try {
            definition = reader.readWSDL(null, this.inFileName);
        } catch (Exception e) {
            throw new Exception("Could not read WSDL input file: " + this.inFileName, e);
        }

        TypesProcessor typesProcessor = new TypesProcessor(definition);

        PortType portType = null;

        try {
            portType = typesProcessor.getPortType(this.portTypeName);
        } catch (Exception e) {
            throw new Exception(
                    "Could not find " + (portTypeName == null ? "any" : portTypeName.toString()) + " portType.", e);
        }

        this.portTypeName = portType.getQName();

        ExtensionRegistry extensionAttributes = new ExtensionRegistry();
        extensionAttributes.registerExtensionAttributeType(PortType.class, EXTENDS,
                AttributeExtensible.LIST_OF_QNAMES_TYPE);
        extensionAttributes.registerExtensionAttributeType(PortType.class, RP, AttributeExtensible.QNAME_TYPE);
        reader.setExtensionRegistry(extensionAttributes);

        if (!this.quiet) {
            reader.setFeature("javax.wsdl.verbose", true);
        }
        reader.setFeature("javax.wsdl.importDocuments", true);

        try {
            definition = reader.readWSDL(null, this.inFileName);
        } catch (Exception e) {
            throw new Exception("Could not read WSDL input file: " + this.inFileName, e);
        }

        typesProcessor = new TypesProcessor(definition);
        portType = definition.getPortType(this.portTypeName);
        List portTypeNames = getDependencies(portType);
        Map resourcePropertyElements = new HashMap();
        Map schemaDocumentLocations = new HashMap();
        try {
            flatten(portType, definition, definition, portTypeNames, resourcePropertyElements,
                    schemaDocumentLocations);
        } catch (Exception e) {
            throw new Exception("Failed to flatten input file " + this.inFileName + ": " + e.getMessage(), e);
        }

        if (!resourcePropertyElements.isEmpty()) {
            try {
                typesProcessor.addResourceProperties(this.portTypeName, resourcePropertyElements,
                        schemaDocumentLocations);
            } catch (Exception e) {
                throw new Exception("Failed to add resource properties: " + e.getMessage(), e);
            }
        }

        if (this.wsaImport) {
            try {
                typesProcessor.addWSAImport(schemaDocumentLocations);
            } catch (Exception e) {
                throw new Exception("Failed to add WSA XSD import: " + e.getMessage(), e);
            }
        }

        FileOutputStream outFile = null;
        try {
            if (this.outFileName != null) {
                outFile = new FileOutputStream(this.outFileName);
            } else {
                outFile = new FileOutputStream(FileDescriptor.out);
            }
        } catch (Exception e) {
            throw new Exception("Could not open output file: " + (outFileName == null ? "STDOUT" : outFileName), e);
        }

        try {
            writer.writeWSDL(definition, outFile);
        } catch (Exception e) {
            throw new Exception("Failed to write processed WSDL: " + e.getMessage(), e);
        } finally {
            if (this.outFileName != null) {
                try {
                    outFile.close();
                } catch (Exception e) {
                }
            }
        }
        if (this.outFileName != null && !this.quiet) {
            log.info("Generated " + this.outFileName);
        }
    }

    private PortType flatten(PortType portType, Definition definition, Definition parentDefinition,
            List portTypeNames, Map resourcePropertyElements, Map schemaDocumentLocations) throws Exception {
        Map portTypes = definition.getPortTypes();
        Iterator portTypeIterator = portTypes.values().iterator();

        while (portTypeIterator.hasNext()) {
            PortType currentPortType = (PortType) portTypeIterator.next();

            if (portTypeNames != null && portTypeNames.contains(currentPortType.getQName())) {
                log.debug("Found porttype: " + currentPortType.getQName());

                QName resourceProperties = (QName) currentPortType.getExtensionAttribute(RP);

                if (resourceProperties != null) {
                    log.debug("Adding resource properties for porttype: " + currentPortType.getQName());
                    resourcePropertyElements.putAll(getResourceProperties(resourceProperties, definition,
                            schemaDocumentLocations, this.quiet));
                }

                List newDependencies = getDependencies(currentPortType);

                if (newDependencies != null && !newDependencies.isEmpty()) {
                    flatten(currentPortType, definition, parentDefinition, newDependencies,
                            resourcePropertyElements, schemaDocumentLocations);
                    if (!newDependencies.isEmpty() && definition != parentDefinition && !quiet) {
                        log.warn("WARNING: The following porttypes are missing:");
                        Iterator portTypeNamesIterator = newDependencies.iterator();
                        while (portTypeNamesIterator.hasNext()) {
                            log.error("\t" + portTypeNamesIterator.next().toString());
                        }
                    }
                }

                fixNameSpaces(currentPortType, parentDefinition);
                List operations = currentPortType.getOperations();
                ListIterator operationsIterator = operations.listIterator();
                Operation operation;
                Input input;
                Output output;

                while (operationsIterator.hasNext()) {
                    operation = (Operation) operationsIterator.next();
                    input = operation.getInput();
                    output = operation.getOutput();

                    if (portType.getOperation(operation.getName(), input == null ? null : input.getName(),
                            output == null ? null : output.getName()) == null) {
                        if (input != null && input.getExtensionAttribute(WSA_ACTION) != null) {
                            this.wsaImport = true;
                            Element schema = getSchemaElement(definition);
                            if (schema != null) {
                                XSModel schemaModel = loadSchema(schema, definition);
                                populateLocations(schemaModel, schemaDocumentLocations, definition);
                            }
                        }
                        portType.addOperation(operation);
                    }
                }

                addImports(definition, parentDefinition);

                portTypeNames.remove(currentPortType.getQName());
            }
        }

        if (portTypeNames == null || portTypeNames.isEmpty()) {
            return portType;
        } else {
            // Only go to immediate imports - nested imports are not processed
            if (definition == parentDefinition) {
                Map imports = new HashMap();
                imports.putAll(definition.getImports());
                Iterator importNSIterator = imports.values().iterator();

                while (importNSIterator.hasNext() && !portTypeNames.isEmpty()) {
                    Vector importVector = (Vector) importNSIterator.next();
                    Iterator importIterator = importVector.iterator();
                    while (importIterator.hasNext() && !portTypeNames.isEmpty()) {
                        // Name space?
                        // I think the rule is to set the target name space to the
                        // namespace
                        // specified by import statement if not already defined
                        Import importDef = (Import) importIterator.next();
                        flatten(portType, importDef.getDefinition(), parentDefinition, portTypeNames,
                                resourcePropertyElements, schemaDocumentLocations);
                    }
                }

                if (!portTypeNames.isEmpty() && !quiet) {
                    log.warn("The following porttypes are missing:");
                    Iterator portTypeNamesIterator = portTypeNames.iterator();
                    while (portTypeNamesIterator.hasNext()) {
                        log.warn("\t" + (portTypeNamesIterator.next()).toString());
                    }
                }
            }

            return portType;
        }
    }

    private static void addImports(Definition definition, Definition parentDefinition) throws Exception {
        Collection imports = definition.getImports().values();
        Iterator importsIterator = imports.iterator();

        while (importsIterator.hasNext()) {
            Vector importsVector = (Vector) importsIterator.next();
            Iterator importsVectorIterator = importsVector.iterator();
            boolean addImport = true;
            while (importsVectorIterator.hasNext()) {
                Import currentImport = (Import) importsVectorIterator.next();
                String location = currentImport.getLocationURI();
                if (location != null && (location.startsWith(".") || location.indexOf('/') == -1)) {
                    location = getRelativePath(parentDefinition.getDocumentBaseURI(),
                            currentImport.getDefinition().getDocumentBaseURI());
                    currentImport.setLocationURI(location);
                }
                List parentImports = parentDefinition.getImports(currentImport.getNamespaceURI());
                if (parentImports != null) {
                    Iterator parentImportsIterator = parentImports.iterator();
                    while (parentImportsIterator.hasNext()) {
                        Import parentImport = (Import) parentImportsIterator.next();
                        if (parentImport.getLocationURI().equals(location)) {
                            addImport = false;
                            break;
                        }
                    }
                }
                if (addImport) {
                    parentDefinition.addImport(currentImport);
                }
                addImport = true;
            }
        }
    }

    private static List getDependencies(PortType portType) {
        List result = (List) portType.getExtensionAttribute(EXTENDS);

        if (result != null) {
            portType.getExtensionAttributes().remove(EXTENDS);
        }

        return result;
    }

    private static Element getSchemaElement(Definition def) {
        Types types = def.getTypes();
        if (types == null) {
            return null;
        }
        List elementList = types.getExtensibilityElements();
        if (elementList.isEmpty()) {
            return null;
        } else {
            return ((UnknownExtensibilityElement) elementList.get(0)).getElement();
        }
    }

    private static XSModel loadSchema(Element schema, Definition def) {
        // add namespaces from definition element
        Map definitionNameSpaces = def.getNamespaces();
        Set nameSpaces = definitionNameSpaces.entrySet();
        Iterator nameSpacesIterator = nameSpaces.iterator();

        while (nameSpacesIterator.hasNext()) {
            Entry nameSpaceEntry = (Entry) nameSpacesIterator.next();
            if (!"".equals((String) nameSpaceEntry.getKey())
                    && !schema.hasAttributeNS("http://www.w3.org/2000/xmlns/", (String) nameSpaceEntry.getKey())) {
                Attr nameSpace = schema.getOwnerDocument().createAttributeNS("http://www.w3.org/2000/xmlns/",
                        "xmlns:" + nameSpaceEntry.getKey());
                nameSpace.setValue((String) nameSpaceEntry.getValue());
                schema.setAttributeNode(nameSpace);
            }
        }

        LSInput schemaInput = new DOMInputImpl();
        schemaInput
                .setStringData("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + XmlUtils.getElementAsString(schema));
        log.info("Loading schema in types section of definition " + def.getDocumentBaseURI());
        schemaInput.setSystemId(def.getDocumentBaseURI());
        XMLSchemaLoader schemaLoader = new XMLSchemaLoader();
        XSModel schemaModel = schemaLoader.load(schemaInput);
        log.info("Done loading");
        return schemaModel;
    }

    private static void populateLocations(XSModel schemaModel, Map documentLocations, Definition def)
            throws Exception {
        String documentURI = def.getDocumentBaseURI();
        XSNamespaceItemList namespaceItemList = schemaModel.getNamespaceItems();
        for (int i = 0; i < namespaceItemList.getLength(); i++) {
            XSNamespaceItem namespaceItem = namespaceItemList.item(i);
            Map locations = (Map) documentLocations.get(namespaceItem.getSchemaNamespace());
            if (locations == null) {
                locations = new HashMap();
                documentLocations.put(namespaceItem.getSchemaNamespace(), locations);
            }

            StringList list = namespaceItem.getDocumentLocations();

            for (int j = 0; j < list.getLength(); j++) {
                String uri = URLDecoder.decode(list.item(j), ENC);
                if (!uri.equals(documentURI)) {
                    locations.put(list.item(j), null);
                }
            }
        }
    }

    public static Map getResourceProperties(QName resourceProperties, Definition def, Map documentLocations)
            throws Exception {
        return getResourceProperties(resourceProperties, def, documentLocations, true);
    }

    public static Map getResourceProperties(QName resourceProperties, Definition def, Map documentLocations,
            boolean quiet) throws Exception {
        HashMap resourcePropertyElements = new HashMap();

        if (resourceProperties != null) {
            Types types = def.getTypes();
            if (types == null) {
                if (!quiet) {
                    log.warn("The " + def.getDocumentBaseURI() + " definition does not have a types section");
                }
                return resourcePropertyElements;
            }

            List elementList = types.getExtensibilityElements();
            // assume only on schema element for now
            if (elementList.size() > 1) {
                if (!quiet) {
                    System.err.println("WARNING: The types section in " + def.getDocumentBaseURI()
                            + " contains more than one top level element.");
                }
                // maybe throw error
            }
            Element schema = ((UnknownExtensibilityElement) elementList.get(0)).getElement();

            XSModel schemaModel = loadSchema(schema, def);

            XSElementDeclaration resourcePropertiesElement = schemaModel
                    .getElementDeclaration(resourceProperties.getLocalPart(), resourceProperties.getNamespaceURI());

            if (resourcePropertiesElement != null) {
                XSComplexTypeDecl type = (XSComplexTypeDecl) resourcePropertiesElement.getTypeDefinition();
                XSParticle particle = type.getParticle();
                XSModelGroup sequence = (XSModelGroup) particle.getTerm();
                XSObjectList objectList = sequence.getParticles();

                for (int i = 0; i < objectList.getLength(); i++) {
                    XSParticle part = (XSParticle) objectList.item(i);
                    Object term = part.getTerm();
                    if (term instanceof XSElementDeclaration) {
                        XSElementDeclaration resourceProperty = (XSElementDeclaration) term;
                        resourcePropertyElements
                                .put(new QName(resourceProperty.getNamespace(), resourceProperty.getName()), part);
                    } else {
                        throw new Exception(
                                "Invalid resource properties document " + resourceProperties.toString());
                    }
                }
            } else {
                Map imports = def.getImports();
                Iterator importNSIterator = imports.values().iterator();
                while (importNSIterator.hasNext() && resourcePropertyElements.isEmpty()) {
                    Vector importVector = (Vector) importNSIterator.next();
                    Iterator importIterator = importVector.iterator();
                    while (importIterator.hasNext() && resourcePropertyElements.isEmpty()) {
                        Import importDef = (Import) importIterator.next();
                        // process imports
                        resourcePropertyElements.putAll(getResourceProperties(resourceProperties,
                                importDef.getDefinition(), documentLocations, quiet));
                    }
                }

                if (resourcePropertyElements.isEmpty()) {
                    throw new Exception("Unable to resolve resource properties " + resourceProperties.toString());
                }
            }

            populateLocations(schemaModel, documentLocations, def);
        }
        return resourcePropertyElements;
    }

    private void fixNameSpaces(PortType porttype, Definition definition) {
        List operations = porttype.getOperations();
        Iterator operationsIterator = operations.iterator();

        while (operationsIterator.hasNext()) {
            Operation operation = (Operation) operationsIterator.next();
            Input input = operation.getInput();
            if (input != null) {
                Message inMessage = input.getMessage();

                if (inMessage != null && definition.getPrefix(inMessage.getQName().getNamespaceURI()) == null) {
                    definition.addNamespace("gtwsdl" + String.valueOf(this.nameSpaceCounter++),
                            inMessage.getQName().getNamespaceURI());
                }
            }

            Output output = operation.getOutput();

            if (output != null) {
                Message outMessage = output.getMessage();
                if (outMessage != null && definition.getPrefix(outMessage.getQName().getNamespaceURI()) == null) {
                    definition.addNamespace("gtwsdl" + String.valueOf(this.nameSpaceCounter++),
                            outMessage.getQName().getNamespaceURI());
                }
            }

            Map faults = operation.getFaults();

            if (faults != null) {
                Iterator faultIterator = faults.values().iterator();
                while (faultIterator.hasNext()) {
                    Message faultMessage = ((Fault) faultIterator.next()).getMessage();
                    if (definition.getPrefix(faultMessage.getQName().getNamespaceURI()) == null) {
                        definition.addNamespace("gtwsdl" + String.valueOf(this.nameSpaceCounter++),
                                faultMessage.getQName().getNamespaceURI());
                    }
                }
            }
        }
    }

    //Todo: move to utility class?

    protected static String getRelativePath(String srcPathURI, String destPathURI) throws IOException {
        String destPath = URLDecoder.decode(destPathURI.substring(5), ENC);
        String srcPath = URLDecoder.decode(srcPathURI.substring(5), ENC);

        return RelativePathUtil.getRelativeFileName(new File(destPath), new File(srcPath));
    }

}