| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.tuscany.sca.binding.ws.wsdlgen; |
| |
| //FIXME: trim the import list down to what's really needed |
| |
| |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| import javax.wsdl.Binding; |
| import javax.wsdl.Definition; |
| import javax.wsdl.Import; |
| import javax.wsdl.Port; |
| import javax.wsdl.PortType; |
| import javax.wsdl.Service; |
| import javax.wsdl.WSDLException; |
| import javax.wsdl.extensions.ExtensibilityElement; |
| import javax.wsdl.extensions.soap.SOAPAddress; |
| import javax.wsdl.extensions.soap.SOAPBinding; |
| import javax.wsdl.extensions.soap12.SOAP12Address; |
| import javax.wsdl.extensions.soap12.SOAP12Binding; |
| import javax.wsdl.factory.WSDLFactory; |
| import javax.wsdl.xml.WSDLWriter; |
| import javax.xml.namespace.QName; |
| |
| import org.apache.tuscany.sca.assembly.AbstractContract; |
| import org.apache.tuscany.sca.assembly.Component; |
| import org.apache.tuscany.sca.assembly.CompositeService; |
| import org.apache.tuscany.sca.binding.ws.WebServiceBinding; |
| import org.apache.tuscany.sca.interfacedef.Interface; |
| import org.apache.tuscany.sca.interfacedef.wsdl.WSDLDefinition; |
| import org.apache.tuscany.sca.interfacedef.wsdl.WSDLInterface; |
| import org.apache.tuscany.sca.monitor.Monitor; |
| import org.apache.tuscany.sca.monitor.Problem; |
| import org.apache.tuscany.sca.monitor.Problem.Severity; |
| import org.apache.tuscany.sca.monitor.impl.ProblemImpl; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.w3c.dom.Text; |
| |
| /** |
| * WSDLServiceGenerator generates a binding WSDL service document. |
| * |
| * @version $Rev$ $Date$ |
| */ |
| public class WSDLServiceGenerator { |
| // the following switch is temporary for debugging |
| public static boolean printWSDL; // external code sets this to print generated WSDL |
| |
| private static final Logger logger = Logger.getLogger(WSDLServiceGenerator.class.getName()); |
| private static final QName TRANSPORT_JMS_QUALIFIED_INTENT = |
| new QName("http://www.osoa.org/xmlns/sca/1.0", "transport.jms"); |
| private static final String DEFAULT_QUEUE_CONNECTION_FACTORY = "TuscanyQueueConnectionFactory"; |
| private static final String ADDRESS = "Address"; |
| |
| private WSDLServiceGenerator() { |
| // this class has static methods only and cannot be instantiated |
| } |
| |
| /** |
| * Log a warning message. |
| * @param problem |
| */ |
| private static void logWarning(Problem problem) { |
| Logger problemLogger = Logger.getLogger(problem.getSourceClassName(), problem.getBundleName()); |
| if (problemLogger != null){ |
| problemLogger.logp(Level.WARNING, problem.getSourceClassName(), null, problem.getMessageId(), problem.getMessageParams()); |
| } else { |
| logger.severe("Can't get logger " + problem.getSourceClassName()+ " with bundle " + problem.getBundleName()); |
| } |
| } |
| |
| /** |
| * Report a warning. |
| * @param message |
| * @param binding |
| * @param parameters |
| */ |
| private static void warning(Monitor monitor, String message, WebServiceBinding wsBinding, String... messageParameters) { |
| Problem problem = new ProblemImpl(WSDLServiceGenerator.class.getName(), "wsdlgen-validation-messages", Severity.WARNING, wsBinding, message, (Object[])messageParameters); |
| if (monitor != null) { |
| monitor.problem(problem); |
| } else { |
| logWarning(problem); |
| } |
| } |
| |
| /** |
| * Report an error. |
| * @param message |
| * @param binding |
| * @param parameters |
| */ |
| private static void error(Monitor monitor, String message, WebServiceBinding wsBinding, String... messageParameters) { |
| Problem problem = new ProblemImpl(WSDLServiceGenerator.class.getName(), "wsdlgen-validation-messages", Severity.ERROR, wsBinding, message, (Object[])messageParameters); |
| if (monitor != null) { |
| monitor.problem(problem); |
| } else { |
| throw new WSDLGenerationException(problem.toString(), null, problem); |
| } |
| } |
| |
| /** |
| * Generate a suitably configured WSDL definition |
| */ |
| protected static Definition configureWSDLDefinition(WebServiceBinding wsBinding, |
| Component component, |
| AbstractContract contract, |
| Monitor monitor) { |
| |
| //[nash] changes to the builder sequence avoid calling this for a CompositeService |
| assert !(contract instanceof CompositeService); |
| /* |
| // For every promoted composite service, the underlying component |
| // gets a copy of the service with the name prefixed by "$promoted$." |
| String contractName = (contract instanceof CompositeService ? "$promoted$." : "") + contract.getName(); |
| */ |
| String contractName = contract.getName(); |
| |
| List<Port> ports = new ArrayList<Port>(); |
| WSDLDefinition wsdlDefinition = wsBinding.getWSDLDefinition(); |
| if (wsdlDefinition == null) { |
| error(monitor, "NoWsdlInterface", wsBinding, component.getName(), contract.getName()); |
| return null; |
| } |
| Definition def = wsdlDefinition.getDefinition(); |
| |
| boolean wsdlProvidedByUser = (wsdlDefinition.getBinding() == null); |
| |
| if (wsdlProvidedByUser) { |
| // The WSDL document was provided by the user. Generate a new |
| // WSDL document with imports from the user-provided document. |
| WSDLFactory factory = null; |
| try { |
| factory = WSDLFactory.newInstance(); |
| } catch (WSDLException e) { |
| throw new WSDLGenerationException(e); |
| } |
| Definition newDef = factory.newDefinition(); |
| |
| // Construct a target namespace from the base URI of the user's |
| // WSDL document (is this what we should be using?) and a path |
| // computed according to the SCA Web Service binding spec. |
| String nsName = component.getName() + "/" + contractName; |
| String namespaceURI = null; |
| try { |
| URI userTNS = new URI(def.getTargetNamespace()); |
| namespaceURI = userTNS.resolve("/" + nsName).toString(); |
| } catch (URISyntaxException e1) { |
| throw new WSDLGenerationException(e1); |
| } catch (IllegalArgumentException e2) { |
| throw new WSDLGenerationException(e2); |
| } |
| |
| // set name and targetNamespace attributes on the definition |
| String defsName = component.getName() + "." + contractName; |
| newDef.setQName(new QName(namespaceURI, defsName)); |
| newDef.setTargetNamespace(namespaceURI); |
| newDef.addNamespace("tns", namespaceURI); |
| |
| // set wsdl namespace prefix on the definition |
| newDef.addNamespace("wsdl", "http://schemas.xmlsoap.org/wsdl/"); |
| |
| // import the service or reference interface portType |
| List<WSDLDefinition> imports = new ArrayList<WSDLDefinition>(); |
| Interface interfaze = wsBinding.getBindingInterfaceContract().getInterface(); |
| if (interfaze instanceof WSDLInterface) { |
| PortType portType = ((WSDLInterface)interfaze).getPortType(); |
| boolean ok = importPortType(portType, wsdlDefinition, newDef, imports); |
| if (!ok) { |
| error(monitor, "PortTypeNotFound", wsBinding, portType.getQName().toString(), |
| component.getName(), contract.getName()); |
| } |
| } |
| |
| // import an existing binding if specified |
| Binding binding = wsBinding.getBinding(); |
| if (binding != null) { |
| boolean ok = importBinding(binding, wsdlDefinition, newDef, imports); |
| if (ok) { |
| boolean ok2 = importPortType(binding.getPortType(), wsdlDefinition, newDef, imports); |
| if (!ok2) { |
| error(monitor, "PortTypeNotFound", wsBinding, binding.getPortType().getQName().toString(), |
| component.getName(), contract.getName()); |
| } |
| } else { |
| error(monitor, "BindingNotFound", wsBinding, binding.getQName().toString(), |
| component.getName(), contract.getName()); |
| } |
| } |
| |
| // import bindings and portTypes needed by services and ports |
| QName serviceQName = wsBinding.getServiceName(); |
| String portName = wsBinding.getPortName(); |
| if (serviceQName != null) { |
| Service service = def.getService(serviceQName); |
| if (portName != null) { |
| Port port = service.getPort(portName); |
| Port newPort = copyPort(newDef, port, wsBinding); |
| if (newPort != null) { |
| importBinding(port.getBinding(), wsdlDefinition, newDef, imports); |
| ports.add(newPort); |
| } else { |
| error(monitor, "InvalidPort", wsBinding, serviceQName.toString(), portName, |
| component.getName(), contract.getName()); |
| } |
| } else { |
| for (Object port : service.getPorts().values()) { |
| Port newPort = copyPort(newDef, (Port)port, wsBinding); |
| if (newPort != null) { |
| importBinding(((Port)port).getBinding(), wsdlDefinition, newDef, imports); |
| ports.add(newPort); |
| } else { |
| // not an error, just ignore the port |
| warning(monitor, "IgnoringPort", wsBinding, serviceQName.toString(), ((Port)port).getName(), |
| component.getName(), contract.getName()); |
| } |
| } |
| if (ports.size() == 0) { |
| error(monitor, "NoValidPorts", wsBinding, serviceQName.toString(), |
| component.getName(), contract.getName()); |
| } |
| } |
| } |
| |
| // replace original WSDL definition by the generated definition |
| def = newDef; |
| |
| } else { |
| // The WSDL definition was generated by Interface2WSDLGenerator. |
| // Reuse it instead of creating a new definition here. |
| } |
| |
| // add a service and ports to the generated definition |
| WSDLDefinitionGenerator helper = |
| new WSDLDefinitionGenerator(BindingWSDLGenerator.requiresSOAP12(wsBinding),false); |
| WSDLInterface wi = (WSDLInterface)wsBinding.getBindingInterfaceContract().getInterface(); |
| PortType portType = wi.getPortType(); |
| Service service = helper.createService(def, portType); |
| if (wsBinding.getBinding() == null && ports.size() == 0) { |
| Binding binding = helper.createBinding(def, portType); |
| if (BindingWSDLGenerator.requiresSOAP12(wsBinding)) { |
| def.addNamespace("SOAP12", "http://schemas.xmlsoap.org/wsdl/soap12/"); |
| } else { |
| def.addNamespace("SOAP11", "http://schemas.xmlsoap.org/wsdl/soap/"); |
| } |
| helper.createBindingOperations(def, binding, portType); |
| binding.setUndefined(false); |
| def.addBinding(binding); |
| |
| String endpointURI = computeActualURI(wsBinding, null); |
| Port port = helper.createPort(def, binding, service, endpointURI); |
| wsBinding.setService(service); |
| wsBinding.setPort(port); |
| } else { |
| if (ports.size() > 0) { |
| // there are one or more user-specified valid ports |
| for (Port port : ports) { |
| service.addPort(port); |
| } |
| if (ports.size() == 1) { |
| // only one port, so use it |
| wsBinding.setPort(ports.get(0)); |
| } else { |
| // multiple ports, make them all available |
| wsBinding.setPort(null); |
| } |
| } else { |
| // no valid user-specified ports, so create a suitably configured port |
| String endpointURI = computeActualURI(wsBinding, null); |
| Port port = helper.createPort(def, wsBinding.getBinding(), service, endpointURI); |
| if (BindingWSDLGenerator.requiresSOAP12(wsBinding)) { |
| def.addNamespace("SOAP12", "http://schemas.xmlsoap.org/wsdl/soap12/"); |
| } else { |
| def.addNamespace("SOAP11", "http://schemas.xmlsoap.org/wsdl/soap/"); |
| } |
| wsBinding.setPort(port); |
| } |
| wsBinding.setService(service); |
| } |
| |
| // TUSCANY-2900 - add jms binding and service port if required |
| // TODO - remove service/ports from any imported WSDL |
| // - find away to allow users to retrieve WSDL with JMS bindings |
| // as a jms binding on it's own provides not target for ?wsdl |
| if ((!wsdlProvidedByUser) && |
| (wsBinding.getURI() != null) && |
| (wsBinding.getURI().startsWith("jms"))){ |
| |
| // need to work out how to check if user has already specified a binding |
| |
| // create jms binding |
| helper = new WSDLDefinitionGenerator(BindingWSDLGenerator.requiresSOAP12(wsBinding),true); |
| Binding binding = helper.createBinding(def, portType); |
| helper.createBindingOperations(def, binding, portType); |
| binding.setUndefined(false); |
| def.addBinding(binding); |
| |
| // create a jms port |
| String endpointURI = computeActualURI(wsBinding, null); |
| Port port = helper.createPort(def, binding, service, endpointURI); |
| wsBinding.setService(service); |
| wsBinding.setPort(port); |
| |
| //printWSDL = true; |
| } |
| |
| // for debugging |
| if (printWSDL) { |
| try { |
| System.out.println("Generated WSDL for " + component.getName() + "/" + contractName); |
| WSDLWriter writer = javax.wsdl.factory.WSDLFactory.newInstance().newWSDLWriter(); |
| writer.writeWSDL(def, System.out); |
| } catch (WSDLException e) { |
| throw new WSDLGenerationException(e); |
| } |
| } |
| |
| return def; |
| } |
| |
| private static boolean importPortType(PortType portType, |
| WSDLDefinition wsdlDef, |
| Definition newDef, |
| List<WSDLDefinition> imports) { |
| return addImport(portType.getQName(), PortType.class, wsdlDef, newDef, imports); |
| } |
| |
| private static boolean importBinding(Binding binding, |
| WSDLDefinition wsdlDef, |
| Definition newDef, |
| List<WSDLDefinition> imports) { |
| boolean ok = addImport(binding.getQName(), Binding.class, wsdlDef, newDef, imports); |
| if (ok) { |
| List bindingExtensions = binding.getExtensibilityElements(); |
| for (final Object extension : bindingExtensions) { |
| if (extension instanceof SOAPBinding) { |
| newDef.addNamespace("SOAP11", "http://schemas.xmlsoap.org/wsdl/soap/"); |
| } |
| if (extension instanceof SOAP12Binding) { |
| newDef.addNamespace("SOAP12", "http://schemas.xmlsoap.org/wsdl/soap12/"); |
| } |
| } |
| } |
| return ok; |
| } |
| |
| private static boolean addImport(QName name, |
| Class type, |
| WSDLDefinition wsdlDef, |
| Definition newDef, |
| List<WSDLDefinition> imports) { |
| String namespace = name.getNamespaceURI(); |
| if (newDef.getImports(namespace) == null) { |
| WSDLDefinition impDef = findDefinition(wsdlDef, name, type); |
| if (impDef != null) { |
| Import imp = newDef.createImport(); |
| imp.setNamespaceURI(namespace); |
| imp.setLocationURI(impDef.getURI().toString()); |
| imp.setDefinition(impDef.getDefinition()); |
| newDef.addNamespace("ns" + imports.size(), namespace); |
| newDef.addImport(imp); |
| imports.add(impDef); |
| return true; |
| } else { |
| // import was not added because element not found |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private static WSDLDefinition findDefinition(WSDLDefinition wsdlDef, QName name, Class type) { |
| if (wsdlDef == null || name == null) { |
| return wsdlDef; |
| } |
| if (wsdlDef.getURI() != null) { // not a facade |
| Definition def = wsdlDef.getDefinition(); |
| Map types = type == PortType.class ? def.getPortTypes() : def.getBindings(); |
| if (types.get(name) != null) { |
| return wsdlDef; |
| } |
| } |
| for (WSDLDefinition impDef : wsdlDef.getImportedDefinitions()) { |
| WSDLDefinition d = findDefinition(impDef, name, type); |
| if (d != null) { |
| return d; |
| } |
| } |
| return null; |
| } |
| |
| private static Port copyPort(Definition def, Port port, WebServiceBinding wsBinding) { |
| Port newPort = def.createPort(); |
| newPort.setName(port.getName()); |
| newPort.setBinding(port.getBinding()); |
| List portExtensions = port.getExtensibilityElements(); |
| for (final Object extension : portExtensions) { |
| ExtensibilityElement newExt = null; |
| if (extension instanceof SOAPAddress) { |
| def.addNamespace("SOAP11", "http://schemas.xmlsoap.org/wsdl/soap/"); |
| try { |
| newExt = def.getExtensionRegistry().createExtension( |
| Port.class, WSDLDefinitionGenerator.SOAP_ADDRESS); |
| } catch (WSDLException e) { |
| } |
| String uri = computeActualURI(wsBinding, port); |
| ((SOAPAddress)newExt).setLocationURI(uri); |
| newPort.addExtensibilityElement(newExt); |
| } else if (extension instanceof SOAP12Address) { |
| def.addNamespace("SOAP12", "http://schemas.xmlsoap.org/wsdl/soap12/"); |
| try { |
| newExt = def.getExtensionRegistry().createExtension( |
| Port.class, WSDLDefinitionGenerator.SOAP12_ADDRESS); |
| } catch (WSDLException e) { |
| } |
| String uri = computeActualURI(wsBinding, port); |
| ((SOAP12Address)newExt).setLocationURI(uri); |
| newPort.addExtensibilityElement(newExt); |
| } else { |
| // we don't support ports with other extensibility elements such as HTTPAddress |
| return null; |
| } |
| } |
| return newPort; |
| } |
| |
| /** |
| * Compute the endpoint URI based on section 2.1.1 of the WS binding Specification 1. |
| * The URIs in the endpoint(s) of the referenced WSDL, which may be relative |
| * 2. The URI specified by the wsa:Address element of the |
| * wsa:EndpointReference, which may be relative 3. The explicitly stated URI |
| * in the "uri" attribute of the binding.ws element, which may be relative, |
| * 4. The implicit URI as defined by in section 1.7 in the SCA Assembly Specification |
| * If the <binding.ws> has no wsdlElement but does have a uri attribute then |
| * the uri takes precedence over any implicitly used WSDL. |
| * |
| */ |
| private static String computeActualURI(WebServiceBinding wsBinding, Port port) { |
| |
| URI eprURI = null; |
| if (wsBinding.getEndPointReference() != null) { |
| eprURI = getEPR(wsBinding); |
| } |
| |
| URI wsdlURI = null; |
| if (wsBinding.getServiceName() != null && wsBinding.getBindingName() == null) { |
| // <binding.ws> explicitly points at a WSDL port, may be a relative URI |
| wsdlURI = getEndpoint(port); |
| } |
| |
| // if the WSDL port/endpoint has an absolute URI use that |
| if (wsdlURI != null && wsdlURI.isAbsolute()) { |
| return wsdlURI.toString(); |
| } |
| |
| // if the wsa:EndpointReference has an address element with an absolute URI use that |
| if (eprURI != null && eprURI.isAbsolute()) { |
| return eprURI.toString(); |
| } |
| |
| // either there is no WSDL port endpoint URI or that URI is relative |
| String actualURI = wsBinding.getURI(); |
| if (eprURI != null && eprURI.toString().length() != 0) { |
| // there is a relative URI in the binding EPR |
| actualURI = actualURI + "/" + eprURI; |
| } |
| |
| if (wsdlURI != null && wsdlURI.toString().length() != 0) { |
| // there is a relative URI in the WSDL port |
| actualURI = actualURI + "/" + wsdlURI; |
| } |
| |
| if (actualURI != null) { |
| actualURI = URI.create(actualURI).normalize().toString(); |
| } |
| |
| return actualURI; |
| } |
| |
| private static URI getEPR(WebServiceBinding wsBinding) { |
| NodeList nodeList = wsBinding.getEndPointReference().getChildNodes(); |
| for (int i = 0; i < nodeList.getLength(); i++) { |
| Node childNode = nodeList.item(i); |
| if (childNode instanceof Element && ADDRESS.equals(childNode.getLocalName())) { |
| NodeList addrNodes = childNode.getChildNodes(); |
| for (int j = 0; j < addrNodes.getLength(); j++) { |
| Node addrNode = addrNodes.item(j); |
| if (addrNode instanceof Text) { |
| return URI.create(((Text)addrNode).getWholeText()); |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the endpoint of a given port. |
| */ |
| private static URI getEndpoint(Port wsdlPort) { |
| if (wsdlPort != null) { |
| List<?> wsdlPortExtensions = wsdlPort.getExtensibilityElements(); |
| for (Object extension : wsdlPortExtensions) { |
| if (extension instanceof SOAPAddress) { |
| String uri = ((SOAPAddress)extension).getLocationURI(); |
| return (uri == null || "".equals(uri)) ? null : URI.create(uri); |
| } |
| if (extension instanceof SOAP12Address) { |
| SOAP12Address address = (SOAP12Address)extension; |
| String uri = address.getLocationURI(); |
| return (uri == null || "".equals(uri)) ? null : URI.create(uri); |
| } |
| } |
| } |
| return null; |
| } |
| |
| } |