com.zimbra.soap.util.WsdlGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.zimbra.soap.util.WsdlGenerator.java

Source

/*
 * ***** BEGIN LICENSE BLOCK *****
 * Zimbra Collaboration Suite Server
 * Copyright (C) 2011, 2012, 2013, 2014, 2016 Synacor, Inc.
 *
 * 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,
 * version 2 of the License.
 *
 * 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, see <https://www.gnu.org/licenses/>.
 * ***** END LICENSE BLOCK *****
 */

package com.zimbra.soap.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.xml.XMLConstants;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.zimbra.common.soap.AccountConstants;
import com.zimbra.common.soap.AdminConstants;
import com.zimbra.common.soap.AdminExtConstants;
import com.zimbra.common.soap.MailConstants;
import com.zimbra.common.soap.ReplicationConstants;
import com.zimbra.common.soap.SyncConstants;
import com.zimbra.common.soap.VoiceConstants;
import com.zimbra.common.soap.ZimbraNamespace;
import com.zimbra.soap.JaxbUtil;

/**
 * This class represents a utility to generate the top level WSDL files for
 * Zimbra SOAP interfaces
 *
 * @author gren
 *
 */
public class WsdlGenerator {

    private static final String ARG_OUTPUT_DIR = "-output.dir";
    private static String outputDir = null;

    private static final String svcPrefix = "svc"; // Namespace prefix used for references to targetNamespace
    private static final Namespace nsSoap = new Namespace("soap", "http://schemas.xmlsoap.org/wsdl/soap/");
    private static final Namespace nsXsd = new Namespace("xsd", XMLConstants.W3C_XML_SCHEMA_NS_URI);
    private static final Namespace nsWsdl = new Namespace("wsdl", "http://schemas.xmlsoap.org/wsdl/");
    private static final Namespace nsZimbra = new Namespace("zimbra", ZimbraNamespace.ZIMBRA_STR);
    private static final String targetNsBase = "http://www.zimbra.com/wsdl/";
    private static final QName xsdSchema = QName.get("schema", nsXsd);
    private static final QName xsdImport = QName.get("import", nsXsd);
    private static final QName soapBinding = QName.get("binding", nsSoap);
    private static final QName wsdlMessage = QName.get("message", nsWsdl);
    private static final QName wsdlBinding = QName.get("binding", nsWsdl);
    private static final QName wsdlOperation = QName.get("operation", nsWsdl);
    private static final QName soapOperation = QName.get("operation", nsSoap);
    private static final QName portType = QName.get("portType", nsWsdl);
    private static final QName part = QName.get("part", nsWsdl);
    private static final QName input = QName.get("input", nsWsdl);
    private static final QName output = QName.get("output", nsWsdl);
    private static final QName body = QName.get("body", nsSoap);
    private static final QName header = QName.get("header", nsSoap);
    private static final QName service = QName.get("service", nsWsdl);
    private static final QName port = QName.get("port", nsWsdl);
    private static final QName address = QName.get("address", nsSoap);

    /**
     * Reads the command line arguments.
     *
     * @param    args        the arguments
     */
    private static void readArguments(String[] args) {
        int argPos = 0;

        if (args[argPos].equals(ARG_OUTPUT_DIR)) {
            outputDir = args[++argPos];
            argPos++;
        }
    }

    public static Document makeWsdlDoc(List<WsdlInfoForNamespace> nsInfos, String serviceName,
            String targetNamespace) {
        Namespace nsSvc = new Namespace(svcPrefix, targetNamespace);

        final QName svcTypes = QName.get("types", nsWsdl);

        Document document = DocumentHelper.createDocument();
        Map<WsdlServiceInfo, Element> bindElems = Maps.newTreeMap();
        Map<WsdlServiceInfo, Element> portTypeElems = Maps.newTreeMap();
        Element root = document.addElement(QName.get("definitions", nsWsdl));
        root.add(nsSvc);
        for (WsdlInfoForNamespace wsdlNsInfo : nsInfos) {
            root.add(wsdlNsInfo.getXsdNamespace());
        }
        root.add(nsZimbra);
        root.add(nsSoap);
        root.add(nsXsd);
        root.add(nsWsdl);
        root.addAttribute("targetNamespace", targetNamespace);
        root.addAttribute("name", serviceName);
        addWsdlTypesElement(root, svcTypes, nsInfos);

        for (WsdlInfoForNamespace wsdlNsInfo : nsInfos) {
            WsdlServiceInfo svcInfo = wsdlNsInfo.getSvcInfo();
            if (!portTypeElems.containsKey(svcInfo)) {
                // wsdl:definitions/wsdl:portType
                Element portTypeElem = DocumentHelper.createElement(portType);
                portTypeElem.addAttribute("name", svcInfo.getPortTypeName());
                portTypeElems.put(svcInfo, portTypeElem);
            }
            if (!bindElems.containsKey(svcInfo)) {
                // wsdl:definitions/wsdl:binding
                Element bindingElem = DocumentHelper.createElement(wsdlBinding);
                bindingElem.addAttribute("name", svcInfo.getBindingName());
                bindingElem.addAttribute("type", svcPrefix + ":" + svcInfo.getPortTypeName());
                // wsdl:definitions/wsdl:binding/soap:binding
                Element soapBindElem = bindingElem.addElement(soapBinding);
                soapBindElem.addAttribute("transport", "http://schemas.xmlsoap.org/soap/http");
                soapBindElem.addAttribute("style", "document");

                bindElems.put(svcInfo, bindingElem);
            }
        }

        for (WsdlInfoForNamespace wsdlNsInfo : nsInfos) {
            WsdlServiceInfo svcInfo = wsdlNsInfo.getSvcInfo();
            for (String requestName : wsdlNsInfo.getRequests()) {
                String rootName = requestName.substring(0, requestName.length() - 7);
                String responseName = rootName + "Response";
                String reqOpName = requestName.substring(0, 1).toLowerCase() + requestName.substring(1);
                String reqMsgName = wsdlNsInfo.getTag() + requestName + "Message";
                String respMsgName = wsdlNsInfo.getTag() + responseName + "Message";

                addWsdlRequestAndResponseMessageElements(root, wsdlNsInfo, reqMsgName, respMsgName, requestName,
                        responseName);

                addWsdlPortTypeOperationElements(portTypeElems.get(svcInfo), reqMsgName, respMsgName, reqOpName);

                addWsdlBindingOperationElements(bindElems.get(svcInfo), wsdlNsInfo, reqOpName, rootName);
            }
        }
        addWsdlSoapHdrContextMessageElement(root);

        for (Entry<WsdlServiceInfo, Element> entry : portTypeElems.entrySet()) {
            root.add(entry.getValue());
        }

        for (Entry<WsdlServiceInfo, Element> entry : bindElems.entrySet()) {
            root.add(entry.getValue());
        }

        Set<WsdlServiceInfo> svcSet = Sets.newHashSet();
        for (WsdlInfoForNamespace wsdlNsInfo : nsInfos) {
            WsdlServiceInfo svcInfo = wsdlNsInfo.getSvcInfo();
            if (!svcSet.contains(svcInfo)) {
                svcSet.add(svcInfo);
                addWsdlServiceElement(root, svcInfo);
            }
        }
        return document;
    }

    private static void addWsdlTypesElement(Element root, QName svcTypes, List<WsdlInfoForNamespace> nsInfos) {
        // wsdl:definitions/svc:types
        Element typesElem = root.addElement(svcTypes);
        // wsdl:definitions/svc:types/xsd:schema
        Element schemaElem = typesElem.addElement(xsdSchema);
        // wsdl:definitions/svc:types/xsd:schema/xsd:import
        Element importZimbraElem = schemaElem.addElement(xsdImport);
        importZimbraElem.addAttribute("namespace", ZimbraNamespace.ZIMBRA_STR);
        importZimbraElem.addAttribute("schemaLocation", "zimbra.xsd");

        for (WsdlInfoForNamespace nsInfo : nsInfos) {
            Element importTnsElem = schemaElem.addElement(xsdImport);
            importTnsElem.addAttribute("namespace", nsInfo.getXsdNamespaceString());
            importTnsElem.addAttribute("schemaLocation", nsInfo.getXsdFilename());
        }
    }

    private static void addWsdlRequestAndResponseMessageElements(Element root, WsdlInfoForNamespace nsInfo,
            String reqMsgName, String respMsgName, String requestName, String responseName) {
        // wsdl:definitions/wsdl:message - for request
        String xsdPrefix = nsInfo.getXsdPrefix();
        Element msgElem = root.addElement(wsdlMessage);
        msgElem.addAttribute("name", reqMsgName);
        // wsdl:definitions/wsdl:message/wsdl:part
        Element partElem = msgElem.addElement(part);
        /**
         * Bug 79898 - changed "name" from "parameters" to "params".
         * http://msdn.microsoft.com/en-us/magazine/cc188906.aspx
         *    Side Effects of "Parameters"
         *    Both wsdl.exe and the WebMethod infrastructure exercise special behavior when a special part name,
         *    "parameters," is used in a WSDL definition. The exact behavior is undocumented, but it's critical
         *    to understand the convention if you happen to use it.
         */
        partElem.addAttribute("name", "params");
        partElem.addAttribute("element", xsdPrefix + ":" + requestName);
        // wsdl:definitions/wsdl:message - for response
        msgElem = root.addElement(wsdlMessage);
        msgElem.addAttribute("name", respMsgName);
        // wsdl:definitions/wsdl:message/wsdl:part
        partElem = msgElem.addElement(part);
        partElem.addAttribute("name", "params");
        partElem.addAttribute("element", xsdPrefix + ":" + responseName);
    }

    private static void addWsdlPortTypeOperationElements(Element portTypeElem, String reqMsgName,
            String respMsgName, String reqOpName) {
        // wsdl:definitions/wsdl:portType/wsdl:operation
        Element opElem = portTypeElem.addElement(wsdlOperation);
        opElem.addAttribute("name", reqOpName);
        // wsdl:definitions/wsdl:portType/wsdl:operation/wsdl:input
        Element inElem = opElem.addElement(input);
        inElem.addAttribute("message", svcPrefix + ":" + reqMsgName);
        // wsdl:definitions/wsdl:portType/wsdl:operation/wsdl:output
        Element outElem = opElem.addElement(output);
        outElem.addAttribute("message", svcPrefix + ":" + respMsgName);
    }

    private static void addWsdlBindingOperationElements(Element bindingElem, WsdlInfoForNamespace nsInfo,
            String reqOpName, String rootName) {
        // wsdl:definitions/wsdl:binding/wsdl:operation
        Element boElem = bindingElem.addElement(wsdlOperation);
        boElem.addAttribute("name", reqOpName);
        // wsdl:definitions/wsdl:binding/wsdl:operation/soap:operation
        Element soapOpElem = boElem.addElement(soapOperation);
        soapOpElem.addAttribute("soapAction", nsInfo.getXsdNamespaceString() + "/" + rootName);
        soapOpElem.addAttribute("style", "document");
        // wsdl:definitions/wsdl:binding/wsdl:operation/wsdl:input
        Element boInElem = boElem.addElement(input);
        // wsdl:definitions/wsdl:binding/wsdl:operation/wsdl:input/soap:body
        Element inSoapBodyElem = boInElem.addElement(body);
        inSoapBodyElem.addAttribute("use", "literal");
        // wsdl:definitions/wsdl:binding/wsdl:operation/wsdl:input/soap:header
        Element inSoapHdrElem = boInElem.addElement(header);
        inSoapHdrElem.addAttribute("message", svcPrefix + ":soapHdrContext");
        inSoapHdrElem.addAttribute("part", "context");
        inSoapHdrElem.addAttribute("use", "literal");
        // wsdl:definitions/wsdl:binding/wsdl:operation/wsdl:output
        Element boOutElem = boElem.addElement(output);
        // wsdl:definitions/wsdl:binding/wsdl:operation/wsdl:output/soap:body
        Element outSoapBodyElem = boOutElem.addElement(body);
        outSoapBodyElem.addAttribute("use", "literal");
    }

    private static void addWsdlSoapHdrContextMessageElement(Element root) {
        // For Header Context
        // wsdl:definitions/wsdl:message
        Element hdrCntxtMsgElem = root.addElement(wsdlMessage);
        hdrCntxtMsgElem.addAttribute("name", "soapHdrContext");
        // wsdl:definitions/wsdl:message/wsdl:part
        Element partElem = hdrCntxtMsgElem.addElement(part);
        partElem.addAttribute("name", "context");
        partElem.addAttribute("element", "zimbra:context");
    }

    private static void addWsdlServiceElement(Element root, WsdlServiceInfo svcInfo) {
        // wsdl:definitions/wsdl:service
        Element svcElem = root.addElement(service);
        svcElem.addAttribute("name", svcInfo.getServiceName());
        // wsdl:definitions/wsdl:service/wsdl:port
        Element svcPortElem = svcElem.addElement(port);
        svcPortElem.addAttribute("name", svcInfo.getServiceName() + "Port");
        svcPortElem.addAttribute("binding", svcPrefix + ":" + svcInfo.getBindingName());
        // wsdl:definitions/wsdl:service/wsdl:port/soap:address
        Element svcPortAddrElem = svcPortElem.addElement(address);
        svcPortAddrElem.addAttribute("location", svcInfo.getSoapAddressURL());
    }

    public static void writeWsdl(OutputStream xmlOut, String targetNamespace, String serviceName,
            List<WsdlInfoForNamespace> nsInfos) throws IOException {
        Document wsdlDoc = makeWsdlDoc(nsInfos, serviceName, targetNamespace);
        OutputFormat format = OutputFormat.createPrettyPrint();
        XMLWriter writer = new XMLWriter(xmlOut, format);
        writer.write(wsdlDoc);
        writer.close();
    }

    public static void createWsdlFile(File wsdlFile, String serviceName, List<WsdlInfoForNamespace> nsInfos)
            throws IOException {
        String targetNamespace = targetNsBase + wsdlFile.getName();
        if (wsdlFile.exists())
            wsdlFile.delete();
        OutputStream xmlOut = new FileOutputStream(wsdlFile);
        writeWsdl(xmlOut, targetNamespace, serviceName, nsInfos);
    }

    public static void createWsdlFile(String wsdlFileName, String serviceName, List<WsdlInfoForNamespace> nsInfos)
            throws IOException {
        File wsdlFile = new File(outputDir, wsdlFileName);
        createWsdlFile(wsdlFile, serviceName, nsInfos);
    }

    /**
     * Create a map whose key is a package name.  The value is the list of requests in that package.
     */
    private static Map<String, List<String>> getPackageToRequestListMap() {
        Map<String, List<String>> packageToRequestListsMap = Maps.newHashMap();
        for (Class<?> currClass : JaxbUtil.getJaxbRequestAndResponseClasses()) {
            String requestName = currClass.getSimpleName();
            if (!requestName.endsWith("Request"))
                continue;
            String pkgName = currClass.getPackage().getName();
            List<String> reqList;
            if (packageToRequestListsMap.containsKey(pkgName)) {
                reqList = packageToRequestListsMap.get(pkgName);
            } else {
                reqList = Lists.newArrayList();
                packageToRequestListsMap.put(pkgName, reqList);
            }
            reqList.add(requestName);
        }
        for (Entry<String, List<String>> entry : packageToRequestListsMap.entrySet()) {
            Collections.sort(entry.getValue());
        }
        return packageToRequestListsMap;
    }

    public enum WsdlDefinition {
        ALL("ZimbraService.wsdl", "ZimbraService", targetNsBase + "ZimbraService.wsdl"), ADMIN(
                "ZimbraAdminService.wsdl", "ZimbraAdminService", targetNsBase + "ZimbraAdminService.wsdl"), USER(
                        "ZimbraUserService.wsdl", "ZimbraUserService", targetNsBase + "ZimbraAdminService.wsdl");

        private final String fileName;
        private final String serviceName;
        private final String targetNamespace;

        private WsdlDefinition(String fileName, String serviceName, String targetNamespace) {
            this.fileName = fileName;
            this.serviceName = serviceName;
            this.targetNamespace = targetNamespace;
        }

        public String getFileName() {
            return fileName;
        }

        public String getServiceName() {
            return serviceName;
        }

        public String getTargetNamespace() {
            return targetNamespace;
        }
    }

    public static boolean handleRequestForWsdl(String fileName, OutputStream out, String soapUrl,
            String adminSoapUrl) throws IOException {
        if (WsdlDefinition.ALL.getFileName().equals(fileName)) {
            createZimbraServiceWsdl(out, soapUrl, adminSoapUrl);
            return true;
        } else if (WsdlDefinition.ADMIN.getFileName().equals(fileName)) {
            createZimbraAdminServiceWsdl(out, adminSoapUrl);
            return true;
        } else if (WsdlDefinition.USER.getFileName().equals(fileName)) {
            createZimbraUserServiceWsdl(out, soapUrl);
            return true;
        }
        return false;
    }

    public static void createZimbraServiceWsdl(OutputStream out, String soapUrl, String adminSoapUrl)
            throws IOException {
        List<WsdlInfoForNamespace> nsInfoList = Lists.newArrayList();
        Map<String, List<String>> packageToRequestListMap = getPackageToRequestListMap();
        WsdlServiceInfo zcsService = WsdlServiceInfo.createForSoap(soapUrl);
        WsdlServiceInfo zcsAdminService = WsdlServiceInfo.createForAdmin(adminSoapUrl);
        addUserNamespaceInfo(nsInfoList, zcsService, packageToRequestListMap);
        addAdminNamespaceInfo(nsInfoList, zcsAdminService, packageToRequestListMap);
        writeWsdl(out, WsdlDefinition.ALL.getTargetNamespace(), WsdlDefinition.ALL.getServiceName(), nsInfoList);
    }

    public static void createZimbraAdminServiceWsdl(OutputStream out, String adminSoapUrl) throws IOException {
        List<WsdlInfoForNamespace> nsInfoList = Lists.newArrayList();
        Map<String, List<String>> packageToRequestListMap = getPackageToRequestListMap();
        WsdlServiceInfo zcsAdminService = WsdlServiceInfo.createForAdmin(adminSoapUrl);
        addAdminNamespaceInfo(nsInfoList, zcsAdminService, packageToRequestListMap);
        writeWsdl(out, WsdlDefinition.ALL.getTargetNamespace(), WsdlDefinition.ALL.getServiceName(), nsInfoList);
    }

    public static void createZimbraUserServiceWsdl(OutputStream out, String soapUrl) throws IOException {
        List<WsdlInfoForNamespace> nsInfoList = Lists.newArrayList();
        Map<String, List<String>> packageToRequestListMap = getPackageToRequestListMap();
        WsdlServiceInfo zcsService = WsdlServiceInfo.createForSoap(soapUrl);
        addUserNamespaceInfo(nsInfoList, zcsService, packageToRequestListMap);
        writeWsdl(out, WsdlDefinition.ALL.getTargetNamespace(), WsdlDefinition.ALL.getServiceName(), nsInfoList);
    }

    private static void addAdminNamespaceInfo(List<WsdlInfoForNamespace> nsInfoList,
            WsdlServiceInfo zcsAdminService, Map<String, List<String>> packageToRequestListMap) {
        nsInfoList.add(WsdlInfoForNamespace.create(AdminConstants.NAMESPACE_STR, zcsAdminService,
                packageToRequestListMap.get("com.zimbra.soap.admin.message")));
        nsInfoList.add(WsdlInfoForNamespace.create(AdminExtConstants.NAMESPACE_STR, zcsAdminService,
                packageToRequestListMap.get("com.zimbra.soap.adminext.message")));
    }

    private static void addUserNamespaceInfo(List<WsdlInfoForNamespace> nsInfoList, WsdlServiceInfo zcsService,
            Map<String, List<String>> packageToRequestListMap) {
        nsInfoList.add(WsdlInfoForNamespace.create(AccountConstants.NAMESPACE_STR, zcsService,
                packageToRequestListMap.get("com.zimbra.soap.account.message")));
        nsInfoList.add(WsdlInfoForNamespace.create(MailConstants.NAMESPACE_STR, zcsService,
                packageToRequestListMap.get("com.zimbra.soap.mail.message")));
        nsInfoList.add(WsdlInfoForNamespace.create(ReplicationConstants.NAMESPACE_STR, zcsService,
                packageToRequestListMap.get("com.zimbra.soap.replication.message")));
        nsInfoList.add(WsdlInfoForNamespace.create(SyncConstants.NAMESPACE_STR, zcsService,
                packageToRequestListMap.get("com.zimbra.soap.sync.message")));
        nsInfoList.add(WsdlInfoForNamespace.create(VoiceConstants.NAMESPACE_STR, zcsService,
                packageToRequestListMap.get("com.zimbra.soap.voice.message")));
    }

    /**
     * Main
     *
     * @param args the utility arguments
     */
    public static void main(String[] args) throws Exception {
        List<WsdlInfoForNamespace> nsInfoList = Lists.newArrayList();
        readArguments(args);
        Map<String, List<String>> packageToRequestListMap = getPackageToRequestListMap();
        addUserNamespaceInfo(nsInfoList, WsdlServiceInfo.zcsService, packageToRequestListMap);
        addAdminNamespaceInfo(nsInfoList, WsdlServiceInfo.zcsAdminService, packageToRequestListMap);
        createWsdlFile("ZimbraService.wsdl", "ZimbraService", nsInfoList);
    }
} // end WsdlGenerator class