blob: 445e158c29afc16ae2b97daf6b764b48ecdc3107 [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2007 The University of Manchester
*
* Modifications to the initial code base are copyright of their
* respective authors, or their employers as appropriate.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
******************************************************************************/
package net.sf.taverna.wsdl.soap;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.wsdl.WSDLException;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.soap.SOAPException;
import net.sf.taverna.wsdl.parser.ArrayTypeDescriptor;
import net.sf.taverna.wsdl.parser.BaseTypeDescriptor;
import net.sf.taverna.wsdl.parser.ComplexTypeDescriptor;
import net.sf.taverna.wsdl.parser.TypeDescriptor;
import net.sf.taverna.wsdl.parser.UnknownOperationException;
import net.sf.taverna.wsdl.parser.WSDLParser;
import org.apache.axis.encoding.Base64;
import org.apache.axis.message.SOAPBodyElement;
import org.apache.log4j.Logger;
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.xml.sax.SAXException;
@SuppressWarnings("unchecked")
public abstract class AbstractBodyBuilder implements BodyBuilder {
private static Logger logger = Logger.getLogger(AbstractBodyBuilder.class);
private String style;
private WSDLParser parser;
private String operationName;
protected enum Style {
DOCUMENT, RPC
};
protected enum Use {
LITERAL, ENCODED
};
protected Map<String, String> namespaceMappings;
protected List<TypeDescriptor> inputDescriptors;
public AbstractBodyBuilder(String style, WSDLParser parser,
String operationName, List<TypeDescriptor> inputDescriptors) {
this.style = style;
this.parser = parser;
this.operationName = operationName;
this.inputDescriptors = inputDescriptors;
}
protected Style getStyle() {
Style result = Style.DOCUMENT;
if (style.equalsIgnoreCase("rpc")) {
result = Style.RPC;
} else if (style.equalsIgnoreCase("document")) {
result = Style.DOCUMENT;
}
return result;
}
protected abstract Use getUse();
/**
*
* @return the namespace for the operation
*/
private String getOperationNamespace() throws UnknownOperationException {
return parser.getOperationNamespaceURI(operationName);
}
private QName getOperationQname() throws UnknownOperationException {
return parser.getOperationQname(operationName);
}
public SOAPBodyElement build(Map inputMap) throws WSDLException,
ParserConfigurationException, SOAPException, IOException,
SAXException, UnknownOperationException {
List inputs = parser.getOperationInputParameters(operationName);
namespaceMappings = generateNamespaceMappings(inputs);
QName operationQname = getOperationQname();
SOAPBodyElement body = new SOAPBodyElement(operationQname);
// its important to preserve the order of the inputs!
for (Iterator iterator = inputs.iterator(); iterator.hasNext();) {
TypeDescriptor descriptor = (TypeDescriptor) iterator.next();
String inputName = descriptor.getName();
Object dataValue = inputMap.get(inputName);
body = createBodyElementForData(operationName, namespaceMappings,
operationQname.getNamespaceURI(), body, descriptor, inputName, dataValue);
}
return body;
}
protected SOAPBodyElement createBodyElementForData(String operationName,
Map<String, String> namespaceMappings, String operationNamespace,
SOAPBodyElement body, TypeDescriptor descriptor, String inputName,
Object dataValue) throws ParserConfigurationException,
SAXException, IOException, UnknownOperationException, SOAPException {
if (dataValue != null) {
String mimeType = getMimeTypeForInputName(inputName);
String typeName = descriptor.getType();
Element el = null;
if (descriptor instanceof ArrayTypeDescriptor) {
el = createElementForArrayType(namespaceMappings, inputName,
dataValue, descriptor, mimeType, typeName);
} else {
el = createSkeletonElementForSingleItem(namespaceMappings,
descriptor, inputName, typeName);
populateElementWithObjectData(mimeType, el, dataValue, descriptor);
}
body = addElementToBody(operationNamespace, body, el);
}
return body;
}
protected abstract SOAPBodyElement addElementToBody(
String operationNamespace, SOAPBodyElement body, Element el)
throws SOAPException;
protected abstract Element createSkeletonElementForSingleItem(
Map<String, String> namespaceMappings, TypeDescriptor descriptor,
String inputName, String typeName);
/**
* generates an XML DOM Element for an array
*
* @param namespaceMappings
* @param inputName
* @param dataValue
* @param descriptor
* @param mimeType
* @param typeName
* @return
* @throws ParserConfigurationException
* @throws SAXException
* @throws IOException
*/
protected abstract Element createElementForArrayType(
Map<String, String> namespaceMappings, String inputName,
Object dataValue, TypeDescriptor descriptor, String mimeType,
String typeName) throws ParserConfigurationException, SAXException,
IOException, UnknownOperationException;
/**
* Populates a DOM XML Element with the contents of a List of dataValues
*
* @param mimeType -
* the mime type of the data
* @param element -
* the Element to be populated
* @param dataValues -
* the List of Objects containing the data
* @param elementType -
* the TypeDescriptor for the element being populated
* @throws ParserConfigurationException
* @throws SAXException
* @throws IOException
*/
protected void populateElementWithList(String mimeType, Element element,
List dataValues, TypeDescriptor elementType)
throws ParserConfigurationException, SAXException, IOException {
for (Iterator dataIterator = dataValues.iterator(); dataIterator
.hasNext();) {
Object dataItem = dataIterator.next();
String tag;
if (elementType instanceof BaseTypeDescriptor) {
tag = elementType.getType();
} else {
tag = elementType.getName();
}
Element item = element.getOwnerDocument().createElement(tag);
populateElementWithObjectData(mimeType, item, dataItem, elementType);
element.appendChild(item);
}
}
/**
* Populates a DOM XML Element with dataValue according to its mimetype
*
* @param mimeType
* @param element
* @param dataValue
* @param descriptor
* @throws ParserConfigurationException
* @throws SAXException
* @throws IOException
*/
protected void populateElementWithObjectData(String mimeType,
Element element, Object dataValue, TypeDescriptor descriptor)
throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
if (mimeType.equals("'text/xml'")) {
Document doc = builder.parse(new ByteArrayInputStream(dataValue
.toString().getBytes()));
Element documentElement = doc.getDocumentElement();
if (descriptor instanceof ComplexTypeDescriptor) {
ComplexTypeDescriptor complexType = (ComplexTypeDescriptor) descriptor;
NamedNodeMap attributes = documentElement.getAttributes();
if (attributes != null) {
for (int i = 0; i < attributes.getLength(); i++) {
Node attributeNode = attributes.item(i);
if (attributeNode instanceof Attr) {
Attr attribute = (Attr) attributeNode;
TypeDescriptor typeDescriptor = complexType.attributeForName(attribute.getName());
if (typeDescriptor != null) {
element.setAttributeNS(typeDescriptor.getNamespaceURI(), typeDescriptor.getName(), attribute.getValue());
}
}
}
}
}
Node child = documentElement.getFirstChild();
while (child != null) {
element.appendChild(element.getOwnerDocument().importNode(
child, true));
child = child.getNextSibling();
}
} else if (mimeType.equals("'application/octet-stream'")
&& dataValue instanceof byte[]) {
String encoded = Base64.encode((byte[]) dataValue);
element.appendChild(element.getOwnerDocument().createTextNode(
encoded));
} else {
element.appendChild(element.getOwnerDocument().createTextNode(
dataValue.toString()));
}
}
/**
* Provides the mime type for a given input
*
* @param inputName
* @return
*/
protected String getMimeTypeForInputName(String inputName) {
for (TypeDescriptor desc : inputDescriptors) {
if (desc.getName().equals(inputName))
return desc.getMimeType();
}
return "";
}
/**
* Generates a map of all the namespaces for the operation and all of the
* types required to call the operation. Namesspace prefixes (the key) start
* with ns1 representing the operation, and continue incrementally for all
* additional namespaces (ns2, ns3 ... etc).
*
* @return
* @param inputs -
* List of input TypeDescriptor's
* @throws UnknownOperationException
* @throws IOException
*/
protected Map<String, String> generateNamespaceMappings(List inputs)
throws UnknownOperationException, IOException {
Map<String, String> result = new HashMap<String, String>();
int nsCount = 2;
result.put(getOperationNamespace(), "ns1");
result.put("http://www.w3.org/2001/XMLSchema", "xsd");
result.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
for (Iterator iterator = inputs.iterator(); iterator.hasNext();) {
TypeDescriptor descriptor = (TypeDescriptor) iterator.next();
nsCount = mapNamespace(descriptor, new ArrayList<TypeDescriptor>(),result, nsCount);
}
return result;
}
/**
* creates a namespace prefix and adds the namespace to the namespaceMap for
* a TypeDescriptor. Further recursive calls are made if this type contains
* addition inner elements that are not already mapped.
*
* @param descriptor
* @param namespaceMap
* @param nsCount
* @return
*/
protected int mapNamespace(TypeDescriptor descriptor, List<TypeDescriptor> visitedDescriptors,
Map<String, String> namespaceMap, int nsCount) {
if (!visitedDescriptors.contains(descriptor)) {
visitedDescriptors.add(descriptor);
String namespace = descriptor.getNamespaceURI();
if (namespace != null && namespace.length() > 0
&& !namespaceMap.containsKey(namespace)) {
namespaceMap.put(namespace, "ns" + nsCount);
nsCount++;
}
if (descriptor instanceof ArrayTypeDescriptor) {
nsCount = mapNamespace(((ArrayTypeDescriptor) descriptor)
.getElementType(),visitedDescriptors, namespaceMap, nsCount);
} else if (descriptor instanceof ComplexTypeDescriptor) {
List elements = ((ComplexTypeDescriptor) descriptor).getElements();
for (Iterator iterator = elements.iterator(); iterator.hasNext();) {
nsCount = mapNamespace((TypeDescriptor) iterator.next(),visitedDescriptors,
namespaceMap, nsCount);
}
}
}
else {
logger.error("The descriptor: "+descriptor+" is appears to be part of a cyclic schema. Bailing out of mapping namespace.");
}
return nsCount;
}
}