/* * 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; import java.lang.reflect.Method; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; 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.BindingOperation; import javax.wsdl.Definition; import javax.wsdl.Fault; import javax.wsdl.Input; import javax.wsdl.Message; import javax.wsdl.OperationType; import javax.wsdl.Output; import javax.wsdl.Part; import javax.wsdl.PortType; import javax.wsdl.Types; import javax.wsdl.WSDLException; import javax.wsdl.extensions.schema.Schema; import javax.wsdl.factory.WSDLFactory; import javax.xml.bind.annotation.XmlSeeAlso; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.tuscany.sca.contribution.resolver.ModelResolver; import org.apache.tuscany.sca.databinding.DataBinding; import org.apache.tuscany.sca.databinding.DataBindingExtensionPoint; import org.apache.tuscany.sca.databinding.XMLTypeHelper; import org.apache.tuscany.sca.databinding.jaxb.JAXBDataBinding; import org.apache.tuscany.sca.interfacedef.DataType; import org.apache.tuscany.sca.interfacedef.Interface; import org.apache.tuscany.sca.interfacedef.Operation; import org.apache.tuscany.sca.interfacedef.impl.DataTypeImpl; import org.apache.tuscany.sca.interfacedef.java.JavaInterface; import org.apache.tuscany.sca.interfacedef.java.JavaOperation; import org.apache.tuscany.sca.interfacedef.util.ElementInfo; import org.apache.tuscany.sca.interfacedef.util.JavaXMLMapper; import org.apache.tuscany.sca.interfacedef.util.TypeInfo; import org.apache.tuscany.sca.interfacedef.util.WrapperInfo; import org.apache.tuscany.sca.interfacedef.util.XMLType; 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.apache.tuscany.sca.xsd.XSDFactory; import org.apache.tuscany.sca.xsd.XSDefinition; import org.apache.tuscany.sca.xsd.impl.XSDefinitionImpl; import org.apache.ws.commons.schema.XmlSchema; import org.apache.ws.commons.schema.XmlSchemaCollection; import org.apache.ws.commons.schema.XmlSchemaElement; import org.apache.ws.commons.schema.XmlSchemaException; import org.apache.ws.commons.schema.utils.NamespaceMap; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * @version $Rev: 670103 $ $Date: 2008-06-21 01:35:00 +0100 (Sat, 21 Jun 2008) $ */ public class Interface2WSDLGenerator { private static final Logger logger = Logger.getLogger(Interface2WSDLGenerator.class.getName()); private static final String SCHEMA_NS = "http://www.w3.org/2001/XMLSchema"; private static final String SCHEMA_NAME = "schema"; private static final QName SCHEMA_QNAME = new QName(SCHEMA_NS, SCHEMA_NAME); private static final String XMLNS_NS = "http://www.w3.org/2000/xmlns/"; private static final String ANYTYPE_NAME = "anyType"; private static final QName ANYTYPE_QNAME = new QName(SCHEMA_NS, ANYTYPE_NAME); private WSDLFactory factory; private DataBindingExtensionPoint dataBindings; private WSDLDefinitionGenerator definitionGenerator; private boolean requiresSOAP12; private ModelResolver resolver; private XSDFactory xsdFactory; private Monitor monitor; public Interface2WSDLGenerator(boolean requiresSOAP12, ModelResolver resolver, DataBindingExtensionPoint dataBindings, XSDFactory xsdFactory, Monitor monitor) throws WSDLException { super(); this.requiresSOAP12 = requiresSOAP12; this.resolver = resolver; definitionGenerator = new WSDLDefinitionGenerator(requiresSOAP12, false); this.dataBindings = dataBindings; this.xsdFactory = xsdFactory; this.monitor = monitor; this.factory = WSDLFactory.newInstance(); } /** * 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 void warning(String message, Interface interfaze, String... messageParameters) { Problem problem = new ProblemImpl(this.getClass().getName(), "wsdlgen-validation-messages", Severity.WARNING, interfaze, message, (Object[])messageParameters); if (monitor != null) { monitor.problem(problem); } else { logWarning(problem); } } /** * Report a fatal error. * @param message * @param binding * @param parameters */ private void fatal(String message, Interface interfaze, String... messageParameters) { Problem problem = new ProblemImpl(this.getClass().getName(), "wsdlgen-validation-messages", Severity.ERROR, interfaze, message, (Object[])messageParameters); throw new WSDLGenerationException(problem.toString(), null, problem); } private XMLTypeHelper getTypeHelper(DataType type, Map helpers) { if (type == null) { return null; } String db = type.getDataBinding(); if (db == null) { return null; } // TUSCANY-3800 while ("java:array".equals(db)) { type = (DataType)type.getLogical(); db = type.getDataBinding(); } return helpers.get(db); } private boolean inputTypesCompatible(DataType wrapperType, DataType> inputType, Map helpers) { XMLTypeHelper wrapperHelper = getTypeHelper(wrapperType, helpers); for (DataType dt : inputType.getLogical()) { if (getTypeHelper(dt, helpers) != wrapperHelper) { return false; } } return true; } private boolean outputTypeCompatible(DataType wrapperType, DataType outputType, Map helpers) { if (getTypeHelper(outputType, helpers) != getTypeHelper(wrapperType, helpers)) { return false; } else { return true; } } private void addDataType(Map> map, DataType type, Map helpers) { if (type == null) { return; } String db = type.getDataBinding(); if (db == null) { return; } if ("java:array".equals(db)) { DataType dt = (DataType)type.getLogical(); db = dt.getDataBinding(); } XMLTypeHelper helper = helpers.get(db); List types = map.get(helper); if (types == null) { types = new ArrayList(); map.put(helper, types); } types.add(type); } private Map> getDataTypes(Interface intf, boolean useWrapper, Map helpers) { Map> dataTypes = new HashMap>(); for (Operation op : intf.getOperations()) { WrapperInfo inputWrapper = op.getInputWrapper(); DataType dt1 = null; boolean useInputWrapper = useWrapper & inputWrapper != null; if (useInputWrapper) { dt1 = inputWrapper.getWrapperType(); useInputWrapper &= inputTypesCompatible(dt1, op.getInputType(), helpers); } if (useInputWrapper) { addDataType(dataTypes, dt1, helpers); } else { for (DataType dt : op.getInputType().getLogical()) { addDataType(dataTypes, dt, helpers); } } DataType dt2 = null; WrapperInfo outputWrapper = op.getOutputWrapper(); boolean useOutputWrapper = useWrapper & outputWrapper != null; if (useOutputWrapper) { dt2 = outputWrapper.getWrapperType(); useOutputWrapper &= outputTypeCompatible(dt2, op.getOutputType(), helpers); } if (useOutputWrapper) { addDataType(dataTypes, dt2, helpers); } else { dt2 = op.getOutputType(); addDataType(dataTypes, dt2, helpers); } for (DataType dt3 : op.getFaultTypes()) { DataType dt4 = dt3.getLogical(); addDataType(dataTypes, dt4, helpers); } } // Adding classes referenced by @XmlSeeAlso in the java interface if (intf instanceof JavaInterface) { JavaInterface javaInterface = (JavaInterface)intf; Class[] seeAlso = getSeeAlso(javaInterface.getJavaClass()); if (seeAlso != null) { for (Class cls : seeAlso) { DataType dt = new DataTypeImpl(JAXBDataBinding.NAME, cls, XMLType.UNKNOWN); addDataType(dataTypes, dt, helpers); } } seeAlso = getSeeAlso(javaInterface.getCallbackClass()); if (seeAlso != null) { for (Class cls : seeAlso) { DataType dt = new DataTypeImpl(JAXBDataBinding.NAME, cls, XMLType.UNKNOWN); addDataType(dataTypes, dt, helpers); } } } return dataTypes; } private static Class[] getSeeAlso(Class interfaze) { if (interfaze == null) { return null; } XmlSeeAlso seeAlso = interfaze.getAnnotation(XmlSeeAlso.class); if (seeAlso == null) { return null; } else { return seeAlso.value(); } } public Definition generate(Interface interfaze, WSDLDefinition wsdlDefinition) throws WSDLException { if (interfaze == null) { return null; } if (interfaze instanceof WSDLInterface) { return ((WSDLInterface)interfaze).getWsdlDefinition().getDefinition(); } JavaInterface iface = (JavaInterface)interfaze; if (!interfaze.isRemotable()) { fatal("InterfaceNotRemotable", interfaze, iface.getName()); } QName name = getQName(iface); Definition definition = factory.newDefinition(); if (requiresSOAP12) { definition.addNamespace("SOAP12", "http://schemas.xmlsoap.org/wsdl/soap12/"); } else { definition.addNamespace("SOAP", "http://schemas.xmlsoap.org/wsdl/soap/"); } definition.addNamespace("wsdl", "http://schemas.xmlsoap.org/wsdl/"); definition.addNamespace("xs", SCHEMA_NS); String namespaceURI = name.getNamespaceURI(); definition.setTargetNamespace(namespaceURI); definition.setQName(new QName(namespaceURI, name.getLocalPart() + "Service", name.getPrefix())); definition.addNamespace(name.getPrefix(), namespaceURI); PortType portType = definition.createPortType(); portType.setQName(name); Binding binding = definitionGenerator.createBinding(definition, portType); Map helpers = new HashMap(); Map> wrappers = new HashMap>(); for (Operation op : interfaze.getOperations()) { javax.wsdl.Operation operation = generateOperation(definition, op, helpers, wrappers); portType.addOperation(operation); String action = ((JavaOperation)op).getAction(); if ((action == null || "".equals(action)) && !op.isInputWrapperStyle() && op.getInputWrapper() == null) { // Bare style action = "urn:" + op.getName(); } BindingOperation bindingOp = definitionGenerator.createBindingOperation(definition, operation, action); binding.addBindingOperation(bindingOp); } portType.setUndefined(false); definition.addPortType(portType); binding.setUndefined(false); definition.addBinding(binding); wsdlDefinition.setBinding(binding); // call each helper in turn to populate the wsdl.types element XmlSchemaCollection schemaCollection = new XmlSchemaCollection(); // TUSCANY-3298: enable JAXB wrapper generation for (Map.Entry> en: getDataTypes(interfaze, true, helpers).entrySet()) { XMLTypeHelper helper = en.getKey(); if (helper == null) { continue; } List xsDefinitions = helper.getSchemaDefinitions(xsdFactory, resolver, en.getValue()); for (XSDefinition xsDef: xsDefinitions) { Document doc = xsDef.getDocument(); if (doc != null) { // TUSCANY-2757 and TUSCANY-3267 - flip global wrapper elements with nillable // set true to be set to false. The JAXB RI seems to be generating this setting // incorrectly according to the JAXB spec. NodeList nodes = doc.getFirstChild().getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node aNode = nodes.item(i); if (aNode.getLocalName() != null && aNode.getLocalName().equals("element")) { NamedNodeMap attributes = aNode.getAttributes(); Node nameAttr = attributes.getNamedItem("name"); if (nameAttr != null) { QName elementName = new QName(xsDef.getNamespace(), nameAttr.getNodeValue()); if (wrappers.containsKey(elementName)) { Node nillable = attributes.getNamedItem("nillable"); if (nillable != null) { nillable.setNodeValue("false"); } } } } } } loadXSD(schemaCollection, xsDef); wsdlDefinition.getXmlSchemas().add(xsDef); } } // remove global wrapper elements with schema definitions from generation list for (QName wrapperName: new HashSet(wrappers.keySet())) { if (wsdlDefinition.getXmlSchemaElement(wrapperName) != null) { wrappers.remove(wrapperName); } } schemaCollection = new XmlSchemaCollection(); // generate schema elements for wrappers that aren't defined in the schemas if (wrappers.size() > 0) { int i = 0; int index = 0; Map wrapperXSDs = new HashMap(); Map> prefixMaps = new HashMap>(); for (Map.Entry> entry: wrappers.entrySet()) { String targetNS = entry.getKey().getNamespaceURI(); Document schemaDoc = null; Element schema = null; XSDefinition xsDef = wrapperXSDs.get(targetNS); if (xsDef != null) { schemaDoc = xsDef.getDocument(); schema = schemaDoc.getDocumentElement(); } else { xsDef = wsdlDefinition.getSchema(targetNS); if (xsDef != null) { schemaDoc = xsDef.getDocument(); schema = schemaDoc.getDocumentElement(); wrapperXSDs.put(targetNS, xsDef); Map prefixMap = prefixMaps.get(schema); if (prefixMap == null){ prefixMap = new HashMap(); prefixMaps.put(schema, prefixMap); String [] prefixes = xsDef.getSchema().getNamespaceContext().getDeclaredPrefixes(); for (int j = 0; j < prefixes.length; j++){ prefixMap.put(xsDef.getSchema().getNamespaceContext().getNamespaceURI(prefixes[j]), prefixes[j]); } } } else { schemaDoc = createDocument(); schema = schemaDoc.createElementNS(SCHEMA_NS, "xs:schema"); // The elementFormDefault should be set to unqualified, see TUSCANY-2388 schema.setAttribute("elementFormDefault", "unqualified"); schema.setAttribute("attributeFormDefault", "qualified"); schema.setAttribute("targetNamespace", targetNS); schema.setAttributeNS(XMLNS_NS, "xmlns:xs", SCHEMA_NS); schema.setAttributeNS(XMLNS_NS, "xmlns:tns", targetNS); schemaDoc.appendChild(schema); prefixMaps.put(schema, new HashMap()); xsDef = xsdFactory.createXSDefinition(); xsDef.setUnresolved(true); xsDef.setNamespace(targetNS); xsDef.setDocument(schemaDoc); // TUSCANY-2465: Set the system id to avoid schema conflict xsDef.setLocation(URI.create("xsd_" + index + ".xsd")); index++; wrapperXSDs.put(targetNS, xsDef); } } Element wrapper = schemaDoc.createElementNS(SCHEMA_NS, "xs:element"); schema.appendChild(wrapper); wrapper.setAttribute("name", entry.getKey().getLocalPart()); if (entry.getValue().size() == 1 && entry.getValue().get(0).getQName() == null) { // special case for global fault element QName typeName = entry.getValue().get(0).getType().getQName(); String nsURI = typeName.getNamespaceURI(); if ("".equals(nsURI)) { wrapper.setAttribute("type", typeName.getLocalPart()); addSchemaImport(schema, "", schemaDoc); } else if (targetNS.equals(nsURI)) { wrapper.setAttribute("type", typeName.getLocalPart()); } else if (SCHEMA_NS.equals(nsURI)) { wrapper.setAttribute("type", "xs:" + typeName.getLocalPart()); } else { Map prefixMap = prefixMaps.get(schema); String prefix = prefixMap.get(nsURI); if (prefix == null) { prefix = "ns" + i++; while(prefixMap.containsValue(prefix)){ prefix = "ns" + i++; } prefixMap.put(nsURI, prefix); schema.setAttributeNS(XMLNS_NS, "xmlns:" + prefix, nsURI); addSchemaImport(schema, nsURI, schemaDoc); } wrapper.setAttribute("type", prefix + ":" + typeName.getLocalPart()); } } else { // normal wrapper containing type definition inline Element complexType = schemaDoc.createElementNS(SCHEMA_NS, "xs:complexType"); wrapper.appendChild(complexType); if (entry.getValue().size() > 0) { Element sequence = schemaDoc.createElementNS(SCHEMA_NS, "xs:sequence"); complexType.appendChild(sequence); for (ElementInfo element: entry.getValue()) { Element xsElement = schemaDoc.createElementNS(SCHEMA_NS, "xs:element"); if (element.isMany()) { xsElement.setAttribute("maxOccurs", "unbounded"); } xsElement.setAttribute("minOccurs", "0"); xsElement.setAttribute("name", element.getQName().getLocalPart()); if (element.isNillable()) { xsElement.setAttribute("nillable", "true"); } QName typeName = element.getType().getQName(); String nsURI = typeName.getNamespaceURI(); if ("".equals(nsURI)) { xsElement.setAttribute("type", typeName.getLocalPart()); addSchemaImport(schema, "", schemaDoc); } else if (SCHEMA_NS.equals(nsURI)) { xsElement.setAttribute("type", "xs:" + typeName.getLocalPart()); } else { Map prefixMap = prefixMaps.get(schema); String prefix = prefixMap.get(nsURI); if (prefix == null) { if (targetNS.equals(nsURI)) { prefix = "tns"; } else { prefix = "ns" + i++; while(prefixMap.containsValue(prefix)){ prefix = "ns" + i++; } addSchemaImport(schema, nsURI, schemaDoc); } prefixMap.put(nsURI, prefix); schema.setAttributeNS(XMLNS_NS, "xmlns:" + prefix, nsURI); } xsElement.setAttribute("type", prefix + ":" + typeName.getLocalPart()); } sequence.appendChild(xsElement); } } } } // resolve XSDefinitions containing generated wrappers for (XSDefinition xsDef: wrapperXSDs.values()) { if (wsdlDefinition.getSchema(xsDef.getNamespace()) == null) { wsdlDefinition.getXmlSchemas().add(xsDef); } xsDef.setSchema(null); xsDef.setSchemaCollection(null); loadXSD(schemaCollection, xsDef); } } XSDefinition noNamespaceSchema = null; XSDefinition defaultNamespaceSchema = null; for (XSDefinition xsDef: wsdlDefinition.getXmlSchemas()) { // only apply these changes if we have a DOM to work with // this will generally come from the JAXB generation. SDO // just goes straight to generating XmlSchema structures Document doc = xsDef.getDocument(); if (doc != null) { // TUSCANY-3283 find any schema that has been generated without a namespace // and any schema using the default WSDL schema so that we can // merge the two together if (xsDef.getNamespace().equals("")){ noNamespaceSchema = xsDef; } if (xsDef.getNamespace().equals(namespaceURI)){ defaultNamespaceSchema = xsDef; } } } // useful for debugging DOM issues //System.out.println("======================= Schema DOM Pre Merge=======================" ); //for (XSDefinition xsDef : wsdlDefinition.getXmlSchemas()) { // if (xsDef.getDocument() != null) { // printDOM(xsDef.getDocument()); // } //} // TUSCANY-3283 merge the no namespace schema into the default namespace schema if (noNamespaceSchema != null && defaultNamespaceSchema != null){ // remove the no namespace schema from our list of schema wsdlDefinition.getXmlSchemas().remove(noNamespaceSchema); // merge the schema with no namespace into the schema with the default namspace for this WSDL mergeSchema(noNamespaceSchema, defaultNamespaceSchema, wsdlDefinition.getXmlSchemas()); // useful for debugging DOM issues //System.out.println("======================= Schema DOM Post Merge=======================" ); //for (XSDefinition xsDef : wsdlDefinition.getXmlSchemas()) { // if (xsDef.getDocument() != null) { // printDOM(xsDef.getDocument()); // } //} schemaCollection = new XmlSchemaCollection(); defaultNamespaceSchema.setSchema(null); defaultNamespaceSchema.setSchemaCollection(null); loadXSD(schemaCollection, defaultNamespaceSchema); } // push the schema into the WSDL for (XSDefinition xsDef: wsdlDefinition.getXmlSchemas()){ addSchemaExtension(xsDef, schemaCollection, wsdlDefinition, definition); } // useful for debugging DOM issues //System.out.println("======================= Schema DOM Process End =======================" ); //for (XSDefinition xsDef : wsdlDefinition.getXmlSchemas()) { // if (xsDef.getDocument() != null) { // printDOM(xsDef.getDocument()); // } //} return definition; } /** * TUSCANY-3283 * Merge the no namespace schema into the defualt namespace schema * Relies on being called just after the getSchemaDefinitions call when the XSDefinitions * have only the DOM information set * * @param noNamespaceSchema * @param defaultNamespaceSchema * @param xsDefinitions */ private void mergeSchema(XSDefinition noNamespaceSchema, XSDefinition defaultNamespaceSchema, Collection xsDefinitions){ Document fromDoc = noNamespaceSchema.getDocument(); Document toDoc = defaultNamespaceSchema.getDocument(); // merge namespace definitions from the nonamespace schema into the default namespace schema for(int i = 0; i < fromDoc.getDocumentElement().getAttributes().getLength(); i++){ Attr attribute = (Attr)fromDoc.getDocumentElement().getAttributes().item(i); String attribName = attribute.getName(); if (attribName.startsWith("xmlns:")){ String fromPrefix = attribName.substring(attribName.indexOf(":") + 1); if (fromPrefix.equals("xs") != true){ // create a new namespace prefix to prevent clashes toDoc.getDocumentElement().setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:__" + fromPrefix, attribute.getValue()); // fix up any references to the existing namespace prefix fixUpNoNamespaceNamespaces(fromDoc, fromPrefix); } } } Node toDocFirstChild = toDoc.getDocumentElement().getFirstChild(); // merge types from the no namespace schema into the default namespace schema for(int i = 0; i < fromDoc.getDocumentElement().getChildNodes().getLength(); i++){ // merge the DOM types Node node = fromDoc.getDocumentElement().getChildNodes().item(i); Node newNode = toDoc.importNode(node, true); if (newNode.getLocalName() != null && (newNode.getLocalName().equals("import") || newNode.getLocalName().equals("include") || newNode.getLocalName().equals("redefine") || newNode.getLocalName().equals("annotation") )){ toDoc.getDocumentElement().insertBefore(newNode, toDocFirstChild); } else { toDoc.getDocumentElement().appendChild(newNode); } } // fix up any references to the types we've just moved for(int i = 0; i < fromDoc.getDocumentElement().getChildNodes().getLength(); i++){ Node node = fromDoc.getDocumentElement().getChildNodes().item(i); // correct any references to no name types in other schema if (node.getLocalName() != null && node.getLocalName().equals("complexType")){ Node typeName = node.getAttributes().getNamedItem("name"); fixUpNoNamespaceReferences(xsDefinitions, typeName.getNodeValue(), defaultNamespaceSchema.getNamespace()); } } } /** * TUSCANY-3283 * Correct any namespace prefixes in the no namespace schema to prevent them from * clashing when the no namespace schema is merged with the default schema * * @param fromSchema * @param fromPrefix * @param toPrefix */ private void fixUpNoNamespaceNamespaces(Document fromSchema, String fromPrefix){ NodeList elements = fromSchema.getElementsByTagNameNS("http://www.w3.org/2001/XMLSchema","element"); for (int k = 0; k < elements.getLength(); k++){ Element element = (Element) elements.item(k); if (element != null && element.getAttributes() != null) { Node type = element.getAttributes().getNamedItem("type"); if (type != null && type.getNodeValue().startsWith(fromPrefix)){ type.setNodeValue("__" + type.getNodeValue()); } } } } /** * TUSCANY-3283 * Correct any references in the schema list that used to point to types in the * no namespace schema * * @param fromSchema * @param toSchema */ private void fixUpNoNamespaceReferences(Collection xsDefinitions, String typeName, String defaultNamespace){ // fix up any references in any other schema that points to this type for (XSDefinition xsDef: xsDefinitions) { if (xsDef.getDocument() != null){ // look for any imports of the no namespace schema Document refSchema = xsDef.getDocument(); NodeList imports = refSchema.getElementsByTagNameNS("http://www.w3.org/2001/XMLSchema","import"); List importsToDelete = new ArrayList(); for (int j = 0; j < imports.getLength(); j++){ Element _import = (Element)imports.item(j); // Fix up the import if (_import.getAttributes().getLength() == 0){ if (xsDef.getNamespace().equals(defaultNamespace)){ // remove the import //_import.getParentNode().removeChild(_import); importsToDelete.add(_import); } else { // update the import to refer to the default namespace _import.setAttribute("namespace", defaultNamespace); } } } for (Node _import : importsToDelete){ _import.getParentNode().removeChild(_import); } // look for any type attributes that refer to the // node being merged NodeList elements = refSchema.getElementsByTagNameNS("http://www.w3.org/2001/XMLSchema","element"); for (int k = 0; k < elements.getLength(); k++){ Element element = (Element) elements.item(k); if (element != null && element.getAttributes() != null) { Node type = element.getAttributes().getNamedItem("type"); if (type != null && type.getNodeValue().equals(typeName)){ if (xsDef.getNamespace().equals(defaultNamespace)){ // double check that there is a "tns" namespace shortname specified String tnsNamespace = refSchema.getDocumentElement().getAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:tns"); if (tnsNamespace == null || tnsNamespace.length() == 0) { refSchema.getDocumentElement().setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:tns", defaultNamespace); } // just add "tns" in front of the type name as // we have merged the type into this schema type.setNodeValue("tns:" + type.getNodeValue()); } else { // add a namespace refSchema.getDocumentElement().setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:__nnns", defaultNamespace); // prefix the type name with the namespace type.setNodeValue("__nnns:" + type.getNodeValue()); } } } } } } } /* * Just used when debugging DOM problems */ private void printDOM(Document document){ try { Transformer transformer = TransformerFactory.newInstance().newTransformer(); Source source = new DOMSource(document); Result result = new StreamResult(System.out); transformer.transform(source, result); } catch (Exception ex){ ex.toString(); } } private static void addSchemaImport(Element schema, String nsURI, Document schemaDoc) { Element imp = schemaDoc.createElementNS(SCHEMA_NS, "xs:import"); if (!"".equals(nsURI)) { imp.setAttribute("namespace", nsURI); } // Scan all xs:import elements to match namespace NodeList childNodes = schema.getElementsByTagNameNS(SCHEMA_NS, "import"); for (int i = 0; i < childNodes.getLength(); i++) { Node childNode = childNodes.item(i); if (childNode instanceof Element) { String ns = ((Element)childNode).getAttributeNS(SCHEMA_NS, "namespace"); if (nsURI.equals(ns)) { // The xs:import with the same namespace has been declared return; } } } // Try to find the first node after the import elements Node firstNodeAfterImport = null; if (childNodes.getLength() > 0) { firstNodeAfterImport = childNodes.item(childNodes.getLength() - 1).getNextSibling(); } else { firstNodeAfterImport = schema.getFirstChild(); } if (firstNodeAfterImport == null) { schema.appendChild(imp); } else { schema.insertBefore(imp, firstNodeAfterImport); } } private void addSchemaExtension(XSDefinition xsDef, XmlSchemaCollection schemaCollection, WSDLDefinition wsdlDefinition, Definition definition) throws WSDLException { if (xsDef.getAggregatedDefinitions() != null) { for (XSDefinition xsd: xsDef.getAggregatedDefinitions()) { addSchemaExtension(xsd, schemaCollection, wsdlDefinition, definition); } } else { String nsURI = xsDef.getNamespace(); Document document = xsDef.getDocument(); if (document == null) { try { NamespaceMap prefixMap = new NamespaceMap(); prefixMap.add("xs", SCHEMA_NS); prefixMap.add("tns", nsURI); XmlSchema schemaDef = xsDef.getSchema(); schemaDef.setNamespaceContext(prefixMap); Document[] docs = schemaDef.getAllSchemas(); document = docs[docs.length-1]; document.setDocumentURI(xsDef.getLocation().toString()); xsDef.setDocument(document); } catch (XmlSchemaException e) { throw new RuntimeException(e); } } //loadXSD(schemaCollection, xsDef); //wsdlDefinition.getXmlSchemas().add(xsDef); Element schema = document.getDocumentElement(); Schema schemaExt = createSchemaExt(definition); schemaExt.setDocumentBaseURI(document.getDocumentURI()); schemaExt.setElement(schema); } } private static void loadXSD(XmlSchemaCollection schemaCollection, XSDefinition definition) { if (definition.getSchema() != null) { return; } if (definition.getDocument() != null) { String uri = null; if (definition.getLocation() != null) { uri = definition.getLocation().toString(); } XmlSchema schema = schemaCollection.read(definition.getDocument(), uri, null); if (definition.getSchemaCollection() == null) { definition.setSchemaCollection(schemaCollection); } if (definition.getSchema() == null) { definition.setSchema(schema); } } } public Schema createSchemaExt(Definition definition) throws WSDLException { Types types = definition.getTypes(); if (types == null) { types = definition.createTypes(); definition.setTypes(types); } Schema schemaExt = createSchema(definition); types.addExtensibilityElement(schemaExt); return schemaExt; } public Schema createSchema(Definition definition) throws WSDLException { return (Schema)definition.getExtensionRegistry().createExtension(Types.class, SCHEMA_QNAME); } private DocumentBuilderFactory documentBuilderFactory; public Document createDocument() { Document document; try { if (documentBuilderFactory == null) { documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setNamespaceAware(true); } document = documentBuilderFactory.newDocumentBuilder().newDocument(); } catch (ParserConfigurationException ex) { throw new WSDLGenerationException(ex); } // document.setDocumentURI("http://"); return document; } protected QName getQName(Interface interfaze) { JavaInterface iface = (JavaInterface)interfaze; QName qname = iface.getQName(); if (qname != null) { return qname; } else { Class javaClass = iface.getJavaClass(); return new QName(JavaXMLMapper.getNamespace(javaClass), javaClass.getSimpleName(), "tns"); } } public javax.wsdl.Operation generateOperation(Definition definition, Operation op, Map helpers, Map> wrappers) throws WSDLException { javax.wsdl.Operation operation = definition.createOperation(); operation.setName(op.getName()); operation.setUndefined(false); Input input = definition.createInput(); Message inputMsg = definition.createMessage(); String namespaceURI = definition.getQName().getNamespaceURI(); QName inputMsgName = new QName(namespaceURI, op.getName()); inputMsg.setQName(inputMsgName); inputMsg.setUndefined(false); definition.addMessage(inputMsg); List elements = null; // FIXME: By default, java interface is mapped to doc-lit-wrapper style WSDL if (op.getInputWrapper() != null) { // Generate doc-lit-wrapper style inputMsg.addPart(generateWrapperPart(definition, op, helpers, wrappers, true)); } else { // Bare style int i = 0; for (DataType d : op.getInputType().getLogical()) { inputMsg.addPart(generatePart(definition, d, "arg" + i)); elements = new ArrayList(); ElementInfo element = getElementInfo(d.getPhysical(), d, null, helpers); elements.add(element); QName elementName = ((XMLType)d.getLogical()).getElementName(); wrappers.put(elementName, elements); i++; } } input.setMessage(inputMsg); operation.setInput(input); if (!op.isNonBlocking()) { Output output = definition.createOutput(); Message outputMsg = definition.createMessage(); QName outputMsgName = new QName(namespaceURI, op.getName() + "Response"); outputMsg.setQName(outputMsgName); outputMsg.setUndefined(false); definition.addMessage(outputMsg); if (op.getOutputWrapper() != null) { outputMsg.addPart(generateWrapperPart(definition, op, helpers, wrappers, false)); } else { DataType outputType = op.getOutputType(); if (outputType != null) { outputMsg.addPart(generatePart(definition, outputType, "return")); elements = new ArrayList(); ElementInfo element = getElementInfo(outputType.getPhysical(), outputType, null, helpers); elements.add(element); QName elementName = ((XMLType)outputType.getLogical()).getElementName(); wrappers.put(elementName, elements); } } output.setMessage(outputMsg); operation.setOutput(output); operation.setStyle(OperationType.REQUEST_RESPONSE); } else { operation.setStyle(OperationType.ONE_WAY); } for (DataType faultType: op.getFaultTypes()) { Fault fault = definition.createFault(); QName faultName = ((XMLType)faultType.getLogical().getLogical()).getElementName(); QName faultMsgName = new QName(namespaceURI, faultName.getLocalPart()); fault.setName(faultName.getLocalPart()); Message faultMsg = definition.getMessage(faultMsgName); if (faultMsg == null) { faultMsg = definition.createMessage(); faultMsg.setQName(faultMsgName); faultMsg.setUndefined(false); definition.addMessage(faultMsg); faultMsg.addPart(generatePart(definition, faultType.getLogical(), faultName.getLocalPart())); } fault.setMessage(faultMsg); operation.addFault(fault); if (faultType.getLogical().getPhysical() != faultType.getPhysical()) { // create special wrapper for type indirection to real fault bean DataType logical = faultType.getLogical(); elements = new ArrayList(); elements.add(getElementInfo(logical.getPhysical(), logical, null, helpers)); } else { // convert synthesized fault bean to a wrapper type for (DataType propDT: op.getFaultBeans().get(faultName)) { XMLType logical = propDT.getLogical(); elements = new ArrayList(); elements.add(getElementInfo(propDT.getPhysical(), propDT, logical.getElementName(), helpers)); } } wrappers.put(faultName, elements); } operation.setUndefined(false); return operation; } public Part generatePart(Definition definition, DataType arg, String partName) { Part part = definition.createPart(); part.setName(partName); if (arg != null && arg.getLogical() instanceof XMLType) { XMLType xmlType = (XMLType)arg.getLogical(); QName elementName = xmlType.getElementName(); part.setElementName(elementName); addNamespace(definition, elementName); if (xmlType.getElementName() == null) { QName typeName = xmlType.getTypeName(); part.setTypeName(typeName); addNamespace(definition, typeName); } } return part; } public Part generateWrapperPart(Definition definition, Operation operation, Map helpers, Map> wrappers, boolean input) throws WSDLException { Part part = definition.createPart(); String partName = input ? operation.getName() : (operation.getName() + "Response"); part.setName(partName); WrapperInfo inputWrapperInfo = operation.getInputWrapper(); WrapperInfo outputWrapperInfo = operation.getOutputWrapper(); if ((inputWrapperInfo != null) && (outputWrapperInfo != null)) { ElementInfo elementInfo = input ? inputWrapperInfo.getWrapperElement() : outputWrapperInfo.getWrapperElement(); List elements = input ? inputWrapperInfo.getChildElements() : outputWrapperInfo.getChildElements(); QName wrapperName = elementInfo.getQName(); part.setElementName(wrapperName); addNamespace(definition, wrapperName); wrappers.put(wrapperName, elements); // FIXME: [rfeng] Ideally, we should try to register the wrappers only. But we are // expriencing the problem that we cannot handle XSD imports /* Class wrapperClass = input ? opWrapper.getInputWrapperClass() : opWrapper.getOutputWrapperClass(); DataType wrapperDT = input ? opWrapper.getInputWrapperType() : opWrapper.getOutputWrapperType(); if (wrapperClass != null) { getElementInfo(wrapperClass, wrapperDT, wrapperName, helpers); return part; } */ Method method = ((JavaOperation)operation).getJavaMethod(); if (input) { Class[] paramTypes = method.getParameterTypes(); for (int i = 0; i < paramTypes.length; i++) { DataType dataType = operation.getInputType().getLogical().get(i); elements.set(i, getElementInfo(paramTypes[i], dataType, elements.get(i).getQName(), helpers)); } } else { Class returnType = method.getReturnType(); if (returnType != Void.TYPE) { DataType dataType = operation.getOutputType(); elements.set(0, getElementInfo(returnType, dataType, elements.get(0).getQName(), helpers)); } } } return part; } private ElementInfo getElementInfo(Class javaType, DataType dataType, QName name, Map helpers) { String db = dataType.getDataBinding(); while ("java:array".equals(db)) { dataType = (DataType)dataType.getLogical(); db = dataType.getDataBinding(); } XMLTypeHelper helper = helpers.get(db); if (helper == null) { DataBinding dataBinding = dataBindings.getDataBinding(db); if (dataBinding == null) { QName element = name; if (element == null || dataType.getLogical() instanceof XMLType) { XMLType xmlType = (XMLType)dataType.getLogical(); if (xmlType.getElementName() != null) { element = xmlType.getElementName(); } } return new ElementInfo(element, new TypeInfo(ANYTYPE_QNAME, false, null)); // throw new ServiceRuntimeException("No data binding for " + db); } helper = dataBinding.getXMLTypeHelper(); if (helper == null) { // Default to JAXB helper = helpers.get(JAXBDataBinding.NAME); if (helper == null) { helper = dataBindings.getDataBinding(JAXBDataBinding.NAME).getXMLTypeHelper(); helpers.put(JAXBDataBinding.NAME, helper); } } helpers.put(db, helper); } TypeInfo typeInfo = helper.getTypeInfo(byte[].class != javaType && javaType.isArray() ? javaType.getComponentType() : javaType, dataType.getLogical()); ElementInfo element = new ElementInfo(name, typeInfo); element.setMany(byte[].class != javaType && javaType.isArray()); // TUSCANY-3298: Check the "many" flag set by databinding introspection Object logical = dataType.getLogical(); if (logical instanceof XMLType && ((XMLType)logical).isMany()) { element.setMany(true); } element.setNillable(!javaType.isPrimitive()); return element; } private static void addNamespace(Definition definition, QName name) { String namespace = name.getNamespaceURI(); if (definition.getPrefix(namespace) == null) { definition.addNamespace("ns" + definition.getNamespaces().size(), namespace); } } /* // currently not using the next three methods public XmlSchemaType getXmlSchemaType(DataType type) { return null; } // FIXME: WE need to add databinding-specific Java2XSD generation public Element generateXSD(DataType dataType) { DataBinding dataBinding = dataBindings.getDataBinding(dataType.getDataBinding()); if (dataBinding != null) { // return dataBinding.generateSchema(dataType); } return null; } public void generateWrapperElements(Operation op) { XmlSchemaCollection collection = new XmlSchemaCollection(); String ns = getQName(op.getInterface()).getNamespaceURI(); XmlSchema schema = new XmlSchema(ns, collection); schema.setAttributeFormDefault(new XmlSchemaForm(XmlSchemaForm.QUALIFIED)); schema.setElementFormDefault(new XmlSchemaForm(XmlSchemaForm.QUALIFIED)); XmlSchemaElement inputElement = new XmlSchemaElement(); inputElement.setQName(new QName(ns, op.getName())); XmlSchemaComplexType inputType = new XmlSchemaComplexType(schema); inputType.setName(""); XmlSchemaSequence inputSeq = new XmlSchemaSequence(); inputType.setParticle(inputSeq); List argTypes = op.getInputType().getLogical(); for (DataType argType : argTypes) { XmlSchemaElement child = new XmlSchemaElement(); Object logical = argType.getLogical(); if (logical instanceof XMLType) { child.setName(((XMLType)logical).getElementName().getLocalPart()); XmlSchemaType type = getXmlSchemaType(argType); child.setType(type); } inputSeq.getItems().add(child); } inputElement.setType(inputType); XmlSchemaElement outputElement = new XmlSchemaElement(); outputElement.setQName(new QName(ns, op.getName() + "Response")); XmlSchemaComplexType outputType = new XmlSchemaComplexType(schema); outputType.setName(""); XmlSchemaSequence outputSeq = new XmlSchemaSequence(); outputType.setParticle(outputSeq); DataType returnType = op.getOutputType(); XmlSchemaElement child = new XmlSchemaElement(); Object logical = returnType.getLogical(); if (logical instanceof XMLType) { child.setName(((XMLType)logical).getElementName().getLocalPart()); XmlSchemaType type = getXmlSchemaType(returnType); child.setType(type); } outputSeq.getItems().add(child); outputElement.setType(outputType); schema.getElements().add(inputElement.getQName(), inputElement); schema.getElements().add(outputElement.getQName(), outputElement); } */ public WSDLFactory getFactory() { return factory; } public void setFactory(WSDLFactory factory) { this.factory = factory; } }