/* * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.xml.internal.ws.wsdl.writer; import static com.sun.xml.internal.bind.v2.schemagen.Util.*; import com.oracle.webservices.internal.api.databinding.WSDLResolver; import com.sun.xml.internal.txw2.TXW; import com.sun.xml.internal.txw2.TypedXmlWriter; import com.sun.xml.internal.txw2.output.ResultFactory; import com.sun.xml.internal.txw2.output.XmlSerializer; import com.sun.xml.internal.txw2.output.TXWResult; import com.sun.xml.internal.ws.api.SOAPVersion; import com.sun.xml.internal.ws.api.WSBinding; import com.sun.xml.internal.ws.api.model.JavaMethod; import com.sun.xml.internal.ws.api.model.MEP; import com.sun.xml.internal.ws.api.model.ParameterBinding; import com.sun.xml.internal.ws.api.model.SEIModel; import com.sun.xml.internal.ws.api.model.soap.SOAPBinding; import com.sun.xml.internal.ws.api.server.Container; import com.sun.xml.internal.ws.api.wsdl.writer.WSDLGeneratorExtension; import com.sun.xml.internal.ws.api.wsdl.writer.WSDLGenExtnContext; import com.sun.xml.internal.ws.model.AbstractSEIModelImpl; import com.sun.xml.internal.ws.model.CheckedExceptionImpl; import com.sun.xml.internal.ws.model.JavaMethodImpl; import com.sun.xml.internal.ws.model.ParameterImpl; import com.sun.xml.internal.ws.model.WrapperParameter; import com.sun.xml.internal.ws.util.xml.XmlUtil; import com.sun.xml.internal.ws.wsdl.parser.SOAPConstants; import com.sun.xml.internal.ws.wsdl.parser.WSDLConstants; import com.sun.xml.internal.ws.wsdl.writer.document.Binding; import com.sun.xml.internal.ws.wsdl.writer.document.BindingOperationType; import com.sun.xml.internal.ws.wsdl.writer.document.Definitions; import com.sun.xml.internal.ws.wsdl.writer.document.Fault; import com.sun.xml.internal.ws.wsdl.writer.document.FaultType; import com.sun.xml.internal.ws.wsdl.writer.document.Import; import com.sun.xml.internal.ws.wsdl.writer.document.Message; import com.sun.xml.internal.ws.wsdl.writer.document.Operation; import com.sun.xml.internal.ws.wsdl.writer.document.ParamType; import com.sun.xml.internal.ws.wsdl.writer.document.Port; import com.sun.xml.internal.ws.wsdl.writer.document.PortType; import com.sun.xml.internal.ws.wsdl.writer.document.Service; import com.sun.xml.internal.ws.wsdl.writer.document.Types; import com.sun.xml.internal.ws.wsdl.writer.document.soap.Body; import com.sun.xml.internal.ws.wsdl.writer.document.soap.BodyType; import com.sun.xml.internal.ws.wsdl.writer.document.soap.Header; import com.sun.xml.internal.ws.wsdl.writer.document.soap.SOAPAddress; import com.sun.xml.internal.ws.wsdl.writer.document.soap.SOAPFault; import com.sun.xml.internal.ws.spi.db.BindingContext; import com.sun.xml.internal.ws.spi.db.BindingHelper; import com.sun.xml.internal.ws.util.RuntimeVersion; import com.sun.xml.internal.ws.policy.jaxws.PolicyWSDLGeneratorExtension; import com.sun.xml.internal.ws.encoding.soap.streaming.SOAPNamespaceConstants; import javax.jws.soap.SOAPBinding.Style; import javax.jws.soap.SOAPBinding.Use; import javax.xml.bind.SchemaOutputResolver; import javax.xml.namespace.QName; import javax.xml.transform.Result; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXResult; import javax.xml.ws.Holder; import javax.xml.ws.WebServiceException; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; /** * Class used to generate WSDLs from a {@link SEIModel}. * * @author WS Development Team */ public class WSDLGenerator { private JAXWSOutputSchemaResolver resolver; private WSDLResolver wsdlResolver = null; private AbstractSEIModelImpl model; private Definitions serviceDefinitions; private Definitions portDefinitions; private Types types; /** * Constant String for ".wsdl" */ private static final String DOT_WSDL = ".wsdl"; /** * The WSDL namespace */ private static final String WSDL_NAMESPACE = WSDLConstants.NS_WSDL; /** * the XSD namespace */ private static final String XSD_NAMESPACE = SOAPNamespaceConstants.XSD; /** * the namespace prefix to use for the XSD namespace */ private static final String XSD_PREFIX = "xsd"; /** * The SOAP 1.1 namespace */ private static final String SOAP11_NAMESPACE = SOAPConstants.NS_WSDL_SOAP; /** * The SOAP 1.2 namespace */ private static final String SOAP12_NAMESPACE = SOAPConstants.NS_WSDL_SOAP12; /** * The namespace prefix to use for the SOAP 1.1 namespace */ private static final String SOAP_PREFIX = "soap"; /** * The namespace prefix to use for the SOAP 1.2 namespace */ private static final String SOAP12_PREFIX = "soap12"; /** * The namespace prefix to use for the targetNamespace */ private static final String TNS_PREFIX = "tns"; /** * Constant String "document" used to specify <code>document</code> style * soapBindings */ private static final String DOCUMENT = "document"; /** * Constant String "rpc" used to specify <code>rpc</code> style * soapBindings */ private static final String RPC = "rpc"; /** * Constant String "literal" used to create <code>literal</code> use binddings */ private static final String LITERAL = "literal"; /** * Constant String to flag the URL to replace at runtime for the endpoint */ private static final String REPLACE_WITH_ACTUAL_URL = "REPLACE_WITH_ACTUAL_URL"; static public final String XsdNs = "http://www.w3.org/2001/XMLSchema"; private Set<QName> processedExceptions = new HashSet<QName>(); private WSBinding binding; private String wsdlLocation; private String portWSDLID; private String schemaPrefix; private WSDLGeneratorExtension extension; List<WSDLGeneratorExtension> extensionHandlers; private String endpointAddress = REPLACE_WITH_ACTUAL_URL; private Container container; private final Class implType; private boolean inlineSchemas; // TODO private final boolean disableXmlSecurity; /** * Creates the WSDLGenerator * @param model The {@link AbstractSEIModelImpl} used to generate the WSDL * @param wsdlResolver The {@link WSDLResolver} to use resovle names while generating the WSDL * @param binding specifies which {@link javax.xml.ws.BindingType} to generate * @param extensions an array {@link WSDLGeneratorExtension} that will * be invoked to generate WSDL extensions */ public WSDLGenerator(AbstractSEIModelImpl model, WSDLResolver wsdlResolver, WSBinding binding, Container container, Class implType, boolean inlineSchemas, WSDLGeneratorExtension... extensions) { this(model, wsdlResolver, binding, container, implType, inlineSchemas, false, extensions); } /** * Creates the WSDLGenerator * @param model The {@link AbstractSEIModelImpl} used to generate the WSDL * @param wsdlResolver The {@link WSDLResolver} to use resovle names while generating the WSDL * @param binding specifies which {@link javax.xml.ws.BindingType} to generate * @param disableXmlSecurity specifies whether to disable the secure xml processing feature * @param extensions an array {@link WSDLGeneratorExtension} that will * be invoked to generate WSDL extensions */ public WSDLGenerator(AbstractSEIModelImpl model, WSDLResolver wsdlResolver, WSBinding binding, Container container, Class implType, boolean inlineSchemas, boolean disableXmlSecurity, WSDLGeneratorExtension... extensions) { this.model = model; resolver = new JAXWSOutputSchemaResolver(); this.wsdlResolver = wsdlResolver; this.binding = binding; this.container = container; this.implType = implType; extensionHandlers = new ArrayList<WSDLGeneratorExtension>(); this.inlineSchemas = inlineSchemas; this.disableXmlSecurity = disableXmlSecurity; // register handlers for default extensions register(new W3CAddressingWSDLGeneratorExtension()); register(new W3CAddressingMetadataWSDLGeneratorExtension()); register(new PolicyWSDLGeneratorExtension()); if (container != null) { // on server WSDLGeneratorExtension[] wsdlGeneratorExtensions = container.getSPI(WSDLGeneratorExtension[].class); if (wsdlGeneratorExtensions != null) { for (WSDLGeneratorExtension wsdlGeneratorExtension : wsdlGeneratorExtensions) { register(wsdlGeneratorExtension); } } } for (WSDLGeneratorExtension w : extensions) register(w); this.extension = new WSDLGeneratorExtensionFacade(extensionHandlers.toArray(new WSDLGeneratorExtension[0])); } /** * Sets the endpoint address string to be written. * Defaults to {@link #REPLACE_WITH_ACTUAL_URL}. * * @param address wsdl:port/soap:address/[@location] value */ public void setEndpointAddress(String address) { this.endpointAddress = address; } protected String mangleName(String name) { return BindingHelper.mangleNameToClassName(name); } /** * Performes the actual WSDL generation */ public void doGeneration() { XmlSerializer serviceWriter; XmlSerializer portWriter = null; String fileName = mangleName(model.getServiceQName().getLocalPart()); Result result = wsdlResolver.getWSDL(fileName + DOT_WSDL); wsdlLocation = result.getSystemId(); serviceWriter = new CommentFilter(ResultFactory.createSerializer(result)); if (model.getServiceQName().getNamespaceURI().equals(model.getTargetNamespace())) { portWriter = serviceWriter; schemaPrefix = fileName + "_"; } else { String wsdlName = mangleName(model.getPortTypeName().getLocalPart()); if (wsdlName.equals(fileName)) wsdlName += "PortType"; Holder<String> absWSDLName = new Holder<String>(); absWSDLName.value = wsdlName + DOT_WSDL; result = wsdlResolver.getAbstractWSDL(absWSDLName); if (result != null) { portWSDLID = result.getSystemId(); if (portWSDLID.equals(wsdlLocation)) { portWriter = serviceWriter; } else { portWriter = new CommentFilter(ResultFactory.createSerializer(result)); } } else { portWSDLID = absWSDLName.value; } schemaPrefix = new java.io.File(portWSDLID).getName(); int idx = schemaPrefix.lastIndexOf('.'); if (idx > 0) schemaPrefix = schemaPrefix.substring(0, idx); schemaPrefix = mangleName(schemaPrefix) + "_"; } generateDocument(serviceWriter, portWriter); } /** * Writing directly to XmlSerializer is a problem, since it doesn't suppress * xml declaration. Creating filter so that comment is written before TXW writes * anything in the WSDL. */ private static class CommentFilter implements XmlSerializer { final XmlSerializer serializer; private static final String VERSION_COMMENT = " Generated by JAX-WS RI (http://jax-ws.java.net). RI's version is " + RuntimeVersion.VERSION + ". "; CommentFilter(XmlSerializer serializer) { this.serializer = serializer; } @Override public void startDocument() { serializer.startDocument(); comment(new StringBuilder(VERSION_COMMENT)); text(new StringBuilder("\n")); } @Override public void beginStartTag(String uri, String localName, String prefix) { serializer.beginStartTag(uri, localName, prefix); } @Override public void writeAttribute(String uri, String localName, String prefix, StringBuilder value) { serializer.writeAttribute(uri, localName, prefix, value); } @Override public void writeXmlns(String prefix, String uri) { serializer.writeXmlns(prefix, uri); } @Override public void endStartTag(String uri, String localName, String prefix) { serializer.endStartTag(uri, localName, prefix); } @Override public void endTag() { serializer.endTag(); } @Override public void text(StringBuilder text) { serializer.text(text); } @Override public void cdata(StringBuilder text) { serializer.cdata(text); } @Override public void comment(StringBuilder comment) { serializer.comment(comment); } @Override public void endDocument() { serializer.endDocument(); } @Override public void flush() { serializer.flush(); } } private void generateDocument(XmlSerializer serviceStream, XmlSerializer portStream) { serviceDefinitions = TXW.create(Definitions.class, serviceStream); serviceDefinitions._namespace(WSDL_NAMESPACE, "");//WSDL_PREFIX); serviceDefinitions._namespace(XSD_NAMESPACE, XSD_PREFIX); serviceDefinitions.targetNamespace(model.getServiceQName().getNamespaceURI()); serviceDefinitions._namespace(model.getServiceQName().getNamespaceURI(), TNS_PREFIX); if (binding.getSOAPVersion() == SOAPVersion.SOAP_12) serviceDefinitions._namespace(SOAP12_NAMESPACE, SOAP12_PREFIX); else serviceDefinitions._namespace(SOAP11_NAMESPACE, SOAP_PREFIX); serviceDefinitions.name(model.getServiceQName().getLocalPart()); WSDLGenExtnContext serviceCtx = new WSDLGenExtnContext(serviceDefinitions, model, binding, container, implType); extension.start(serviceCtx); if (serviceStream != portStream && portStream != null) { // generate an abstract and concrete wsdl portDefinitions = TXW.create(Definitions.class, portStream); portDefinitions._namespace(WSDL_NAMESPACE, "");//WSDL_PREFIX); portDefinitions._namespace(XSD_NAMESPACE, XSD_PREFIX); if (model.getTargetNamespace() != null) { portDefinitions.targetNamespace(model.getTargetNamespace()); portDefinitions._namespace(model.getTargetNamespace(), TNS_PREFIX); } String schemaLoc = relativize(portWSDLID, wsdlLocation); Import _import = serviceDefinitions._import().namespace(model.getTargetNamespace()); _import.location(schemaLoc); } else if (portStream != null) { // abstract and concrete are the same portDefinitions = serviceDefinitions; } else { // import a provided abstract wsdl String schemaLoc = relativize(portWSDLID, wsdlLocation); Import _import = serviceDefinitions._import().namespace(model.getTargetNamespace()); _import.location(schemaLoc); } extension.addDefinitionsExtension(serviceDefinitions); if (portDefinitions != null) { generateTypes(); generateMessages(); generatePortType(); } generateBinding(); generateService(); //Give a chance to WSDLGeneratorExtensions to write stuff before closing </wsdl:defintions> extension.end(serviceCtx); serviceDefinitions.commit(); if (portDefinitions != null && portDefinitions != serviceDefinitions) portDefinitions.commit(); } /** * Generates the types section of the WSDL */ protected void generateTypes() { types = portDefinitions.types(); if (model.getBindingContext() != null) { if (inlineSchemas && model.getBindingContext().getClass().getName().indexOf("glassfish") == -1) { resolver.nonGlassfishSchemas = new ArrayList<DOMResult>(); } try { model.getBindingContext().generateSchema(resolver); } catch (IOException e) { // TODO locallize and wrap this throw new WebServiceException(e.getMessage()); } } if (resolver.nonGlassfishSchemas != null) { TransformerFactory tf = XmlUtil.newTransformerFactory(!disableXmlSecurity); try { Transformer t = tf.newTransformer(); for (DOMResult xsd : resolver.nonGlassfishSchemas) { Document doc = (Document) xsd.getNode(); if (inlineSchemas) { NodeList importList = doc.getDocumentElement().getElementsByTagNameNS("http://www.w3.org/2001/XMLSchema", "import"); for(int i = 0; i < importList.getLength(); i++) { org.w3c.dom.Element impElem = (org.w3c.dom.Element)importList.item(i); impElem.removeAttribute("schemaLocation"); } } SAXResult sax = new SAXResult(new TXWContentHandler(types)); t.transform(new DOMSource(doc.getDocumentElement()), sax); } } catch (TransformerConfigurationException e) { throw new WebServiceException(e.getMessage(), e); } catch (TransformerException e) { throw new WebServiceException(e.getMessage(), e); } } } /** * Generates the WSDL messages */ protected void generateMessages() { for (JavaMethodImpl method : model.getJavaMethods()) { generateSOAPMessages(method, method.getBinding()); } } /** * Generates messages for a SOAPBinding * @param method The {@link JavaMethod} to generate messages for * @param binding The {@link com.sun.xml.internal.ws.api.model.soap.SOAPBinding} to add the generated messages to */ protected void generateSOAPMessages(JavaMethodImpl method, com.sun.xml.internal.ws.api.model.soap.SOAPBinding binding) { boolean isDoclit = binding.isDocLit(); // Message message = portDefinitions.message().name(method.getOperation().getName().getLocalPart()); Message message = portDefinitions.message().name(method.getRequestMessageName()); extension.addInputMessageExtension(message, method); com.sun.xml.internal.ws.wsdl.writer.document.Part part; BindingContext jaxbContext = model.getBindingContext(); boolean unwrappable = true; for (ParameterImpl param : method.getRequestParameters()) { if (isDoclit) { if (isHeaderParameter(param)) unwrappable = false; part = message.part().name(param.getPartName()); part.element(param.getName()); } else { if (param.isWrapperStyle()) { for (ParameterImpl childParam : ((WrapperParameter) param).getWrapperChildren()) { part = message.part().name(childParam.getPartName()); part.type(jaxbContext.getTypeName(childParam.getXMLBridge().getTypeInfo())); } } else { part = message.part().name(param.getPartName()); part.element(param.getName()); } } } if (method.getMEP() != MEP.ONE_WAY) { message = portDefinitions.message().name(method.getResponseMessageName()); extension.addOutputMessageExtension(message, method); for (ParameterImpl param : method.getResponseParameters()) { if (isDoclit) { part = message.part().name(param.getPartName()); part.element(param.getName()); } else { if (param.isWrapperStyle()) { for (ParameterImpl childParam : ((WrapperParameter) param).getWrapperChildren()) { part = message.part().name(childParam.getPartName()); part.type(jaxbContext.getTypeName(childParam.getXMLBridge().getTypeInfo())); } } else { part = message.part().name(param.getPartName()); part.element(param.getName()); } } } } for (CheckedExceptionImpl exception : method.getCheckedExceptions()) { QName tagName = exception.getDetailType().tagName; String messageName = exception.getMessageName(); QName messageQName = new QName(model.getTargetNamespace(), messageName); if (processedExceptions.contains(messageQName)) continue; message = portDefinitions.message().name(messageName); extension.addFaultMessageExtension(message, method, exception); part = message.part().name("fault");//tagName.getLocalPart()); part.element(tagName); processedExceptions.add(messageQName); } } /** * Generates the WSDL portType */ protected void generatePortType() { PortType portType = portDefinitions.portType().name(model.getPortTypeName().getLocalPart()); extension.addPortTypeExtension(portType); for (JavaMethodImpl method : model.getJavaMethods()) { Operation operation = portType.operation().name(method.getOperationName()); generateParameterOrder(operation, method); extension.addOperationExtension(operation, method); switch (method.getMEP()) { case REQUEST_RESPONSE: // input message generateInputMessage(operation, method); // output message generateOutputMessage(operation, method); break; case ONE_WAY: generateInputMessage(operation, method); break; default: break; } // faults for (CheckedExceptionImpl exception : method.getCheckedExceptions()) { QName messageName = new QName(model.getTargetNamespace(), exception.getMessageName()); FaultType paramType = operation.fault().message(messageName).name(exception.getMessageName()); extension.addOperationFaultExtension(paramType, method, exception); } } } /** * Determines if the <CODE>method</CODE> is wrapper style * @param method The {@link JavaMethod} to check if it is wrapper style * @return true if the method is wrapper style, otherwise, false. */ protected boolean isWrapperStyle(JavaMethodImpl method) { if (method.getRequestParameters().size() > 0) { ParameterImpl param = method.getRequestParameters().iterator().next(); return param.isWrapperStyle(); } return false; } /** * Determines if a {@link JavaMethod} is rpc/literal * @param method The method to check * @return true if method is rpc/literal, otherwise, false */ protected boolean isRpcLit(JavaMethodImpl method) { return method.getBinding().getStyle() == Style.RPC; } /** * Generates the parameterOrder for a PortType operation * @param operation The operation to generate the parameterOrder for * @param method The {@link JavaMethod} to generate the parameterOrder from */ protected void generateParameterOrder(Operation operation, JavaMethodImpl method) { if (method.getMEP() == MEP.ONE_WAY) return; if (isRpcLit(method)) generateRpcParameterOrder(operation, method); else generateDocumentParameterOrder(operation, method); } /** * Generates the parameterOrder for a PortType operation * @param operation the operation to generate the parameterOrder for * @param method the {@link JavaMethod} to generate the parameterOrder from */ protected void generateRpcParameterOrder(Operation operation, JavaMethodImpl method) { String partName; StringBuilder paramOrder = new StringBuilder(); Set<String> partNames = new HashSet<String>(); List<ParameterImpl> sortedParams = sortMethodParameters(method); int i = 0; for (ParameterImpl parameter : sortedParams) { if (parameter.getIndex() >= 0) { partName = parameter.getPartName(); if (!partNames.contains(partName)) { if (i++ > 0) paramOrder.append(' '); paramOrder.append(partName); partNames.add(partName); } } } if (i > 1) { operation.parameterOrder(paramOrder.toString()); } } /** * Generates the parameterOrder for a PortType operation * @param operation the operation to generate the parameterOrder for * @param method the {@link JavaMethod} to generate the parameterOrder from */ protected void generateDocumentParameterOrder(Operation operation, JavaMethodImpl method) { String partName; StringBuilder paramOrder = new StringBuilder(); Set<String> partNames = new HashSet<String>(); List<ParameterImpl> sortedParams = sortMethodParameters(method); // boolean isWrapperStyle = isWrapperStyle(method); int i = 0; for (ParameterImpl parameter : sortedParams) { // System.out.println("param: "+parameter.getIndex()+" name: "+parameter.getName().getLocalPart()); if (parameter.getIndex() < 0) continue; // This should be safe change. if it affects compatibility, // remove the following single statement and uncomment the code in block below. partName = parameter.getPartName(); /* if (isWrapperStyle && isBodyParameter(parameter)) { System.out.println("isWrapper and is body"); if (method.getRequestParameters().contains(parameter)) partName = PARAMETERS; else { //Rama: don't understand this logic "Response" below, // really make sure this is a wrapper style wsdl we are creating partName = RESPONSE; } } else { partName = parameter.getPartName(); }*/ if (!partNames.contains(partName)) { if (i++ > 0) paramOrder.append(' '); paramOrder.append(partName); partNames.add(partName); } } if (i > 1) { operation.parameterOrder(paramOrder.toString()); } } /** * Sorts the parameters for the method by their position * @param method the {@link JavaMethod} used to sort the parameters * @return the sorted {@link List} of parameters */ protected List<ParameterImpl> sortMethodParameters(JavaMethodImpl method) { Set<ParameterImpl> paramSet = new HashSet<ParameterImpl>(); List<ParameterImpl> sortedParams = new ArrayList<ParameterImpl>(); if (isRpcLit(method)) { for (ParameterImpl param : method.getRequestParameters()) { if (param instanceof WrapperParameter) { paramSet.addAll(((WrapperParameter) param).getWrapperChildren()); } else { paramSet.add(param); } } for (ParameterImpl param : method.getResponseParameters()) { if (param instanceof WrapperParameter) { paramSet.addAll(((WrapperParameter) param).getWrapperChildren()); } else { paramSet.add(param); } } } else { paramSet.addAll(method.getRequestParameters()); paramSet.addAll(method.getResponseParameters()); } Iterator<ParameterImpl> params = paramSet.iterator(); if (paramSet.isEmpty()) return sortedParams; ParameterImpl param = params.next(); sortedParams.add(param); ParameterImpl sortedParam; int pos; for (int i = 1; i < paramSet.size(); i++) { param = params.next(); for (pos = 0; pos < i; pos++) { sortedParam = sortedParams.get(pos); if (param.getIndex() == sortedParam.getIndex() && param instanceof WrapperParameter) break; if (param.getIndex() < sortedParam.getIndex()) { break; } } sortedParams.add(pos, param); } return sortedParams; } /** * Determines if a parameter is associated with the message Body * @param parameter the parameter to check * @return true if the parameter is a <code>body</code> parameter */ protected boolean isBodyParameter(ParameterImpl parameter) { ParameterBinding paramBinding = parameter.getBinding(); return paramBinding.isBody(); } protected boolean isHeaderParameter(ParameterImpl parameter) { ParameterBinding paramBinding = parameter.getBinding(); return paramBinding.isHeader(); } protected boolean isAttachmentParameter(ParameterImpl parameter) { ParameterBinding paramBinding = parameter.getBinding(); return paramBinding.isAttachment(); } /** * Generates the Binding section of the WSDL */ protected void generateBinding() { Binding newBinding = serviceDefinitions.binding().name(model.getBoundPortTypeName().getLocalPart()); extension.addBindingExtension(newBinding); newBinding.type(model.getPortTypeName()); boolean first = true; for (JavaMethodImpl method : model.getJavaMethods()) { if (first) { SOAPBinding sBinding = method.getBinding(); SOAPVersion soapVersion = sBinding.getSOAPVersion(); if (soapVersion == SOAPVersion.SOAP_12) { com.sun.xml.internal.ws.wsdl.writer.document.soap12.SOAPBinding soapBinding = newBinding.soap12Binding(); soapBinding.transport(this.binding.getBindingId().getTransport()); if (sBinding.getStyle().equals(Style.DOCUMENT)) soapBinding.style(DOCUMENT); else soapBinding.style(RPC); } else { com.sun.xml.internal.ws.wsdl.writer.document.soap.SOAPBinding soapBinding = newBinding.soapBinding(); soapBinding.transport(this.binding.getBindingId().getTransport()); if (sBinding.getStyle().equals(Style.DOCUMENT)) soapBinding.style(DOCUMENT); else soapBinding.style(RPC); } first = false; } if (this.binding.getBindingId().getSOAPVersion() == SOAPVersion.SOAP_12) generateSOAP12BindingOperation(method, newBinding); else generateBindingOperation(method, newBinding); } } protected void generateBindingOperation(JavaMethodImpl method, Binding binding) { BindingOperationType operation = binding.operation().name(method.getOperationName()); extension.addBindingOperationExtension(operation, method); String targetNamespace = model.getTargetNamespace(); QName requestMessage = new QName(targetNamespace, method.getOperationName()); List<ParameterImpl> bodyParams = new ArrayList<ParameterImpl>(); List<ParameterImpl> headerParams = new ArrayList<ParameterImpl>(); splitParameters(bodyParams, headerParams, method.getRequestParameters()); SOAPBinding soapBinding = method.getBinding(); operation.soapOperation().soapAction(soapBinding.getSOAPAction()); // input TypedXmlWriter input = operation.input(); extension.addBindingOperationInputExtension(input, method); BodyType body = input._element(Body.class); boolean isRpc = soapBinding.getStyle().equals(Style.RPC); if (soapBinding.getUse() == Use.LITERAL) { body.use(LITERAL); if (headerParams.size() > 0) { if (bodyParams.size() > 0) { ParameterImpl param = bodyParams.iterator().next(); if (isRpc) { StringBuilder parts = new StringBuilder(); int i = 0; for (ParameterImpl parameter : ((WrapperParameter) param).getWrapperChildren()) { if (i++ > 0) parts.append(' '); parts.append(parameter.getPartName()); } body.parts(parts.toString()); } else { body.parts(param.getPartName()); } } else { body.parts(""); } generateSOAPHeaders(input, headerParams, requestMessage); } if (isRpc) { body.namespace(method.getRequestParameters().iterator().next().getName().getNamespaceURI()); } } else { // TODO localize this throw new WebServiceException("encoded use is not supported"); } if (method.getMEP() != MEP.ONE_WAY) { // output bodyParams.clear(); headerParams.clear(); splitParameters(bodyParams, headerParams, method.getResponseParameters()); TypedXmlWriter output = operation.output(); extension.addBindingOperationOutputExtension(output, method); body = output._element(Body.class); body.use(LITERAL); if (headerParams.size() > 0) { StringBuilder parts = new StringBuilder(); if (bodyParams.size() > 0) { ParameterImpl param = bodyParams.iterator().hasNext() ? bodyParams.iterator().next() : null; if (param != null) { if (isRpc) { int i = 0; for (ParameterImpl parameter : ((WrapperParameter) param).getWrapperChildren()) { if (i++ > 0) { parts.append(" "); } parts.append(parameter.getPartName()); } } else { parts = new StringBuilder(param.getPartName()); } } } body.parts(parts.toString()); QName responseMessage = new QName(targetNamespace, method.getResponseMessageName()); generateSOAPHeaders(output, headerParams, responseMessage); } if (isRpc) { body.namespace(method.getRequestParameters().iterator().next().getName().getNamespaceURI()); } } for (CheckedExceptionImpl exception : method.getCheckedExceptions()) { Fault fault = operation.fault().name(exception.getMessageName()); extension.addBindingOperationFaultExtension(fault, method, exception); SOAPFault soapFault = fault._element(SOAPFault.class).name(exception.getMessageName()); soapFault.use(LITERAL); } } protected void generateSOAP12BindingOperation(JavaMethodImpl method, Binding binding) { BindingOperationType operation = binding.operation().name(method.getOperationName()); extension.addBindingOperationExtension(operation, method); String targetNamespace = model.getTargetNamespace(); QName requestMessage = new QName(targetNamespace, method.getOperationName()); ArrayList<ParameterImpl> bodyParams = new ArrayList<ParameterImpl>(); ArrayList<ParameterImpl> headerParams = new ArrayList<ParameterImpl>(); splitParameters(bodyParams, headerParams, method.getRequestParameters()); SOAPBinding soapBinding = method.getBinding(); String soapAction = soapBinding.getSOAPAction(); if (soapAction != null) { operation.soap12Operation().soapAction(soapAction); } // input TypedXmlWriter input = operation.input(); extension.addBindingOperationInputExtension(input, method); com.sun.xml.internal.ws.wsdl.writer.document.soap12.BodyType body = input._element(com.sun.xml.internal.ws.wsdl.writer.document.soap12.Body.class); boolean isRpc = soapBinding.getStyle().equals(Style.RPC); if (soapBinding.getUse().equals(Use.LITERAL)) { body.use(LITERAL); if (headerParams.size() > 0) { if (bodyParams.size() > 0) { ParameterImpl param = bodyParams.iterator().next(); if (isRpc) { StringBuilder parts = new StringBuilder(); int i = 0; for (ParameterImpl parameter : ((WrapperParameter) param).getWrapperChildren()) { if (i++ > 0) parts.append(' '); parts.append(parameter.getPartName()); } body.parts(parts.toString()); } else { body.parts(param.getPartName()); } } else { body.parts(""); } generateSOAP12Headers(input, headerParams, requestMessage); } if (isRpc) { body.namespace(method.getRequestParameters().iterator().next().getName().getNamespaceURI()); } } else { // TODO localize this throw new WebServiceException("encoded use is not supported"); } if (method.getMEP() != MEP.ONE_WAY) { // output bodyParams.clear(); headerParams.clear(); splitParameters(bodyParams, headerParams, method.getResponseParameters()); TypedXmlWriter output = operation.output(); extension.addBindingOperationOutputExtension(output, method); body = output._element(com.sun.xml.internal.ws.wsdl.writer.document.soap12.Body.class); body.use(LITERAL); if (headerParams.size() > 0) { if (bodyParams.size() > 0) { ParameterImpl param = bodyParams.iterator().next(); if (isRpc) { StringBuilder parts = new StringBuilder(); int i = 0; for (ParameterImpl parameter : ((WrapperParameter) param).getWrapperChildren()) { if (i++ > 0) { parts.append(" "); } parts.append(parameter.getPartName()); } body.parts(parts.toString()); } else { body.parts(param.getPartName()); } } else { body.parts(""); } QName responseMessage = new QName(targetNamespace, method.getResponseMessageName()); generateSOAP12Headers(output, headerParams, responseMessage); } if (isRpc) { body.namespace(method.getRequestParameters().iterator().next().getName().getNamespaceURI()); } } for (CheckedExceptionImpl exception : method.getCheckedExceptions()) { Fault fault = operation.fault().name(exception.getMessageName()); extension.addBindingOperationFaultExtension(fault, method, exception); com.sun.xml.internal.ws.wsdl.writer.document.soap12.SOAPFault soapFault = fault._element(com.sun.xml.internal.ws.wsdl.writer.document.soap12.SOAPFault.class).name(exception.getMessageName()); soapFault.use(LITERAL); } } protected void splitParameters(List<ParameterImpl> bodyParams, List<ParameterImpl> headerParams, List<ParameterImpl> params) { for (ParameterImpl parameter : params) { if (isBodyParameter(parameter)) { bodyParams.add(parameter); } else { headerParams.add(parameter); } } } protected void generateSOAPHeaders(TypedXmlWriter writer, List<ParameterImpl> parameters, QName message) { for (ParameterImpl headerParam : parameters) { Header header = writer._element(Header.class); header.message(message); header.part(headerParam.getPartName()); header.use(LITERAL); } } protected void generateSOAP12Headers(TypedXmlWriter writer, List<ParameterImpl> parameters, QName message) { for (ParameterImpl headerParam : parameters) { com.sun.xml.internal.ws.wsdl.writer.document.soap12.Header header = writer._element(com.sun.xml.internal.ws.wsdl.writer.document.soap12.Header.class); header.message(message); header.part(headerParam.getPartName()); header.use(LITERAL); } } /** * Generates the Service section of the WSDL */ protected void generateService() { QName portQName = model.getPortName(); QName serviceQName = model.getServiceQName(); Service service = serviceDefinitions.service().name(serviceQName.getLocalPart()); extension.addServiceExtension(service); Port port = service.port().name(portQName.getLocalPart()); port.binding(model.getBoundPortTypeName()); extension.addPortExtension(port); if (model.getJavaMethods().isEmpty()) return; if (this.binding.getBindingId().getSOAPVersion() == SOAPVersion.SOAP_12) { com.sun.xml.internal.ws.wsdl.writer.document.soap12.SOAPAddress address = port._element(com.sun.xml.internal.ws.wsdl.writer.document.soap12.SOAPAddress.class); address.location(endpointAddress); } else { SOAPAddress address = port._element(SOAPAddress.class); address.location(endpointAddress); } } protected void generateInputMessage(Operation operation, JavaMethodImpl method) { ParamType paramType = operation.input(); extension.addOperationInputExtension(paramType, method); // paramType.message(method.getOperation().getName()); paramType.message(new QName(model.getTargetNamespace(), method.getRequestMessageName())); } protected void generateOutputMessage(Operation operation, JavaMethodImpl method) { ParamType paramType = operation.output(); extension.addOperationOutputExtension(paramType, method); // paramType.message(new QName(model.getTargetNamespace(), method.getOperation().getLocalName()+RESPONSE)); paramType.message(new QName(model.getTargetNamespace(), method.getResponseMessageName())); } /** * Creates the {@link Result} object used by JAXB to generate a schema for the * namesapceUri namespace. * @param namespaceUri The namespace for the schema being generated * @param suggestedFileName the JAXB suggested file name for the schema file * @return the {@link Result} for JAXB to generate the schema into * @throws java.io.IOException thrown if on IO error occurs */ public Result createOutputFile(String namespaceUri, String suggestedFileName) throws IOException { Result result; if (namespaceUri == null) { return null; } Holder<String> fileNameHolder = new Holder<String>(); fileNameHolder.value = schemaPrefix + suggestedFileName; result = wsdlResolver.getSchemaOutput(namespaceUri, fileNameHolder); // System.out.println("schema file: "+fileNameHolder.value); // System.out.println("result: "+result); String schemaLoc; if (result == null) schemaLoc = fileNameHolder.value; else schemaLoc = relativize(result.getSystemId(), wsdlLocation); boolean isEmptyNs = namespaceUri.trim().equals(""); if (!isEmptyNs) { com.sun.xml.internal.ws.wsdl.writer.document.xsd.Import _import = types.schema()._import(); _import.namespace(namespaceUri); _import.schemaLocation(schemaLoc); } return result; } private Result createInlineSchema(String namespaceUri, String suggestedFileName) throws IOException { Result result; if (namespaceUri.equals("")) { return null; } // Holder<String> fileNameHolder = new Holder<String>(); // fileNameHolder.value = schemaPrefix+suggestedFileName; // result = wsdlResolver.getSchemaOutput(namespaceUri, fileNameHolder); // if (result == null) { // // JAXB doesn't have to generate it, a schema is already available // com.sun.xml.internal.ws.wsdl.writer.document.xsd.Import _import = types.schema()._import().namespace(namespaceUri); // _import.schemaLocation(fileNameHolder.value); // } else { // Let JAXB write the schema directly into wsdl's TypedXmlWriter result = new TXWResult(types); result.setSystemId(""); // } return result; } /** * Relativizes a URI by using another URI (base URI.) * * <p> * For example, {@code relative("http://www.sun.com/abc/def","http://www.sun.com/pqr/stu") => "../abc/def"} * * <p> * This method only works on hierarchical URI's, not opaque URI's (refer to the * <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/net/URI.html">java.net.URI</a> * javadoc for complete definitions of these terms. * * <p> * This method will not normalize the relative URI. * @param uri the URI to relativize * * * @param baseUri the base URI to use for the relativization * @return the relative URI or the original URI if a relative one could not be computed */ protected static String relativize(String uri, String baseUri) { try { assert uri != null; if (baseUri == null) return uri; URI theUri = new URI(escapeURI(uri)); URI theBaseUri = new URI(escapeURI(baseUri)); if (theUri.isOpaque() || theBaseUri.isOpaque()) return uri; if (!equalsIgnoreCase(theUri.getScheme(), theBaseUri.getScheme()) || !equal(theUri.getAuthority(), theBaseUri.getAuthority())) return uri; String uriPath = theUri.getPath(); String basePath = theBaseUri.getPath(); // normalize base path if (!basePath.endsWith("/")) { basePath = normalizeUriPath(basePath); } if (uriPath.equals(basePath)) return "."; String relPath = calculateRelativePath(uriPath, basePath); if (relPath == null) return uri; // recursion found no commonality in the two uris at all StringBuilder relUri = new StringBuilder(); relUri.append(relPath); if (theUri.getQuery() != null) relUri.append('?').append(theUri.getQuery()); if (theUri.getFragment() != null) relUri.append('#').append(theUri.getFragment()); return relUri.toString(); } catch (URISyntaxException e) { throw new InternalError("Error escaping one of these uris:\n\t" + uri + "\n\t" + baseUri); } } private static String calculateRelativePath(String uri, String base) { if (base == null) { return null; } if (uri.startsWith(base)) { return uri.substring(base.length()); } else { return "../" + calculateRelativePath(uri, getParentUriPath(base)); } } /** * Implements the SchemaOutputResolver used by JAXB to */ protected class JAXWSOutputSchemaResolver extends SchemaOutputResolver { ArrayList<DOMResult> nonGlassfishSchemas = null; /** * Creates the {@link Result} object used by JAXB to generate a schema for the * namesapceUri namespace. * @param namespaceUri The namespace for the schema being generated * @param suggestedFileName the JAXB suggested file name for the schema file * @return the {@link Result} for JAXB to generate the schema into * @throws java.io.IOException thrown if on IO error occurs */ @Override public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException { return inlineSchemas ? ((nonGlassfishSchemas != null) ? nonGlassfishSchemaResult(namespaceUri, suggestedFileName) : createInlineSchema(namespaceUri, suggestedFileName)) // ? createInlineSchema(namespaceUri, suggestedFileName) : createOutputFile(namespaceUri, suggestedFileName); } private Result nonGlassfishSchemaResult(String namespaceUri, String suggestedFileName) throws IOException { DOMResult result = new DOMResult(); result.setSystemId(""); nonGlassfishSchemas.add(result); return result; } } private void register(WSDLGeneratorExtension h) { extensionHandlers.add(h); } }