/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.tools.dbws; //javase imports import java.io.OutputStream; //Java extension imports 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.Input; import javax.wsdl.Message; import javax.wsdl.Output; import javax.wsdl.Part; import javax.wsdl.Port; import javax.wsdl.PortType; import javax.wsdl.Service; import javax.wsdl.Types; import javax.wsdl.WSDLException; 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.SOAPOperation; import javax.wsdl.extensions.soap12.SOAP12Address; import javax.wsdl.extensions.soap12.SOAP12Binding; import javax.wsdl.extensions.soap12.SOAP12Body; import javax.wsdl.extensions.soap12.SOAP12Fault; import javax.wsdl.extensions.soap12.SOAP12Operation; import javax.wsdl.factory.WSDLFactory; import javax.wsdl.xml.WSDLWriter; import javax.xml.namespace.QName; import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; //import static //EclipseLink imports import org.eclipse.persistence.internal.oxm.schema.SchemaModelProject; import org.eclipse.persistence.internal.oxm.schema.model.Any; import org.eclipse.persistence.internal.oxm.schema.model.ComplexType; import org.eclipse.persistence.internal.oxm.schema.model.Element; import org.eclipse.persistence.internal.oxm.schema.model.Import; import org.eclipse.persistence.internal.oxm.schema.model.Schema; import org.eclipse.persistence.internal.oxm.schema.model.Sequence; import org.eclipse.persistence.internal.xr.Operation; import org.eclipse.persistence.internal.xr.Parameter; import org.eclipse.persistence.internal.xr.QueryOperation; import org.eclipse.persistence.internal.xr.XRServiceModel; import org.eclipse.persistence.oxm.NamespaceResolver; import org.eclipse.persistence.oxm.XMLContext; import org.eclipse.persistence.oxm.XMLDescriptor; import org.eclipse.persistence.oxm.XMLMarshaller; import static org.eclipse.persistence.internal.xr.Util.DBWS_SCHEMA_XML; import static org.eclipse.persistence.internal.xr.Util.TARGET_NAMESPACE_PREFIX; import static org.eclipse.persistence.internal.xr.Util.SERVICE_SUFFIX; import static org.eclipse.persistence.tools.dbws.Util.THE_INSTANCE_NAME; import static org.eclipse.persistence.tools.dbws.Util.WSI_SWAREF; import static org.eclipse.persistence.tools.dbws.Util.WSI_SWAREF_PREFIX; import static org.eclipse.persistence.tools.dbws.Util.WSI_SWAREF_URI; import static org.eclipse.persistence.tools.dbws.Util.WSI_SWAREF_XSD_FILE; public class WSDLGenerator { public static final String BINDING_SUFFIX = "_SOAP_HTTP"; public static final String EMPTY_RESPONSE = "EmptyResponse"; public static final String EXCEPTION_SUFFIX = "Exception"; public static final String FAULT_SUFFIX = "Fault"; public static final String NS_SCHEMA_PREFIX = "xsd"; public static final String NS_TNS_PREFIX = "tns"; public static final String SOAP_11_NAMESPACE_URI = "http://schemas.xmlsoap.org/wsdl/soap/"; public static final String SOAP_11_NAMESPACE_PREFIX = "soap"; public static final String SOAP_12_NAMESPACE_URI = "http://schemas.xmlsoap.org/wsdl/soap12/"; public static final String SOAP_12_NAMESPACE_PREFIX = "soap12"; public static final String PORT_SUFFIX = "_Interface"; public static final String REQUEST_SUFFIX = "Request"; public static final String RESPONSE_SUFFIX = "Response"; public static final String SOAP_STYLE = "document"; public static final String SOAP_HTTP_TRANSPORT = "http://schemas.xmlsoap.org/soap/http"; public static final String SOAP_USE = "literal"; public static final String TAG_SCHEMA = "schema"; public static final String TAG_SOAP_ADDRESS = "address"; public static final String TAG_SOAP_BINDING = "binding"; public static final String TAG_SOAP_BODY = "body"; public static final String TAG_SOAP_FAULT = "fault"; public static final String TAG_SOAP_OPERATION = "operation"; public static final String TYPE_SUFFIX = "Type"; protected XRServiceModel xrServiceModel; protected NamingConventionTransformer nct; protected String serviceName; protected String serviceNameSpace; protected String importedSchemaNameSpace; protected String wsdlLocationURI; protected boolean hasAttachments; protected OutputStream os; public WSDLGenerator(XRServiceModel xrServiceModel, NamingConventionTransformer nct, String wsdlLocationURI, boolean hasAttachments, String importedSchemaNameSpace, OutputStream os) { this.xrServiceModel = xrServiceModel; this.nct = nct; this.wsdlLocationURI = wsdlLocationURI; this.hasAttachments = hasAttachments; this.os = os; this.importedSchemaNameSpace = importedSchemaNameSpace; serviceName = xrServiceModel.getName() + SERVICE_SUFFIX; serviceNameSpace = importedSchemaNameSpace + SERVICE_SUFFIX; } public Definition generateWSDL() throws WSDLException { return generateWSDL(false); } public Definition generateWSDL(boolean useSOAP12) throws WSDLException { WSDLFactory factory = WSDLFactory.newInstance(); ExtensionRegistry registry = factory.newPopulatedExtensionRegistry(); Definition def = factory.newDefinition(); if (useSOAP12) { def.addNamespace(SOAP_12_NAMESPACE_PREFIX, SOAP_12_NAMESPACE_URI); } else { def.addNamespace(SOAP_11_NAMESPACE_PREFIX, SOAP_11_NAMESPACE_URI); } def.setTargetNamespace(serviceNameSpace); def.setQName(new QName(serviceNameSpace, serviceName)); def.addNamespace(NS_TNS_PREFIX, serviceNameSpace); def.addNamespace(TARGET_NAMESPACE_PREFIX, importedSchemaNameSpace); def.addNamespace(NS_SCHEMA_PREFIX, W3C_XML_SCHEMA_NS_URI); Types types = def.createTypes(); javax.wsdl.extensions.schema.Schema schema = (javax.wsdl.extensions.schema.Schema)registry.createExtension(Types.class, new QName( W3C_XML_SCHEMA_NS_URI, TAG_SCHEMA)); schema.setElement(createInlineSchema()); types.addExtensibilityElement(schema); def.setTypes(types); PortType portType = def.createPortType(); portType.setUndefined(false); portType.setQName(new QName(serviceNameSpace, serviceName + PORT_SUFFIX)); def.addPortType(portType); ExtensibilityElement soap = null; if (useSOAP12) { soap = registry.createExtension(Binding.class, new QName( SOAP_12_NAMESPACE_URI, TAG_SOAP_BINDING)); ((SOAP12Binding)soap).setStyle(SOAP_STYLE); ((SOAP12Binding)soap).setTransportURI(SOAP_HTTP_TRANSPORT); } else { soap = registry.createExtension(Binding.class, new QName(SOAP_11_NAMESPACE_URI, TAG_SOAP_BINDING)); ((SOAPBinding)soap).setStyle(SOAP_STYLE); ((SOAPBinding)soap).setTransportURI(SOAP_HTTP_TRANSPORT); } Binding binding = def.createBinding(); binding.setUndefined(false); binding.setQName(new QName(serviceNameSpace, serviceName + BINDING_SUFFIX)); binding.setPortType(portType); binding.addExtensibilityElement(soap); def.addBinding(binding); ExtensibilityElement sa = null; if (useSOAP12) { sa = registry.createExtension(Port.class, new QName(SOAP_12_NAMESPACE_URI, TAG_SOAP_ADDRESS)); ((SOAP12Address)sa).setLocationURI(wsdlLocationURI); } else { sa = registry.createExtension(Port.class, new QName(SOAP_11_NAMESPACE_URI, TAG_SOAP_ADDRESS)); ((SOAPAddress)sa).setLocationURI(wsdlLocationURI); } Service ws = def.createService(); ws.setQName(new QName(serviceNameSpace, serviceName)); Port port = def.createPort(); port.setName(serviceName + "Port"); port.setBinding(binding); port.addExtensibilityElement(sa); ws.addPort(port); def.addService(ws); boolean requireEmptyResponseMessages = false; for (Operation op : xrServiceModel.getOperationsList()) { if (!(op instanceof QueryOperation)) { requireEmptyResponseMessages = true; break; } } if (requireEmptyResponseMessages) { /* * Add in empty response message * <wsdl:message name="EmptyResponse"> * <wsdl:part name="emptyResponse" element="tns:EmptyResponse"></wsdl:part> * </wsdl:message> */ Message emptyResponseMessage = def.createMessage(); emptyResponseMessage.setUndefined(false); emptyResponseMessage.setQName(new QName(serviceNameSpace, EMPTY_RESPONSE)); Part responsePart = def.createPart(); responsePart.setName("emptyResponse"); responsePart.setElementName(new QName(serviceNameSpace, EMPTY_RESPONSE)); emptyResponseMessage.addPart(responsePart); def.addMessage(emptyResponseMessage); /* * Add in Fault message * <wsdl:message name="FaultType"> * <wsdl:part name="fault" element="tns:FaultType"></wsdl:part> * </wsdl:message> */ Message faultMessage = def.createMessage(); faultMessage.setUndefined(false); faultMessage.setQName(new QName(serviceNameSpace, FAULT_SUFFIX + TYPE_SUFFIX)); Part faultPart = def.createPart(); faultPart.setName("fault"); faultPart.setElementName(new QName(serviceNameSpace, FAULT_SUFFIX + TYPE_SUFFIX)); faultMessage.addPart(faultPart); def.addMessage(faultMessage); } for (Operation op : xrServiceModel.getOperationsList()) { createMethodDefinition(factory, registry, def, op, useSOAP12); } if (os != null) { WSDLWriter writer = factory.newWSDLWriter(); writer.writeWSDL(def, os); } return def; } private void createMethodDefinition(WSDLFactory factory, ExtensionRegistry registry, Definition def, Operation operation, boolean useSOAP12) throws WSDLException { Message requestMessage = def.createMessage(); requestMessage.setUndefined(false); requestMessage.setQName(new QName(serviceNameSpace, operation.getName() + REQUEST_SUFFIX)); Part requestPart = def.createPart(); requestPart.setName(operation.getName() + REQUEST_SUFFIX); requestPart.setElementName(new QName(serviceNameSpace, operation.getName())); requestMessage.addPart(requestPart); def.addMessage(requestMessage); Message responseMessage = null; if (operation.hasResponse()) { responseMessage = def.createMessage(); responseMessage.setUndefined(false); responseMessage.setQName(new QName(serviceNameSpace, operation.getName() + RESPONSE_SUFFIX)); Part responsePart = def.createPart(); responsePart.setName(operation.getName() + RESPONSE_SUFFIX); responsePart.setElementName(new QName(serviceNameSpace, operation.getName() + RESPONSE_SUFFIX)); responseMessage.addPart(responsePart); def.addMessage(responseMessage); } PortType portType = def.getPortType(new QName(serviceNameSpace, serviceName + PORT_SUFFIX)); javax.wsdl.Operation op = def.createOperation(); op.setUndefined(false); op.setName(operation.getName()); Input input = def.createInput(); input.setMessage(requestMessage); op.setInput(input); if (operation.hasResponse()) { Output output = def.createOutput(); output.setMessage(responseMessage); op.setOutput(output); } portType.addOperation(op); Binding binding = def.getBinding(new QName(serviceNameSpace, serviceName + BINDING_SUFFIX)); BindingOperation bop = def.createBindingOperation(); bop.setName(operation.getName()); ExtensibilityElement so = null; if (useSOAP12) { so = registry.createExtension(BindingOperation.class, new QName( SOAP_12_NAMESPACE_URI, TAG_SOAP_OPERATION)); ((SOAP12Operation)so).setSoapActionURI(serviceNameSpace + ":" + op.getName()); } else { so = registry.createExtension(BindingOperation.class, new QName( SOAP_11_NAMESPACE_URI, TAG_SOAP_OPERATION)); ((SOAPOperation)so).setSoapActionURI(serviceNameSpace + ":" + op.getName()); } bop.addExtensibilityElement(so); BindingInput bi = def.createBindingInput(); ExtensibilityElement soapInputBody = null; if (useSOAP12) { soapInputBody = registry.createExtension(BindingInput.class, new QName( SOAP_12_NAMESPACE_URI, TAG_SOAP_BODY)); ((SOAP12Body)soapInputBody).setUse(SOAP_USE); } else { soapInputBody = registry.createExtension(BindingInput.class, new QName( SOAP_11_NAMESPACE_URI, TAG_SOAP_BODY)); ((SOAPBody)soapInputBody).setUse(SOAP_USE); } bi.addExtensibilityElement(soapInputBody); bop.setBindingInput(bi); if (operation.hasResponse()) { BindingOutput bo = def.createBindingOutput(); ExtensibilityElement soapOutputBody = null; if (useSOAP12) { soapOutputBody = registry.createExtension(BindingOutput.class, new QName( SOAP_12_NAMESPACE_URI, TAG_SOAP_BODY)); ((SOAP12Body)soapOutputBody).setUse(SOAP_USE); } else { soapOutputBody = registry.createExtension(BindingOutput.class, new QName( SOAP_11_NAMESPACE_URI, TAG_SOAP_BODY)); ((SOAPBody)soapOutputBody).setUse(SOAP_USE); } bo.addExtensibilityElement(soapOutputBody); bop.setBindingOutput(bo); } if (!(operation instanceof QueryOperation)) { // non-QueryOperations don't have Responses, but the fault requirements // mean we have to create 'dummy' WSDL outputs BindingOutput bo = def.createBindingOutput(); ExtensibilityElement soapOutputBody = null; if (useSOAP12) { soapOutputBody = registry.createExtension(BindingOutput.class, new QName( SOAP_12_NAMESPACE_URI, TAG_SOAP_BODY)); ((SOAP12Body)soapOutputBody).setUse(SOAP_USE); } else { soapOutputBody = registry.createExtension(BindingOutput.class, new QName( SOAP_11_NAMESPACE_URI, TAG_SOAP_BODY)); ((SOAPBody)soapOutputBody).setUse(SOAP_USE); } bo.addExtensibilityElement(soapOutputBody); bop.setBindingOutput(bo); // add WSDL fault to binding operations BindingFault bindingFault = def.createBindingFault(); String exceptionName = FAULT_SUFFIX + EXCEPTION_SUFFIX; bindingFault.setName(exceptionName); ExtensibilityElement soapFaultBody = null; if (useSOAP12) { soapFaultBody = registry.createExtension(BindingFault.class, new QName( SOAP_12_NAMESPACE_URI, TAG_SOAP_FAULT)); ((SOAP12Fault)soapFaultBody).setUse(SOAP_USE); ((SOAP12Fault)soapFaultBody).setName(exceptionName); } else { soapFaultBody = registry.createExtension(BindingFault.class, new QName( SOAP_11_NAMESPACE_URI, TAG_SOAP_FAULT)); ((SOAPFault)soapFaultBody).setUse(SOAP_USE); ((SOAPFault)soapFaultBody).setName(exceptionName); } bindingFault.addExtensibilityElement(soapFaultBody); bop.addBindingFault(bindingFault); Message emptyResponseMessage = def.getMessage(new QName(serviceNameSpace, EMPTY_RESPONSE)); Output output = def.createOutput(); output.setName(operation.getName() + EMPTY_RESPONSE); output.setMessage(emptyResponseMessage); op.setOutput(output); Message faultMessage = def.getMessage(new QName(serviceNameSpace, FAULT_SUFFIX + TYPE_SUFFIX)); Fault fault = def.createFault(); fault.setMessage(faultMessage); fault.setName(exceptionName); op.addFault(fault); } binding.addBindingOperation(bop); } /** * Build the inline schema that will make up the content of the * <wsdl:types> section of the WSDL. * @return */ @SuppressWarnings("unchecked") private org.w3c.dom.Element createInlineSchema() { SchemaModelProject project = new SchemaModelProject(); XMLContext context = new XMLContext(project); XMLMarshaller marshaller = context.createMarshaller(); XMLDescriptor descriptor = (XMLDescriptor) project.getClassDescriptor(Schema.class); if (descriptor.getNamespaceResolver() == null) { descriptor.setNamespaceResolver(new NamespaceResolver()); } descriptor.getNamespaceResolver().put(NS_TNS_PREFIX, serviceNameSpace); descriptor.getNamespaceResolver().put(NS_SCHEMA_PREFIX, W3C_XML_SCHEMA_NS_URI); if (hasAttachments) { descriptor.getNamespaceResolver().put(WSI_SWAREF_PREFIX, WSI_SWAREF_URI); } Schema schema = new Schema(); schema.setTargetNamespace(serviceNameSpace); schema.setElementFormDefault(true); Import parent = new Import(); parent.setNamespace(importedSchemaNameSpace); parent.setSchemaLocation(DBWS_SCHEMA_XML); schema.getImports().add(parent); if (hasAttachments) { Import ref = new Import(); ref.setNamespace(WSI_SWAREF_URI); // ref.setSchemaLocation("http://ws-i.org/profiles/basic/1.1/swaref.xsd"); // later version ref.setSchemaLocation(WSI_SWAREF_XSD_FILE); schema.getImports().add(ref); } boolean requireFaultTypeEmptyResponse = false; for (Operation op : xrServiceModel.getOperationsList()) { String opName = op.getName(); ComplexType requestType = new ComplexType(); // may need to extract tableNameAlias from operation name - everything after first // underscore '_' character (for cases where a QueryOperation is not user defined) // i.e. String tableNameAlias = opName.substring(opName.indexOf('_') + 1); requestType.setName(opName + REQUEST_SUFFIX + TYPE_SUFFIX); Sequence requestSequence = null; if (op.getParameters().size() > 0) { requestSequence = new Sequence(); for (Parameter p : op.getParameters()) { Element arg = new Element(); if (p.isOptional()) { arg.setMinOccurs("0"); } arg.setName(p.getName()); if (THE_INSTANCE_NAME.equals(p.getName())) { ComplexType nestedComplexType = new ComplexType(); Sequence nestedSequence = new Sequence(); nestedComplexType.setSequence(nestedSequence); Element nestedElement = new Element(); nestedElement.setRef(TARGET_NAMESPACE_PREFIX + ":" + p.getType().getLocalPart()); // here we may need to use 'tableNameAlias' instead, i.e. // nestedElement.setRef(TARGET_NAMESPACE_PREFIX + ":" + tableNameAlias); nestedSequence.addElement(nestedElement); arg.setComplexType(nestedComplexType); } else { arg.setName(p.getName()); if (p.getType().getNamespaceURI().equals(W3C_XML_SCHEMA_NS_URI)) { arg.setType(NS_SCHEMA_PREFIX + ":" + p.getType().getLocalPart()); } else if (p.getType().getNamespaceURI().equals(importedSchemaNameSpace)) { arg.setType(TARGET_NAMESPACE_PREFIX + ":" + p.getType().getLocalPart()); } else { arg.setType(p.getType().getLocalPart()); } } requestSequence.addElement(arg); } requestType.setSequence(requestSequence); } schema.addTopLevelComplexTypes(requestType); Element requestElement = new Element(); requestElement.setName(op.getName()); requestElement.setType(NS_TNS_PREFIX + ":" + requestType.getName()); schema.addTopLevelElement(requestElement); // build response message based on operation type if (op instanceof QueryOperation) { QueryOperation q = (QueryOperation) op; ComplexType responseType = new ComplexType(); responseType.setName(op.getName() + RESPONSE_SUFFIX + TYPE_SUFFIX); Sequence responseSequence = new Sequence(); Element result = new Element(); result.setName("result"); if (q.isAttachment()) { result.setType(WSI_SWAREF_PREFIX + ":" + WSI_SWAREF); } else if (q.isSimpleXMLFormat() || q.getResultType().equals(new QName(W3C_XML_SCHEMA_NS_URI, "any"))) { ComplexType anyType = new ComplexType(); Sequence anySequence = new Sequence(); anySequence.addAny(new Any()); anyType.setSequence(anySequence); result.setComplexType(anyType); } else { if (q.getResultType().getNamespaceURI().equals(W3C_XML_SCHEMA_NS_URI)) { result.setType(NS_SCHEMA_PREFIX + ":" + q.getResultType().getLocalPart()); } else { ComplexType nestedComplexType = new ComplexType(); Sequence nestedSequence = new Sequence(); nestedComplexType.setSequence(nestedSequence); Element nestedElement = new Element(); // may need if/else based on if the operation is/isn't user defined, i.e. //if (!q.isUserDefined()) { // nestedElement.setRef(TARGET_NAMESPACE_PREFIX + ":" + tableNameAlias); //} else { // nestedElement.setRef(TARGET_NAMESPACE_PREFIX + ":" + q.getResultType().getLocalPart()); //} nestedElement.setRef(TARGET_NAMESPACE_PREFIX + ":" + q.getResultType().getLocalPart()); nestedElement.setMinOccurs("0"); if (q.isCollection()) { nestedElement.setMaxOccurs("unbounded"); } nestedSequence.addElement(nestedElement); result.setComplexType(nestedComplexType); } } responseSequence.addElement(result); responseType.setSequence(responseSequence); schema.addTopLevelComplexTypes(responseType); Element responseElement = new Element(); responseElement.setName(op.getName() + RESPONSE_SUFFIX); responseElement.setType(NS_TNS_PREFIX + ":" + responseType.getName()); schema.addTopLevelElement(responseElement); } else { requireFaultTypeEmptyResponse = true; } } if (requireFaultTypeEmptyResponse) { // <element name="EmptyResponse"> // <xsd:complexType/> // </element> Element emptyResponseElement = new Element(); emptyResponseElement.setName(EMPTY_RESPONSE); ComplexType emptyResponseComplexType = new ComplexType(); emptyResponseElement.setComplexType(emptyResponseComplexType); schema.addTopLevelElement(emptyResponseElement); // <xsd:element name="FaultType"> // <xsd:complexType> // <xsd:sequence> // <xsd:element name="faultCode" type="xsd:string"/> // <xsd:element name="faultString" type="xsd:string"/> // </xsd:sequence> // </xsd:complexType> // </element> Element elementFaultType = new Element(); elementFaultType.setName(FAULT_SUFFIX + TYPE_SUFFIX); ComplexType faultComplexType = new ComplexType(); elementFaultType.setComplexType(faultComplexType); Sequence nestedSequence = new Sequence(); faultComplexType.setSequence(nestedSequence); Element faultCodeElement = new Element(); faultCodeElement.setName("faultCode"); faultCodeElement.setMinOccurs("1"); faultCodeElement.setType(NS_SCHEMA_PREFIX + ":string"); nestedSequence.addElement(faultCodeElement); Element faultStringElement = new Element(); faultStringElement.setMinOccurs("1"); faultStringElement.setName("faultString"); faultStringElement.setType(NS_SCHEMA_PREFIX + ":string"); nestedSequence.addElement(faultStringElement); schema.addTopLevelElement(elementFaultType); } return marshaller.objectToXML(schema).getDocumentElement(); } /** * Return the name of the service. */ public String getServiceName() { return serviceName; } /** * Return the namespace of the service. */ public String getServiceNameSpace() { return serviceNameSpace; } }