blob: c5dc4a833d87550425a1d09ef4c479837d89abb1 [file] [log] [blame]
/*
* 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<String, XMLTypeHelper> helpers) {
if (type == null) {
return null;
}
String db = type.getDataBinding();
if (db == null) {
return null;
}
if ("java:array".equals(db)) {
DataType dt = (DataType)type.getLogical();
db = dt.getDataBinding();
}
return helpers.get(db);
}
private boolean inputTypesCompatible(DataType wrapperType, DataType<List<DataType>> inputType, Map<String, XMLTypeHelper> 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<String, XMLTypeHelper> helpers) {
if (getTypeHelper(outputType, helpers) != getTypeHelper(wrapperType, helpers)) {
return false;
} else {
return true;
}
}
private void addDataType(Map<XMLTypeHelper, List<DataType>> map, DataType type, Map<String, XMLTypeHelper> 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<DataType> types = map.get(helper);
if (types == null) {
types = new ArrayList<DataType>();
map.put(helper, types);
}
types.add(type);
}
private Map<XMLTypeHelper, List<DataType>> getDataTypes(Interface intf, boolean useWrapper, Map<String, XMLTypeHelper> helpers) {
Map<XMLTypeHelper, List<DataType>> dataTypes = new HashMap<XMLTypeHelper, List<DataType>>();
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<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<XMLType>(JAXBDataBinding.NAME, cls, XMLType.UNKNOWN);
addDataType(dataTypes, dt, helpers);
}
}
seeAlso = getSeeAlso(javaInterface.getCallbackClass());
if (seeAlso != null) {
for (Class<?> cls : seeAlso) {
DataType dt = new DataTypeImpl<XMLType>(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<String, XMLTypeHelper> helpers = new HashMap<String, XMLTypeHelper>();
Map<QName, List<ElementInfo>> wrappers = new HashMap<QName, List<ElementInfo>>();
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();
for (Map.Entry<XMLTypeHelper, List<DataType>> en: getDataTypes(interfaze, false, helpers).entrySet()) {
XMLTypeHelper helper = en.getKey();
if (helper == null) {
continue;
}
List<XSDefinition> 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<QName>(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<String, XSDefinition> wrapperXSDs = new HashMap<String, XSDefinition>();
Map<Element, Map<String, String>> prefixMaps = new HashMap<Element, Map<String, String>>();
for (Map.Entry<QName, List<ElementInfo>> 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<String, String> prefixMap = prefixMaps.get(schema);
if (prefixMap == null){
prefixMap = new HashMap<String, String>();
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<String, String>());
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<String, String> 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<String, String> 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<XSDefinition> 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<XSDefinition> 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<Node> importsToDelete = new ArrayList<Node>();
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<String, XMLTypeHelper> helpers,
Map<QName, List<ElementInfo>> 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<ElementInfo> 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>();
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>();
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<DataType> faultType: op.getFaultTypes()) {
Fault fault = definition.createFault();
QName faultName = ((XMLType)faultType.getLogical().getLogical()).getElementName();
fault.setName(faultName.getLocalPart());
Message faultMsg = definition.getMessage(faultName);
if (faultMsg == null) {
faultMsg = definition.createMessage();
faultMsg.setQName(new QName(namespaceURI, faultName.getLocalPart()));
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<ElementInfo>();
elements.add(getElementInfo(logical.getPhysical(), logical, null, helpers));
} else {
// convert synthesized fault bean to a wrapper type
for (DataType<XMLType> propDT: op.getFaultBeans().get(faultName)) {
XMLType logical = propDT.getLogical();
elements = new ArrayList<ElementInfo>();
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<String, XMLTypeHelper> helpers,
Map<QName, List<ElementInfo>> 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<ElementInfo> 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<String, XMLTypeHelper> 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());
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<DataType> 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;
}
}