Skip to main content

This CGIT instance is deprecated, and repositories have been moved to Gitlab or Github. See the repository descriptions for specific locations.

summaryrefslogblamecommitdiffstats
blob: af763ee598bde0a5233670825e4882b07cf55f6f (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
                                                                                
                                                       









                                                                                                  
                                                                                                                        
                                                                                                                                   

































































                                                                                                          


                                                                                                                















                                                                                                        
                                                             

                                                                 

































































































































































































































































                                                                                                                                                      
                                                                       










































































































































                                                                                                                                                                            
/*******************************************************************************
 * Copyright (c) 2007, 2009 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * IBM Corporation - initial API and implementation
 * yyyymmdd bug      Email and other contact information
 * -------- -------- -----------------------------------------------------------
 * 20070413   176493 makandre@ca.ibm.com - Andrew Mak, WSE: Make message/transport stack pluggable
 * 20081020   251269 mahutch@ca.ibm.com - Mark Hutchinson, WSE Sends Wrong Namespace for child elements of the SOAP body
 * 20091104   294260 mahutch@ca.ibm.com - Mark Hutchinson, WSE source view missing some namespace prefixes in SOAP response message
 *******************************************************************************/
package org.eclipse.wst.ws.internal.explorer.platform.wsdl.transport;

import java.io.IOException;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.wsdl.Operation;
import javax.wsdl.Part;
import javax.wsdl.extensions.ExtensibilityElement;
import javax.wsdl.extensions.soap.SOAPBody;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.axis.Constants;
import org.eclipse.wst.ws.internal.explorer.platform.util.XMLUtils;
import org.eclipse.wst.ws.internal.explorer.platform.wsdl.util.SoapHelper;
import org.eclipse.wst.ws.internal.explorer.transport.IDeserializer;
import org.eclipse.wst.ws.internal.explorer.transport.ISOAPMessage;
import org.eclipse.wst.ws.internal.explorer.transport.ISerializer;
import org.eclipse.wst.ws.internal.explorer.transport.MessageContext;
import org.eclipse.wst.ws.internal.explorer.transport.TransportException;
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;
import org.xml.sax.SAXException;

/**
 * This class provides implementation for WSE default SOAP transport's ISerializer and IDeserializer.
 */
public class SOAPMessageProcessor implements ISerializer, IDeserializer {
	
	private static final String LITERAL = "literal";
	private static final String DEFAULT_SOAP_ENCODING = "UTF-8";	
	private static final String LINE_SEPARATOR = System.getProperties().getProperty("line.separator");
	
	/*
	 * Constructor.
	 */
	SOAPMessageProcessor() {}
	
	/*
	 * Join the encodingStyles into a space separated string
	 */
	private String joinEncodingStyles(List list) {
		
		if (list.isEmpty())
			return null;
		
		StringBuffer sb = new StringBuffer();
		
		Iterator iter = list.iterator();
		while (iter.hasNext())
			sb.append(" ").append(iter.next());
		
		return sb.substring(1).toString();
	}
	
	/*
	 * Retrieves the encoding information from the binding for the selected operation.
	 * This method is called only for RPC style bindings.  Two pieces of information are returned:  
	 * This method returns the encoding namespace in element [0] 
	 * If the use is encoded then the encoding style(s) is returned in element [1].  If the use is literal, 
	 * then element[1] is returned null.
	 */
	private String[] getEncodingInfo(MessageContext context) {
		
		String[] info = new String[] { null, null };
		
		Iterator iter = context.getBindingOperation().getBindingInput()
			.getExtensibilityElements().iterator();
				
		// look for the soapbind:body extensilibity element
		while (iter.hasNext()) {
			ExtensibilityElement e = (ExtensibilityElement) iter.next();			
			if (!(e instanceof SOAPBody))
				continue;
			
			SOAPBody soapBody = (SOAPBody) e;
			
			info[0] = soapBody.getNamespaceURI();
			// use="encoded"
			if (!LITERAL.equals(soapBody.getUse())) {
				info[1] = joinEncodingStyles(soapBody.getEncodingStyles());
			}
					
			break;
		}
		
		// namespace not specified on the soapbind:body element, use the definition's
		if (info[0] == null)
			info[0] = context.getDefinition().getTargetNamespace();
		
		return info;
	}
	
	/*
     * WS-I: In a rpc-literal SOAP binding, the serialized child element of the 
     * soap:Body element consists of a wrapper element, whose namespace is the value 
     * of the namespace attribute of the soapbind:body element and whose local name is 
     * either the name of the operation or the name of the operation suffixed 
     * with "Response". The namespace attribute is required, as opposed to being 
     * optional, to ensure that the children of the soap:Body element are namespace-
     * qualified.
     */
	private Element createRPCWrapper(Document document, MessageContext context, Hashtable namespaceTable) {
		
		String encodingNamespaceURI = getEncodingInfo(context)[0];
		
		return SoapHelper.createRPCWrapperElement(
				document, 
				namespaceTable, 
				encodingNamespaceURI, 
				context.getBindingOperation().getOperation().getName(),
				null);
	}
	
	/*
	 * Initializes the contents of new ISOAPMessage.
	 */
	void initMessage(ISOAPMessage message) throws TransportException {
	
		try {
			Document document = XMLUtils.createNewDocument(null);
			
			Hashtable namespaceTable = new Hashtable();
			SoapHelper.addDefaultSoapEnvelopeNamespaces(namespaceTable);
			
			message.setEnvelope(SoapHelper.createSoapEnvelopeElement(document, namespaceTable));
			message.setHeader(SoapHelper.createSoapHeaderElement(document));
			
			Element body = SoapHelper.createSoapBodyElement(document);
			if (!message.getMessageContext().isDocumentStyle()) {
				Element rpcWrapper = createRPCWrapper(document, message.getMessageContext(), namespaceTable);
				body.appendChild(rpcWrapper);
			}			
			message.setBody(body);			
			
			message.setNamespaceTable(namespaceTable);
		}
		catch (ParserConfigurationException e) {
			throw new TransportException(e);
		}		
	}
	
	/*
	 * Returns the first element with the given local name from the soap envelope namespace
	 * This method can be used on elements that are not namespace aware, but we have to give
	 * it a namespace table to look up prefixes.
	 */
	private Element getSOAPElement(Element root, String localName, Map namespaceTable) {
		String prefix = (String) namespaceTable.get(Constants.URI_SOAP11_ENV);
		if (prefix == null)
			return null;
		
		NodeList list = root.getElementsByTagName(prefix + ":" + localName);
		if (list.getLength() == 0)
			return null;
		
		return (Element) list.item(0);
	}
	
	/*
	 * Returns the first element with the given local name from the soap envelope namespace.
	 * This version assume the elements are namespace aware.
	 */
	private Element getSOAPElementNS(Element root, String localName) {
		NodeList list = root.getElementsByTagNameNS(Constants.URI_SOAP11_ENV, localName);
		if (list.getLength() == 0)
			return null;		
	
		return (Element) list.item(0);
	}
	
	/*
	 * Convert a NodeList to a vector of elements, nodes which are not elements are skipped.
	 */
	private Vector toElementVector(NodeList nodes) {
		
		Vector vector = new Vector();
		
		for (int i = 0; i < nodes.getLength(); i++) {
			Node node = nodes.item(i);
			if (node instanceof Element)
		    	vector.add(node);		    
		}
		
		return vector;	
	}
	
	/*
	 * Convert a NodeList to an array of elements, nodes which are not elements are skipped.
	 */
	private Element[] toElementArray(NodeList nodes) {
		Vector vector = toElementVector(nodes);
		Element[] elements = new Element[vector.size()];
		vector.copyInto(elements);
		return elements;
	}
	
	// Serialize ////////////////////////////////////////////////////////////////////////////////////////////////////////////	
	
	/*
	 * adds the encoding style attribute to the each element in the given array.
	 * noop if encodingStyle is null
	 */
	private void addEncodingStyle(Element[] elements, String encodingStyle) {
				
		// encodingStyle is non-null only if use="encoded"
		if (elements == null || encodingStyle == null)
			return;
		
		for (int i=0; i < elements.length; i++) {
	        if (elements[i] == null)
	        	continue;
	    	elements[i].setAttribute(
	    			Constants.NS_PREFIX_SOAP_ENV + ":" + Constants.ATTR_ENCODING_STYLE, encodingStyle);			
		}
	}
	
	/*
	 * Serialize an array of elements
	 */
	private String serializeElements(Element[] elements) {
	
		StringBuffer buffer = new StringBuffer();
		
		for (int i = 0; i < elements.length; i++) {
					
			String serializedFragment = XMLUtils.serialize(elements[i], true);
			if (serializedFragment == null) {
				// On Some JRE's (Sun java 5) elements with an attribute with the xsi
				// prefix do not serialize properly because the namespace can not
				// be found so the string returned comes back as null. To workaround
				// this problem try adding in the namespace declaration attribute
				// and retry the serialization (bug 144824)			 
				elements[i].setAttribute("xmlns:xsi", Constants.URI_2001_SCHEMA_XSI);
				serializedFragment = XMLUtils.serialize(elements[i], true);
			}
			
			buffer.append(serializedFragment);
			buffer.append(LINE_SEPARATOR);
		}
		
		return buffer.toString();
	}

	/*
	 * Determines if the given message is a request message using RPC style
	 */
	private boolean isRPCRequest(ISOAPMessage message) {
		return 
			!message.getMessageContext().isDocumentStyle() &&							// rpc style
			!Boolean.TRUE.equals(message.getProperty(SOAPTransport.PROP_READ_ONLY));	// not read-only
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.wst.ws.internal.explorer.transport.ISerializer#serialize(int, org.eclipse.wst.ws.internal.explorer.transport.ISOAPMessage)
	 */
	public String serialize(int part, ISOAPMessage message) throws TransportException {

		switch (part) {
			case ISOAPMessage.ENVELOPE:
				Element envelope = message.getEnvelope(true);
				
				if (isRPCRequest(message)) {					
					Element body = getSOAPElementNS(envelope, Constants.ELEM_BODY);
					if (body == null)
						body = getSOAPElement(envelope, Constants.ELEM_BODY, message.getNamespaceTable());
					
					if (body != null) {
						Element[] bodyContent = toElementArray(body.getFirstChild().getChildNodes());
						addEncodingStyle(bodyContent, getEncodingInfo(message.getMessageContext())[1]);
					}
				}
				
				String xml = XMLUtils.serialize(envelope, true);
				if (xml == null)
					xml = new String((byte[]) message.getProperty(SOAPTransport.PROP_RAW_BYTES));
				
				return xml;
				
			case ISOAPMessage.HEADER_CONTENT:
				return serializeElements(message.getHeaderContent());
				
			case ISOAPMessage.BODY_CONTENT:				
				Element[] bodyContent = message.getBodyContent();
				
				if (isRPCRequest(message))
					addEncodingStyle(bodyContent, getEncodingInfo(message.getMessageContext())[1]);
				
				return serializeElements(bodyContent);
		}
		
		return "";
	}
	
	// Deserialize //////////////////////////////////////////////////////////////////////////////////////////////////////////		
		
	/*
	 * Retrieve the soap header from the envelope and populate the given message
	 */
	private void processHeader(Element envelope, ISOAPMessage message) {
		
		Element header = getSOAPElementNS(envelope, Constants.ELEM_HEADER);
		if (header == null)
			return;
		
		message.setHeader(header);
		message.setHeaderContent(toElementArray(header.getChildNodes()));
	}
	
	/*
	 * HACK - The root element tag name of the instance document
	 * is ambiguous.  It lands on a very grey area between the SOAP
	 * spec and the WSDL spec.  The two specs do not explicitly define
	 * that the root element tag name must match the name of the
	 * WSDL part.  The hack is to treat elements with different tag names
	 * as instances of the WSDL part.
	 */
	private Element[] fixSOAPResponse(NodeList instanceList, MessageContext context) {
		
		Vector instanceVector = toElementVector(instanceList);		
		Element[] instanceDocuments = new Element[instanceVector.size()];
		
		Operation oper = context.getBindingOperation().getOperation();		
		Map partsMap = oper.getOutput().getMessage().getParts();
		
		if (partsMap.size() == 1) {
			Iterator it = partsMap.values().iterator();
			Part part = (Part) it.next();
			
			String fragName;
			if (part.getElementName() != null)
				fragName = part.getElementName().getLocalPart();
			else
				fragName = part.getName();
		    
		    for (int i = 0; i < instanceVector.size(); i++) {
		    	Element element = (Element) instanceVector.get(i);
		    	
		    	if (!element.getLocalName().equals(fragName)) {
		    		Document doc = element.getOwnerDocument();
		    		NodeList children = element.getChildNodes();
		    		NamedNodeMap attributes = element.getAttributes();
		    		element = doc.createElement(fragName);
		    		
		    		for (int j = 0; j < children.getLength(); j++) {
		    			if (children.item(j) != null) {
		    				element.appendChild(children.item(j));
				            // When you append a node from one element to another,
				            // the original element will lose its reference to this node,
				            // therefore, the size of the node list will decrease by 1.
				            j--;
		    			}
		    		}
		    		
		    		for (int j = 0; j < attributes.getLength(); j++) {
		    			Object attr = attributes.item(j);
		    			if (attr != null && (attr instanceof Attr)) {
		    				Attr attribute = (Attr)attr;
		    				element.setAttribute(attribute.getName(), attribute.getValue());
		    			}
		    		}
		    	}
		    	instanceDocuments[i] = element;
		    }
		}
		else
			instanceVector.copyInto(instanceDocuments);
		
		return instanceDocuments;
	}
	
	/*
	 * Retrieve the soap header from the envelope and populate the given message
	 */
	private void processBody(Element envelope, ISOAPMessage message) {
		
		Element body = getSOAPElementNS(envelope, Constants.ELEM_BODY);
		if (body == null)
			return;				
		
		message.setBody(body);
		
		// check for soap fault
		Element fault = getSOAPElementNS(body, Constants.ELEM_FAULT);
	    if (fault != null) {
	    	message.setFault(fault);
	    	return;
	    }
	        	        
	    NodeList instanceList;
	    
	    if (message.getMessageContext().isDocumentStyle())
	    	instanceList = body.getChildNodes();
	    else {
	        NodeList rpcWrapper = body.getElementsByTagNameNS("*", 
	        		message.getMessageContext().getBindingOperation().getOperation().getOutput().getMessage().getQName().getLocalPart());

	        /*
	        * HACK - Some of the web services out on the internet do not
	        * set their RPC wrapper properly.  It should be set to the output
	        * message name of the selected operation.  The hack is to
	        * assume the first element inside the body element is the
	        * RPC wrapper.
	        */
	        if (rpcWrapper.getLength() <= 0)
	        	rpcWrapper = body.getElementsByTagNameNS("*", "*");

	        if (rpcWrapper.getLength() > 0)
	        	instanceList = rpcWrapper.item(0).getChildNodes();
	        else
	        	return;
	    }
	    
	    message.setBodyContent(fixSOAPResponse(instanceList, message.getMessageContext()));
	}
	
	/*
	 * Deserialize the byte array into the given soap message.  The part parameters tells
	 * the method whether the input is the whole envelop or just the contents of the
	 * header or body. 
	 */
	void deserialize(int part, byte[] xml, ISOAPMessage message) throws		
		ParserConfigurationException,
		SAXException,
		IOException {
		
		Element root = XMLUtils.byteArrayToElement(xml, true);		
		
		switch (part) {
			case ISOAPMessage.ENVELOPE:				 
				message.setEnvelope(root);
				processHeader(root, message);
				processBody(root, message);
				break;
			case ISOAPMessage.HEADER_CONTENT:
				message.setHeaderContent(toElementArray(root.getChildNodes()));
				break;
			case ISOAPMessage.BODY_CONTENT:
				message.setBodyContent(toElementArray(root.getChildNodes()));
				break;
		}
	}
	
	/*
	 * Tack on a root element the string which represents a series of elements.
	 * This is needed to deserialize the string.
	 */
	private String addRootElement(String xml, Map namespaceTable) {
		StringBuffer sb = new StringBuffer();
	    sb.append("<root");
	    
	    Iterator iter = namespaceTable.keySet().iterator();
	    while (iter.hasNext()) {
	    	Object uri = iter.next();
	    	Object prefix = namespaceTable.get(uri);
	    	sb.append(" ").append("xmlns:").append(prefix).append("=\"").append(uri).append("\"");
	    }	    
	    
	    sb.append(">").append(xml).append("</root>");
	    return sb.toString();
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.wst.ws.internal.explorer.transport.IDeserializer#deserialize(int, java.lang.String, org.eclipse.wst.ws.internal.explorer.transport.ISOAPMessage)
	 */
	public void deserialize(int part, String xml, ISOAPMessage message) throws TransportException {
		
		if (part != ISOAPMessage.ENVELOPE)
			xml = addRootElement(xml, message.getNamespaceTable());
		
		try {
			deserialize(part, xml.getBytes(DEFAULT_SOAP_ENCODING), message);
		}
		catch (Exception e) {
			throw new TransportException(e);
		}
	}
}

Back to the top