All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.xfa.soap.SOAP Maven / Gradle / Ivy

There is a newer version: 2024.11.18598.20241113T125352Z-241000
Show newest version
/*
 * ADOBE CONFIDENTIAL
 *
 * Copyright 2008 Adobe Systems Incorporated
 * All Rights Reserved
 *
 * Notice: All information contained herein is, and remains the property of
 * Adobe Systems Incorporated and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Adobe Systems
 * Incorporated and its suppliers and may be covered by U.S. and Foreign
 * Patents, patents in process, and are protected by trade secret or copyright
 * law. Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained from
 * Adobe Systems Incorporated.
 */
package com.adobe.xfa.soap;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

import com.adobe.xfa.AppModel;
import com.adobe.xfa.Attribute;
import com.adobe.xfa.Chars;
import com.adobe.xfa.DOMSaveOptions;
import com.adobe.xfa.Document;
import com.adobe.xfa.Element;
import com.adobe.xfa.Node;
import com.adobe.xfa.protocol.HttpForm;
import com.adobe.xfa.ut.ExFull;
import com.adobe.xfa.ut.FindBugsSuppress;
import com.adobe.xfa.ut.ResId;


/**
 * SOAP is the class which allows sending and receiving of soap messages.
 * @exclude from published api.
 */
public class SOAP {
	public static final int SOAP_UNKNOWN = 0;
	public static final int SOAP_BODY = 1;
	public static final int SOAP_ENVELOPE = 2;
	public static final int SOAP_HEADER = 3;
	public static final int SOAP_FAULT = 4;

	private static final String BODY = "Body";
	private static final String ENVELOPE = "Envelope";
	private static final String ENVELOPE_NS = "http://schemas.xmlsoap.org/soap/envelope/";
	private static final String ENVELOPE_QUAL = "soap:Envelope";
	private static final String FAULT = "Fault";
	private static final String FAULT_ACTOR = "faultactor";
	private static final String FAULT_CODE = "faultcode";
	private static final String FAULT_DETAIL = "detail";
	private static final String FAULT_STRING = "faultstring";
	private static final String HEADER = "Header";

	private static final String[] mEnvelopeNamespaces = {
//		"xmlns:SOAP-ENV",   "SOAP-ENV",     "http://schemas.xmlsoap.org/soap/envelope/",
		"xmlns:xsi",        "xsi",          "http://www.w3.org/2001/XMLSchema-instance",
		"xmlns:xsd",        "xsd",          "http://www.w3.org/2001/XMLSchema",
		"xmlns:SOAP-ENC",   "SOAP-ENC",     "http://schemas.xmlsoap.org/soap/encoding/"
	};
	private static final int ENVELOPE_NS_STRINGS = 3;

	private Element mEnvelopeNode;
	private Element mBodyNode;
	private Element mHeaderNode;
	private Element mFaultNode;
	private Map mNamespaces;
//	private String msTargetNS;
	private Document mDomDocument;
	private String mLastErrorText;

	/**
	 * Creates a Soap model from a header stream and a body stream.
	 *
	 * @param headerStream - the stream that contains the XML data to load into the header.
	 * @param bodyStream - the stream that contains the XML data to load into the Body.
	 * @param loadOptions - (optional) loading options.
	 * @return The created SOAPModel
	 */
	public static SOAP createFromXMLStreams (InputStream headerStream, InputStream bodyStream, String loadOptions /* = "" */) {
// Load the stream into our DOM Document
		SOAP soap = new SOAP();
		Document document = null;
		Element envelope = null;
		try {
			AppModel appModel = new AppModel(null);
			document = appModel.getDocument();
			
			envelope = soap.createEnvelopeDomNode(document);
			appModel.appendChild(envelope);
			
			if (headerStream != null) {
				soap.createChildAndContentDomNode(document, HEADER, envelope, headerStream);
			}

			if (bodyStream != null) {
				soap.createChildAndContentDomNode(document, BODY, envelope, bodyStream);
			}
		}
		catch (ExFull oEx) {
// load (will give the error "XML parse error:
			int resId = oEx.firstResId();
			if (resId != ResId.EXPAT_ERROR) {
				throw (oEx); // XML parse error
			}
		}

// Note: This check is equivalent to what is in the C++ code.  In that
// code, the way the document is created, it is almost certainly not
// null, similar to here.  This behaviour is different from that in
// method loadFromStream(), where the document can be null in the C++
// code.  The Java implementation of that method returns null on catching
// a parser exception, to emulate the C++ behaviour.  It was probably not
// the intent of the original C++ coder to have inconsistent behaviour
// between these two methods, but it was felt safer to preserve it than
// change it.
		if (document == null) {
			return null;
		}

		soap.mDomDocument = document;
		soap.loadDocument(envelope);

		return soap;
	}

	/**
	 * Export the contents of a SOAP node (and its descendants) to XML.
	 * Note that this method is used by the test program.  It's unclear whether it has
	 * any other value in the public API.
	 * @param element SOAP node (element) to export.
	 * @param outputStream Output stream to receive the contents of the exported node.
	 * @return True if the node was a valid node and successfully exported.
	 * To be exported, the node must be a SOAP header or body element.
	 */
	public static boolean exportContentsToXML (Element element, OutputStream outputStream) {
		int nodeType = getNodeType (element);
		if ((nodeType != SOAP_HEADER) && (nodeType != SOAP_BODY)) {
			return false;
		}
		Element child = element.getFirstXMLChildElement();
		while (child != null) {
			if (child.getNS() != ENVELOPE_NS) {
				break;
			}
			if (child.getLocalName() == FAULT) {
				break;
			}
			child = child.getNextXMLSiblingElement();
		}
		if (child == null) {
			return false;
		}
		DOMSaveOptions saveOptions = new DOMSaveOptions();
		saveOptions.setDisplayFormat (DOMSaveOptions.PRETTY_OUTPUT);
		element.getOwnerDocument().saveAs (outputStream, child, saveOptions);
		return true;
	}

	/**
	 * Return the SOAP type of a given DOM node.
	 * @param node Node for which the type is desired.
	 * @return One of SOAP_ENVELOPE, SOAP_HEADER, SOAP_BODY, SOAP_FAULT
	 * or SOAP_UNKNOWN, depending on the node.
	 */
	public static final int getNodeType (Node node) {
		if (! (node instanceof Element)) {
			return SOAP_UNKNOWN;
		}
		Element element = (Element) node;
		String localName = element.getLocalName();
		if (localName == BODY) {
			return SOAP_BODY;
		} else if (localName == ENVELOPE) {
			return SOAP_ENVELOPE;
		} else if (localName == FAULT) {
			return SOAP_FAULT;
		} else if (localName == HEADER) {
			return SOAP_HEADER;
		}
		return SOAP_UNKNOWN;
	}

	/**
	 * Loads a Soap message into a model from a stream, and creates a
	 * hierarchy for it.
	 *
	 * @param stream - the stream that contains the data to load.
	 * @param loadOptions - (optional) loading options.
	 * @return The created SOAPModel
	 */
	public static SOAP loadFromStream (InputStream stream, String loadOptions) {
		SOAP soap = new SOAP();
		
		AppModel appModel = new AppModel(null);
		Document document = appModel.getDocument();

		Element bogusRoot = null;
		try {
			// Note: Used to call Document.load(), but had a hard-coded encoding.
			// Changed to the following two calls to defer encoding decisions to the
			// document (where it is currently hard-coded as UTF-8).
			bogusRoot = document.loadIntoDocument(stream);
		}
		catch (ExFull oEx) {
			int resId = oEx.firstResId();
			if (resId != ResId.EXPAT_ERROR) {
				throw (oEx);						// XML parse error
			}
		}

		if (bogusRoot == null)
			return null;
		
		// The first element child of bogusRoot should be an Envelope element
		Element envelope = bogusRoot.getFirstXMLChildElement();
		if (envelope == null)
			return null;
		
		appModel.appendChild(envelope);
		
		soap.mDomDocument = document;
		soap.loadDocument(envelope);
		
		return soap;
	}

	@FindBugsSuppress(code="ES")
	private static final Element getChildDomNode (Element node, String inNodeName) {
// first node should be the  node
// find the faultcode element:
		Element child = node.getFirstXMLChildElement();

		while (child != null) {
			if (child.getLocalName() == inNodeName) {
				return child;
			}
			child = child.getNextXMLSiblingElement();
		}

		return null;
	}

	private static final String getChildDomNodeTextValue (Element startNode) {
		Node child = startNode.getFirstXMLChild();
		while (child != null) {
			if (child instanceof Chars) {
				// just return the first text node's value
				Chars chars = (Chars) child;
				return chars.getText();
			}
			child = child.getNextXMLSibling();
		}

		return "";
	}

	private static final String getChildDomNodeValue (Element node, String inNodeName) {
// first node should be the  node
// find the faultcode element:
		Element child = getChildDomNode (node, inNodeName);
		return (child == null) ? "" : getChildDomNodeTextValue (child);
	}

	/**
	 * Default Constructor.
	 * Note that the caller cannot construct SOAP objects directly;
	 * it must use one of the static methods.
	 */
	private SOAP () {
	}

	/**
	 * Gets the element which is the SOAP:Body element
	 * @return the SOAP:Body element.
	 */
	public Element getBodyNode () {
		return mBodyNode;
	}

	/**
	 * Gets the element which is the Envelope element
	 * @return the SOAP:Envelope element.
	 */
	public Element getEnvelopeNode () {
		return mEnvelopeNode;
	}

	/**
	 * Return the fault actor node.
	 * @return The faultactor element from the SOAP operation; null if no fault actor.
	 */
	public Node getFaultActor () {
		return getChildDomNode (mFaultNode, FAULT_ACTOR);
	}

	/**
	 * Return the fault code string (value of faultcode element).
	 * @return The fault code from the SOAP operation; null if no fault code.
	 */
	public String getFaultCode () {
		return getChildDomNodeValue (mFaultNode, FAULT_CODE);
	}

	/**
	 * Return the fault detail string (value of faultdetail element).
	 * @return The fault detail from the SOAP operation; null if no fault detail.
	 */
	public Node getFaultDetail () {
		return getChildDomNode (mFaultNode, FAULT_DETAIL);
	}

	/**
	 * Return the SOAP fault node.
	 * @return The fault element from the SOAP operation; null if no fault element.
	 */
	public Element getFaultNode () {
		return mFaultNode;
	}

	/**
	 * Return the fault string (value of faultstring element).
	 * @return The fault string from the SOAP operation; null if no fault string.
	 */
	public String getFaultString () {
		return getChildDomNodeValue (mFaultNode, FAULT_STRING);
	}

	/**
	 * Gets the element which is the SOAP:Header element
	 * @return the SOAP:Header element.
	 */
	public Element getHeaderNode () {
		return mHeaderNode;
	}

	/**
	 * Get an error string from the last operation.
	 * @return Error string from the last operation; null if that operation succeeded.
	 */
	public String getLastError () {
		return mLastErrorText;
	}

	/**
	 * Writes the soap message into a stream.
	 * @param outputStream - the stream to which the message will be written.
	 */
	public void saveAs (OutputStream outputStream) {
		if (mEnvelopeNode != null && mDomDocument != null) {
			DOMSaveOptions saveOptions = new DOMSaveOptions();
			saveOptions.setDisplayFormat (DOMSaveOptions.PRETTY_OUTPUT);
			mDomDocument.saveAs(outputStream, mEnvelopeNode, saveOptions);
		}
	}

	/**
	 * Sends a SOAP message and returns the response.
	 * @param inSOAPAddress The HTTP address where this SOAP message will be sent.
	 * @param inSOAPAction The SOAP Action.
	 * @return SOAPModel which is the response.  If the message failed, this will contain the SOAP fault element
	 */
	public SOAP sendRequest (String inSOAPAddress, String inSOAPAction) {
		mLastErrorText = null;
		HttpForm httpForm = new HttpForm();

		ByteArrayOutputStream memStream = new ByteArrayOutputStream();
		saveAs (memStream);
		byte[] sent = memStream.toByteArray();
		memStream = null;

		httpForm.setEncodingType (HttpForm.PostEncodingType.USER_ENCODING);
		httpForm.addEncodedData (sent, "text/xml", "utf-8");
		sent = null;

// add the SOAP Action to the header data
		httpForm.addHeaderData ("SOAPAction", inSOAPAction);

		ExFull receivedException = null;
		String errorString = null;
		String stringReturnedByServer = null;
		SOAP responseModel = null;

		try {
// do the post
			httpForm.post (inSOAPAddress);
		}
		catch (ExFull exception) {
			if (exception.hasResId (ResId.PROTOCOL_ERR_SYS)) {
				int numExceptions = exception.count();
				for (int i = 0; i < numExceptions; i++) {
					int resID = exception.getResId (i);
					if (resID == ResId.PROTOCOL_ERR_POST) {
						errorString = exception.item(i).text();
					} 
					else if (resID == ResId.PROTOCOL_ERR_SYS) {
						stringReturnedByServer = exception.item(i).text();
					}
				}
				receivedException = exception;
			} 
			else {
				throw exception;
			}
		}

		byte[] response = null;
		
		if (errorString != null || stringReturnedByServer != null) {
			if (errorString != null) {
				mLastErrorText = errorString;
			}
			else if (stringReturnedByServer != null) {
				mLastErrorText = stringReturnedByServer;
			}
			
			if (stringReturnedByServer != null) {
				// FIXME: This assumes that if the response is XML that the XML Decl will
				// use an encoding of UTF-8, which is not necessarily true.
				try {
					response = stringReturnedByServer.getBytes("UTF-8");
				}
				catch (UnsupportedEncodingException ignored) {
					// not possible - UTF-8 is alway supported
				}
			}
		}
		else {
			response = httpForm.getResponse();
		}
		
		if (response != null) {			
			// create a SOAPModel from the response stream
			try {
				responseModel = loadFromStream (new ByteArrayInputStream(response), "");
			} 
			catch (ExFull loadException) {
// node: does not throw or assign receivedException in c++; simply returns null model
				receivedException = loadException;
			}
		}

		if (responseModel == null) {
// we were unable to create a SOAPModel from the response stream, and we got an exception
// when we did the post.  Chances are this was a Protocol error, so just throw.
			if (receivedException != null) {
				throw (receivedException);
			}
		}

		return responseModel;
	}

	@FindBugsSuppress(code="ES")
	private Node createChildAndContentDomNode (Document inDoc, String inNodeName, Element inParentNode, InputStream inContentStream) {
// use loadIntoDocument instead of load and importNode to speed up execution
		Element contentNode = inDoc.loadIntoDocument(inContentStream);
		
		// attach the first child, since the oContentNode will actually be a 'dummy' node.
		Node oContentChild = contentNode.getFirstXMLChild();	    
		
		// is this the node we want or is it just content for the node?		
		if (oContentChild instanceof Element &&
				((Element) oContentChild).getLocalName().equals(inNodeName)) {
			inParentNode.appendChild(oContentChild);
			return oContentChild;
		}
		else {
			Element oDomNode = inDoc.createElementNS(ENVELOPE_NS, inNodeName, inParentNode);
			oDomNode.appendChild(oContentChild);
			return oDomNode;
		}
	}
	
	private Element createEnvelopeDomNode(Document inDoc) {
		
		// use a qname with the "soap" prefix to match soap:Body generated in dataDescriptions
		// - at least some wsdl servers react badly to differing prefix usage for one namespace
		Element envelope = inDoc.createElementNS(ENVELOPE_NS, ENVELOPE_QUAL, null);
		
		for (int i = 0; i < mEnvelopeNamespaces.length; i+= ENVELOPE_NS_STRINGS) {
			envelope.setAttribute (null, mEnvelopeNamespaces[i], mEnvelopeNamespaces[i+1], mEnvelopeNamespaces[i+2]);
		}
		
		return envelope;
	}

	private boolean findFault (Element root) {
		Element child = root.getFirstXMLChildElement();
		while (child != null) {
			if (getNodeType (child) == SOAP_FAULT) {
				mFaultNode = child;
				return true;
			}
			if (findFault (child)) {
				return true;
			}
			child = child.getNextXMLSiblingElement();
		}
		return false;
	}

	private void loadDocument (Element startNode) {
		if (startNode == null || !startNode.getLocalName().equals(ENVELOPE))
			return;

		mEnvelopeNode = startNode;
		// go through the attributes to get the namespace URI definitions
		int numAttrs = mEnvelopeNode.getNumAttrs();
		for (int i = 0; i < numAttrs; i++) {
			Attribute attr = mEnvelopeNode.getAttr(i);
			// if an attribute's nodeName contains a ":", it is probably
			// a "xmlns:prefix" attribute where the localName is the
			// prefix and the nodeValue is the namespace URI
			if (attr.isNameSpaceAttr()) {
				if (mNamespaces == null)
					mNamespaces = new HashMap();
				mNamespaces.put(attr.getLocalName(), attr.getAttrValue());
			}
		}
		Element child = mEnvelopeNode.getFirstXMLChildElement();
		while (child != null) {
			switch (getNodeType (child)) {
			case SOAP_BODY:
				mBodyNode = child;
				findFault (child);
				break;
			case SOAP_FAULT:
				mFaultNode = child;
				break;
			case SOAP_HEADER:
				mHeaderNode = child;
				findFault (child);
				break;
			}
			child = child.getNextXMLSiblingElement();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy