com.adobe.xfa.soap.SOAP Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
/*
* 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();
}
}
}