/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.enterprise.jbi.serviceengine.util.soap; import com.ibm.wsdl.extensions.soap.SOAPAddressImpl; import com.ibm.wsdl.extensions.soap.SOAPBindingImpl; import com.ibm.wsdl.extensions.soap.SOAPBodyImpl; import com.ibm.wsdl.extensions.soap.SOAPFaultImpl; import com.ibm.wsdl.extensions.soap.SOAPHeaderImpl; import com.ibm.wsdl.extensions.soap.SOAPOperationImpl; import java.io.File; import java.io.FileWriter; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.wsdl.Binding; import javax.wsdl.BindingFault; import javax.wsdl.BindingInput; import javax.wsdl.BindingOperation; import javax.wsdl.BindingOutput; import javax.wsdl.Definition; import javax.wsdl.Fault; import javax.wsdl.Message; import javax.wsdl.Operation; import javax.wsdl.Part; import javax.wsdl.Port; import javax.wsdl.PortType; import javax.wsdl.Service; import javax.wsdl.extensions.ElementExtensible; import javax.wsdl.extensions.ExtensibilityElement; import javax.wsdl.extensions.ExtensionRegistry; import javax.wsdl.extensions.soap.SOAPAddress; import javax.wsdl.extensions.soap.SOAPBinding; import javax.wsdl.extensions.soap.SOAPBody; import javax.wsdl.extensions.soap.SOAPFault; import javax.wsdl.extensions.soap.SOAPHeader; import javax.wsdl.extensions.soap.SOAPOperation; import javax.wsdl.factory.WSDLFactory; import javax.wsdl.xml.WSDLReader; import javax.wsdl.xml.WSDLWriter; import javax.xml.namespace.QName; import org.w3c.dom.Element; /** * * Converts any non-SOAP WSDL to a SOAP WSDL. * * If the original WSDL is a SOAP WSDL then it is not modified. * * @author bhavanishankar@dev.java.net */ public class WSDLConverter { ExtensionRegistry extnReg; WSDLReader wsdlReader; WSDLWriter wsdlWriter; public static final String SOAP_NS = "http://schemas.xmlsoap.org/wsdl/soap/"; public static final String SOAP_PREFIX = "soap"; public static final String SOAP_BINDING_SUFFIX = "binding"; public static final String SOAP_OPERATION_SUFFIX = "operation"; public static final String SOAP_ADDRESS_SUFFIX = "address"; public static final String SOAP_BODY_SUFFIX = "body"; public static final String SOAP_HEADER_SUFFIX = "header"; public static final String SOAP_FAULT_SUFFIX = "fault"; public static final String SOAP_HTTP_TRANSPORT_URI = "http://schemas.xmlsoap.org/soap/http"; public static final String SOAP_ADDRESS_LOCATION = "REPLACE_WITH_ACTUAL_URL"; public static final String RPC_STYLE = "rpc"; public static final String DOCUMENT_STYLE = "document"; public static final String USE_LITERAL = "literal"; public WSDLConverter() throws Exception { WSDLFactory factory = WSDLFactory.newInstance(); wsdlReader = factory.newWSDLReader(); wsdlWriter = factory.newWSDLWriter(); extnReg = new ExtensionRegistry(); extnReg.mapExtensionTypes(Binding.class, new QName(SOAP_NS,SOAP_BINDING_SUFFIX), SOAPBindingImpl.class); extnReg.mapExtensionTypes(ExtensibilityElement.class, new QName(SOAP_NS,SOAP_OPERATION_SUFFIX), SOAPOperationImpl.class); extnReg.mapExtensionTypes(ExtensibilityElement.class, new QName(SOAP_NS,SOAP_ADDRESS_SUFFIX), SOAPAddressImpl.class); extnReg.mapExtensionTypes(SOAPBody.class, new QName(SOAP_NS,SOAP_BODY_SUFFIX), SOAPBodyImpl.class); extnReg.mapExtensionTypes(SOAPHeader.class, new QName(SOAP_NS,SOAP_HEADER_SUFFIX), SOAPHeaderImpl.class); extnReg.mapExtensionTypes(SOAPFault.class, new QName(SOAP_NS,SOAP_FAULT_SUFFIX), SOAPFaultImpl.class); } private SOAPBinding createSoapBinding() throws Exception { SOAPBinding soapBinding = (SOAPBinding)extnReg.createExtension(Binding.class, new QName(SOAP_NS,SOAP_BINDING_SUFFIX)); soapBinding.setTransportURI(SOAP_HTTP_TRANSPORT_URI); return soapBinding; } private SOAPAddress createSoapAddress() throws Exception { SOAPAddress soapAddress = (SOAPAddress)extnReg.createExtension(ExtensibilityElement.class, new QName(SOAP_NS,SOAP_ADDRESS_SUFFIX)); soapAddress.setLocationURI(SOAP_ADDRESS_LOCATION); return soapAddress; } private SOAPOperation createSoapOperation() throws Exception { SOAPOperation soapOperation = (SOAPOperation)extnReg.createExtension( ExtensibilityElement.class, new QName(SOAP_NS, SOAP_OPERATION_SUFFIX)); return soapOperation; } private SOAPBody createSoapBody() throws Exception { SOAPBody soapBody = (SOAPBody)extnReg.createExtension( SOAPBody.class, new QName(SOAP_NS, SOAP_BODY_SUFFIX)); soapBody.setUse(USE_LITERAL); return soapBody; } private SOAPHeader createSoapHeader() throws Exception { SOAPHeader soapHeader = (SOAPHeader)extnReg.createExtension( SOAPHeader.class, new QName(SOAP_NS, SOAP_HEADER_SUFFIX)); soapHeader.setUse(USE_LITERAL); return soapHeader; } private SOAPFault createSoapFault() throws Exception { SOAPFault soapFault = (SOAPFault)extnReg.createExtension( SOAPFault.class, new QName(SOAP_NS, SOAP_FAULT_SUFFIX)); soapFault.setUse(USE_LITERAL); return soapFault; } /** * Style can be calculated by looking into how the input/output parts are defined. * Style can not be calculated by looking into fault. * * WS-I Basic Profile 1.0 : * * R2203 An rpc-literal binding in a DESCRIPTION MUST refer, * in its soapbind:body element(s), only to wsdl:part element(s) * that have been defined using the type attribute. * * R2204 A document-literal binding in a DESCRIPTION MUST refer, * in each of its soapbind:body element(s), only to wsdl:part element(s) * that have been defined using the element attribute. * */ private String getStyle(Operation portTypeOperation) { try { if(portTypeOperation.getInput() != null) { List<Part> parts = portTypeOperation.getInput().getMessage().getOrderedParts(null); for(Part p : parts) { if(p.getTypeName() != null) { return RPC_STYLE; } } } if(portTypeOperation.getOutput() != null) { List<Part> parts = portTypeOperation.getOutput().getMessage().getOrderedParts(null); for(Part p : parts) { if(p.getTypeName() != null) { return RPC_STYLE; } } } } catch(Exception ex) { ex.printStackTrace(); } return DOCUMENT_STYLE; } private void processElement( ElementExtensible elementExtensible, Message message, String style) throws Exception { if(RPC_STYLE.equals(style)) { SOAPBody soapBody = createSoapBody(); /** * namespace needs to be set in <soap:body> only for 'rpc' style. * * WS-I Basic Profile 1.0 : * * R2716 A document-literal binding in a DESCRIPTION MUST NOT have * the namespace attribute specified on contained soapbind:body, * soapbind:header, soapbind:headerfault and soapbind:fault elements. * * R2717 : An rpc-literal binding in a DESCRIPTION MUST have * the namespace attribute specified, the value of which MUST * be an absolute URI, on contained soapbind:body elements. * * R2726 An rpc-literal binding in a DESCRIPTION MUST NOT have * the namespace attribute specified on contained soapbind:header, * soapbind:headerfault and soapbind:fault elements. * */ soapBody.setNamespaceURI( message.getQName().getNamespaceURI()); elementExtensible.addExtensibilityElement(soapBody); } else { List<Part> orderedParts = message.getOrderedParts(null); boolean partBoundToBody = false; // only one part can be bound to <soap:body>. for(int i=orderedParts.size()-1; i>=0; i--) { Part p = orderedParts.get(i); if( (p.getElementName().getLocalPart().equals (message.getQName().getLocalPart())) // wrapped doc/literal || (i==0 && !partBoundToBody) // bare doc/literal ) { partBoundToBody = true; SOAPBody soapBody = createSoapBody(); List<String> parts = new ArrayList<String>(); parts.add(p.getName()); soapBody.setParts(parts); elementExtensible.addExtensibilityElement(soapBody); } else { SOAPHeader soapHeader = createSoapHeader(); soapHeader.setPart(p.getName()); soapHeader.setMessage(message.getQName()); elementExtensible.addExtensibilityElement(soapHeader); } } } //return elementExtensible; } /** * Converts all <wsdl:service> sections. */ private boolean convertServices(Definition defn) throws Exception { boolean wsdlModified = false; Map<QName,Service> services = defn.getServices(); if(services == null || services.isEmpty()) { return wsdlModified; } Service[] originalServices = services.values().toArray(new Service[0]); for(Service originalService : originalServices) { Map<String,Port> originalPorts = originalService.getPorts(); boolean isSOAP = false; Service newService = defn.createService(); for(String key : originalPorts.keySet()) { Port originalPort = originalPorts.get(key); /** * Step1. Skip if it is already SOAP */ List<ExtensibilityElement> extList = originalPort.getExtensibilityElements(); for(ExtensibilityElement extElem : extList) { if(extElem instanceof SOAPAddress) { isSOAP = true; break; } } if(isSOAP) { continue; } wsdlModified = true; /** * Step2. Collect the required information from * the existing <wsdl:port> */ String originalPortName = originalPort.getName(); Element originalPortDocElem = originalPort.getDocumentationElement(); Binding originalBinding = originalPort.getBinding(); /** * Step3. Create a new port and add it to <wsdl:service> * * <wsdl:port name="#name" binding="#tns:binding"> * <soap:address location="REPLACE_WITH_ACTUAL_URL"/> * </wsdl:port> * */ Port newPort = defn.createPort(); // Add -> name="#name" newPort.setName(originalPortName); // Add -> binding="#tns:binding" // Since 'originalBinding' might have been deleted already, so get the binding from defn newPort.setBinding(defn.getBinding(originalBinding.getQName())); newPort.setDocumentationElement(originalPortDocElem); // Add -> <soap:address location="REPLACE_WITH_ACTUAL_URL"/> SOAPAddress soapAddress = createSoapAddress(); //sa.setRequired(true); newPort.addExtensibilityElement(soapAddress); /** * Step4. Add the newly created <wsdl:port> to <wsdl:service>. */ newService.setQName(originalService.getQName()); newService.addPort(newPort); } if(!isSOAP) { defn.removeService(originalService.getQName()); defn.addService(newService); } } return wsdlModified; } /** * Converts all <wsdl:binding> sections. */ private boolean convertBindings(Definition defn) throws Exception { boolean wsdlModified = false; Map<QName,Binding> bindings = defn.getBindings(); if(bindings == null || bindings.isEmpty()) { return wsdlModified; } Binding[] originalBindings = bindings.values().toArray(new Binding[0]); for(Binding originalBinding : originalBindings) { /** * Step 1. Skip if it is already SOAP */ boolean isSOAP = false; List<ExtensibilityElement> extList = originalBinding.getExtensibilityElements(); for(ExtensibilityElement extElem : extList) { if(extElem instanceof SOAPBinding) { isSOAP = true; break; } } if(isSOAP) { continue; } wsdlModified = true; /** * Step 2. Collect the required information from * the existing <wsdl:port> */ PortType p = originalBinding.getPortType(); QName originalBindingQname = originalBinding.getQName(); List<BindingOperation> originalBindingOps = originalBinding.getBindingOperations(); /** * Step 3. Create a new binding * * <wsdl:binding name="#name" type="#type"> * <soap:binding transport="http://schemas.xmlsoap.org/soap/http"/> * <wsdl:operation name="#operation1"> * <soap:operation/> * <wsdl:input> * <soap:body use="literal" namespace="#namespace"/> * </wsdl:input> * <wsdl:output> * <soap:body use="literal" namespace="#namespace"/> * </wsdl:output> * <wsdl:fault name="#faultname"> * <soap:fault name="#faultname" use="literal"/> * </wsdl:fault> * ...more faults... * </wsdl:operation> * ...more operations... * </wsdl:binding> * */ Binding newBinding = defn.createBinding(); // Add -> name="#name" newBinding.setQName(originalBindingQname); // Add -> type="#type" newBinding.setPortType(p); newBinding.setUndefined(false); // Add -> <soap:binding tranport="http://schemas.xmlsoap.org/soap/http" SOAPBinding soapBinding = createSoapBinding(); newBinding.addExtensibilityElement(soapBinding); /** * Add -> * * <wsdl:operation name="#operation1"> * .... * </wsdl:operation> * ... more operations... * */ String style = null; for(BindingOperation originalBindingOp : originalBindingOps) { BindingOperation newBindingOp = defn.createBindingOperation(); newBindingOp.setOperation(originalBindingOp.getOperation()); Operation portTypeOperation = newBindingOp.getOperation(); /** * * Style should be set in <soap:binding/>, not in <soap:operation/> * * WS-I Basic Profile 1.0 : * * 5.6.3 Consistency of style Attribute : * * The style, "document" or "rpc", of an interaction is specified * at the wsdl:operation level, permitting wsdl:bindings whose wsdl:operations * have different styles. This has led to interoperability problems. * * R2705 A wsdl:binding in a DESCRIPTION MUST use either be a rpc-literal binding * or a document-literal binding. * */ if(style == null) { style = getStyle(portTypeOperation); if(RPC_STYLE.equals(style)) { // default style is 'document', no need to set. soapBinding.setStyle(style); } } // Add > name="#operation". newBindingOp.setName(originalBindingOp.getName()); newBindingOp.setDocumentationElement(originalBindingOp.getDocumentationElement()); // Add -> <soap:operation/> SOAPOperation soapOperation = createSoapOperation(); newBindingOp.addExtensibilityElement(soapOperation); // Add -> <wsdl:input><soap:body use="literal" namespace="#namespace"/></wsdl:input> if(portTypeOperation.getInput() != null) { BindingInput newBindingInput = defn.createBindingInput(); processElement( newBindingInput, portTypeOperation.getInput().getMessage(), style); newBindingOp.setBindingInput(newBindingInput); } // Add -> <wsdl:output><soap:body use="literal" namespace="#namespace"/></wsdl:output> if(portTypeOperation.getOutput() != null) { BindingOutput newBindingOutput = defn.createBindingOutput(); processElement( newBindingOutput, portTypeOperation.getOutput().getMessage(), style); newBindingOp.setBindingOutput(newBindingOutput); } // Add -> <wsdl:fault name="faultname"><soap:fault use="literal" name="#faultname"/></wsdl:fault>....more faults... if(portTypeOperation.getFaults() != null) { Map<String,Fault> faults = portTypeOperation.getFaults(); for(String faultName : faults.keySet()) { BindingFault newBindingFault = defn.createBindingFault(); newBindingFault.setName(faultName); SOAPFault soapFault = createSoapFault(); soapFault.setName(faultName); newBindingFault.addExtensibilityElement(soapFault); newBindingOp.addBindingFault(newBindingFault); } } newBinding.addBindingOperation(newBindingOp); } /** * Step4. Replace the existing <wsdl:binding> with new one. */ defn.removeBinding(originalBindingQname); defn.addBinding(newBinding); } return wsdlModified; } private void writeWSDL(Definition defn, String wsdl) throws Exception { defn.addNamespace(SOAP_PREFIX, SOAP_NS); /** * Take care of i18n while creating a FileWriter. */ FileWriter fw = new FileWriter(wsdl); wsdlWriter.writeWSDL(defn, fw); fw.close(); } public boolean convert(String wsdl) throws Exception { String wsdlLocationUri = new File(wsdl).toURI().toString(); Definition defn = wsdlReader.readWSDL(wsdlLocationUri); boolean wsdlModified = false; /** * Modify the <wsdl:binding> sections. */ wsdlModified = convertBindings(defn); /** * Modify the <wsdl:service> sections. */ wsdlModified = convertServices(defn); if(wsdlModified) { System.out.println("Modified WSDL : " + wsdl); writeWSDL(defn, wsdl); } return wsdlModified; } List<String> listWSDLs(File baseDir) { List<String> files = new ArrayList(); for(File f : baseDir.listFiles()) { if(f.isDirectory()) { files.addAll(listWSDLs(f)); } else if(f.getAbsolutePath().endsWith("wsdl")) { files.add(f.getAbsolutePath()); } } return files; } public List<String> convertAllWSDLs(String baseDir) { List<String> convertedWSDLs = new ArrayList<String>(); List<String> wsdls = listWSDLs(new File(baseDir)); for(String wsdl : wsdls) { try { if(convert(wsdl)) { convertedWSDLs.add(wsdl.substring(baseDir.length() + 1)); } } catch(Exception ex) { ex.printStackTrace(); } } System.out.println("BaseDir = " + baseDir + ", Converted WSDLs = " + convertedWSDLs); return convertedWSDLs; } public static List<String> convertWSDLs(String baseDir) { try { WSDLConverter converter = new WSDLConverter(); return converter.convertAllWSDLs(baseDir); } catch(Exception ex) { ex.printStackTrace(); } return new ArrayList<String>(); } void print(String s) { System.out.println(s); } /** * @param args the command line arguments */ public static void main(String[] args) throws Exception { convertWSDLs("/tmp/wsdl");//System.getProperty("java.io.tmpdir")); } }