SOAP and Web Services¶
Acrobat 7.0 and later provides support for the SOAP 1.1 and 1.2 standards in order to enable PDF forms to communicate with web services. This support has made it possible to include both SOAP header and body information, send binary data more efficiently, use document/literal encoding, and facilitate authenticated or encrypted communications. In addition, it provides the ability to locate network services using DNS Service Discovery. All of this makes it possible to integrate PDF files into existing workflows by binding XML forms to schemas, databases, and web services. These workflows include the ability to share comments remotely or invoke web services through form field actions.
Note
Acrobat’s SOAP implementation cannot handle faults that follow the SOAP 1.2 specification. Acrobat will not trigger an error when it receives a SOAP 1.2 response containing a failure.
Note
Beginning with version 8, the SOAP object is deprecated, though support will continue. Use the Net.SOAP
when developing any new web services. See the documentation of the Net
object in the Acrobat JavaScript API Reference for details.
Using SOAP and web services¶
JavaScript for Acrobat provides a SOAP
object that encapsulates the support for web services and service discovery. Through the usage of this object, you can extend and customize your workflows by engaging in XML-based communication with remote servers.
The SOAP
object has a wireDump
property that sends all XML requests and responses to the JavaScript Console for debugging purposes. In addition, the SOAP
object provides the methods described in the following table.
SOAP object
Method |
Description |
---|---|
connect |
Obtains a WSDL proxy object used to invoke a web service. |
queryServices |
Locates network services using DNS Service Discovery. |
resolveService |
Binds a service name to a network address and port. |
request |
The principal method used to invoke a web service. |
response |
A callback method used in asynchronous web method calls. |
streamDecode |
Decodes a Base64 or Hex stream object. |
streamEncode |
Applies Base64 or Hex encoding to a stream object. |
streamFromString |
Converts a string to a stream object. |
stringFromStream |
Converts a stream object to a string. |
Using a WSDL proxy to invoke a web service¶
When connecting to a web service, your JavaScript code may use a WSDL proxy object to generate a SOAP envelope. The envelope contains a request for the server to run a web method on behalf of the client. To obtain the WSDL proxy object, invoke the SOAP
object’s connect
method, which accepts a single parameter containing the HTTP or HTTPS URL of the WSDL document.
The returned proxy object contains the web methods available in the web service described by the WSDL document. If the web service uses SOAP/RPC encoding, the parameters in the proxy object’s methods will match the order specified in the WSDL document. If the web service uses document/literal encoding, the methods will accept a single parameter describing the request message.
In the example shown below, a connection to a web service will be established, and its RPC-encoded web methods will be invoked. Assume that myURL
contains the address of the WSDL document:
// Obtain the WSDL proxy object:
var myProxy = SOAP.connect(myURL);
// Invoke the echoString web service, which requires a string parameter:
var testString = "This is a test string.";
var result = myProxy.echoString(testString);
// Display the response in the console:
console.println("Result is " + result);
// Invoke the echoInteger web service, which requires an integer parameter:
// Since JavaScript does not support XSD-compliant integer values,
// we will create an integer object compliant with the XSD standard:
var myIntegerObject = {
soapType: "xsd:int",
soapValue: "10"
};
var result = myProxy.echoInteger(myIntegerObject);
// Display the response in the console:
console.println("Result is " + result);
Note that each call to a web method generates a SOAP envelope that is delivered to the web service, and that the return value is extracted from the corresponding envelope returned by the web service. Also, since XML relies on text, there is no problem sending a string to the web service. In the case of integers, however, it is necessary to create an XSD-compliant object to represent the integer value. JavaScript for Acrobat does support some of the standard data types specified in the XSD. These are shown in the following table.
XSD-compliant data types supported in JavaScript
JavaScript type |
Equivalent XSD-compliant type |
---|---|
String |
xsd:string |
Number |
xsd:float |
Date |
xsd:dateTime |
Boolean |
xsd:boolean |
ReadStream |
SOAP-ENC:base64 |
Array |
SOAP-ENC:Array |
Synchronous and asynchronous information exchange¶
The SOAP
object request
method may be used to establish either synchronous or asynchronous communication with a web service, and provides extensive support for SOAP header information, firewall support, the type of encoding used, namespace qualified names, compressed or uncompressed attachments, the SOAP protocol version to be used, authentication schemes, response style, and exception handling.
The request
method accepts the parameters shown in the following table.
Parameter |
Description |
---|---|
cURL |
URL for SOAP HTTP endpoint. |
oRequest |
Object containing RPC information. |
oAsync |
Object used for asynchronous method invocation. |
cAction |
SOAPAction header used by firewalls and servers to filter requests. |
bEncoded |
Indicates whether SOAP encoding is used. |
cNamespace |
Message schema namespace when SOAP encoding is not used. |
oReqHeader |
SOAP header to be included with request. |
oRespHeader |
SOAP header to be included with response. |
cVersion |
SOAP protocol version to be used (1.1 or 1.2). |
oAuthenticate |
Authentication scheme. |
cResponseStyle |
The type and structure of the response information (JS, XML, Message). |
cRequestStyle |
Indicates how oRequest is interpreted. |
cContentType |
Specifies the HTTP content-type header. |
Establishing a synchronous connection¶
The SOAP
object request
method may be used to establish a synchronous connection with a web service. To establish the connection and invoke the web methods, it is necessary to provide the cURL
, oRequest
, and cAction
parameters. The example below demonstrates how to invoke the same web services used in the previous example.
Similar to the parameter used in the connect
method, the cURL
parameter contains the URL for the WSDL document. For the purposes of our example, assume that myURL
represents the WSDL document location.
The oRequest
parameter is a fully qualified object literal specifying both the web method name and its parameters, in which the namespace is separated from the method name by a colon. It may also contain the following properties:
soapType
: the SOAP type used for the valuesoapValue
: the SOAP value used when generating the messagesoapName
: the element name used when generating the SOAP messagesoapAttributes
: an object containing the XML attributes in the request nodesoapQName
: the namespace-qualified name of the request nodesoapAttachment
: determines whethersoapValue
is encoded as an attachment according to the SwA/MTOM specification. In this case,soapValue
will be a stream.
Assume that the namespace is http://www.example.com/methods, the method name is echoString
, and it accepts a string parameter called inputString
. The following code represents the oRequest
parameter:
var echoStringRequest = {
"http://www.example.com/methods:echoString {
inputString: "This is a test."
}
};
The cAction
parameter contains header information described by the WSDL document that is used by firewalls to filter SOAP requests. In our example, we will supply the location of the WSDL document:
var mySOAPAction = "http://www.example.com/methods";
We may now invoke the echoString
web method:
var response = SOAP.request ({
cURL: myURL,
oRequest: echoStringRequest,
cAction: mySOAPAction
});
In the case of synchronous requests such as this one, the value returned by the request
method (response
in this example) is an object containing the result of the web method, which will be one of the JavaScript types corresponding to XSD types. The default format for the response value is an object describing the SOAP body (or header) of the returned message.
Note
In the case of base64 or hex encoding of binary information, the type returned will be a readStream
object.
We may now obtain the returned value by using syntax that corresponds to the SOAP body sent back by the web method:
var responseString = "http://www.example.com/methods:echoStringResponse";
var result = response[responseString]["return"];
// Display the response in the console:
console.println("Result is " + result);
Similarly, we can invoke the echoInteger
method. To do this, we will use the same value for the cURL
and cAction
parameters, and develop an oRequest
parameter like the one we used for the echoString
method. In this case, however, we must supply an XSD-compliant object to represent the integer value:
var myIntegerObject = {
soapType: "xsd:int",
soapValue: "10"
};
var echoIntegerRequest = {
"http://www.example.com/methods:echoInteger {
inputInteger: myIntegerObject
}
};
Now we may invoke the echoInteger
web method and display its results:
var response = SOAP.request ({
cURL: myURL,
oRequest: echoIntegerRequest,
cAction: mySOAPAction
});
var responseString = "http://www.example.com/methods:echoIntegerResponse";
var result = response[responseString]["return"];
// Display the response in the console:
console.println("Result is " + result);
Asynchronous web service calls¶
The SOAP
object request
method may be used in conjunction with the response
method to establish asynchronous communication with a web service. In this case, the request
method calls a method on the notification object, and does not return a value.
Asynchronous communication is made possible by assigning a value to the request
method’s aSync
parameter, which is an object literal that must contain a function called response
that accepts two parameters: oResult
(the result object) and cURI
(the URI of the requested HTTP endpoint).
In the example below, the aSync
parameter named mySync
contains an attribute called isDone
, which is used to monitor the status of the web service call, and an attribute called val
which will contain the result of the web service call. When the response
function is called by the web service, it sets isDone
to true
indicating that the asynchronous call has been completed:
// Create the aSync parameter:
var mySync = {
isDone: false,
val: null,
// Generates the result of the web method:
result: function(cMethod)
{
this.isDone = false;
var name = "http://www.example.com/methods/:" + cMethod + "Response";
if (typeof this.val[name] == "undefined")
return null;
else
return this.val[name]["return"];
},
// The method called by the web service after completion:
response: function(oResult, cURI)
{
this.val = oResult;
this.isDone = true;
},
// While the web service is not done, do something else:
wait: function()
{
while (!this.isDone) doSomethingElse();
}
};
// Invoke the web service:
SOAP.request({
cURL: myURL,
oRequest: echoIntegerRequest,
oAsync: mySync,
cAction: mySOAPAction
});
// The response callback function could contain the following code:
// Handle the asynchronous response:
var result = mySync.result("echoInteger");
// Display the response in the console:
console.println("Result is " + result);
Using document/literal encoding¶
You can use document/literal encoding in your SOAP messages by assigning values to the following parameters of the request
method:
bEncoded
: Assign a value offalse
to this parameter.cNamespace
: Specify a namespace for the message schema.
SOAPMessageStyle.JS
, SOAPMessageStyle.XML
, orSOAPMessageStyle.Message
value to this parameter.Once this is done, fill the oRequest
parameter with an object literal containing the data. An example is given below:
// Set up two integer values to be added in a web method call:
var aInt = {soapType: "xsd:int", soapValue: "10"};
var bInt = {soapType: "xsd:int", soapValue: "4"};
// Set up the document literal:
var req = {};
req["Add"] = {a: aInt, b: bInt};
// Invoke the web service:
var response = SOAP.request({
cURL: myURL,
oRequest: req,
cAction: mySOAPAction,
bEncoded: false,
cNamespace: myNamespace,
cResponseStyle: SOAPMessageStyle.Message
});
// Display the response to the console:
var value = response[0].soapValue[0].soapValue;
console.println("ADD(10 + 4) = " + value);
Exchanging file attachments and binary data¶
As you learned earlier, the oRequest
parameter provides alternative options for sending binary-encoded data. This may be useful for sending information such as serialized object data or embedded images. You can embed binary information in text-based format in the SOAP envelope by using base64 encoding, or take advantage of the Soap With Attachments (SwA) standard or the Message Transmission Optimization Mechanism (MTOM) to send the binary data in a more efficient format. Both SwA and MTOM can significantly reduce network transmission time, file size, and XML parsing time.
SwA can be used by setting the oRequest
parameter soapAttachment
value to true
, as shown in the example below. Assume myStream
is a readStream
object containing binary data:
// Use the SwA standard:
var SwARequest = {
"http://www.example.com/methods:echoAttachment": {
dh:
{
soapAttachment: true,
soapValue: myStream
}
}
};
var response = SOAP.request ({
cURL: myURL,
oRequest: SwARequest
});
var responseString = "http://www.example.com/methods:echoAttachmentResponse";
var result = response[responseString]["return"];
MTOM is used by additionally setting the request
method’s bEncoded
parameter to false
and the cNamespace
parameter to an appropriate value. This is illustrated in the following code, which creates an oRequest
object:
// Use the MTOM standard:
var MTOMRequest = {
"echoAttachmentDL": {
dh:
{
inclusion:
{
soapAttachment: true,
soapValue: myStream
}
}
}
};
var response = SOAP.request({
cURL: myURL,
oRequest: MTOMRequest,
bEncoded: false,
cNamespace: myNamespace
});
Converting between string and readstream information¶
The SOAP
object streamFromString
and stringFromStream
methods are useful for converting between formats. The streamFromString
method is useful for submitting data in a web service call, and the stringFromStream
method is useful for examining the contents of a response returned by a web service call. An example is shown below:
// Create a ReadStream object from an XML string:
var myStream = streamFromString("<mom name = 'Mary'></mom>");
// Place the information in an attachment:
this.setDataObjectContents("org.xml", myStream);
// Convert the ReadStream object back to a string and display in console:
console.println(stringFromStream(myStream));
Accessing SOAP version information¶
Acrobat 7.0 and later provides improved support for SOAP Version 1.1 and support for Version 1.2. To encode the message using a specific version, assign one of the following values to the request
method’s cVersion
parameter: SOAPVersion.version_1_1
(SOAP Version 1.1) or SOAPVersion.version_1_2
(SOAP Version 1.2). Its usage is shown in the following example:
var response = SOAP.request ({
cURL: myURL,
oRequest: myRequest,
cVersion: SOAPVersion.version_1_2
});
Accessing SOAP header information¶
You can send SOAP header information to the web service using the request
method’s oReqHeader
parameter, and access the returned header information using the oRespHeader
parameter. The oReqHeader
is identical to the oRequest
object, with the addition of two attributes:
soapActor
: the SOAP actor that should interpret the headersoapMustUnderstand
: determines whether the SOAP actor must understand the header contents
Their usage is shown in the following example:
// Set up the namespace to be used:
var myNamespace = "http://www.example.com/methods/:";
// Create the oReqHeader parameter:
var sendHeader = {};
sendHeader[myNamespace + "testSession"] = {
soapType: "xsd:string",
soapValue: "Header Test String"
};
// Create the intially empty oRespHeader parameter:
var responseHeader = {};
responseHeader[myNamespace + "echoHeader"] = {};
// Exchange header information with the web service:
var response = SOAP.request({
cURL: myURL,
oRequest: {},
cAction: "http://www.example.com/methods",
oReqHeader: sendHeader,
oRespHeader: responseHeader
});
Authentication¶
You can use the request
method’s oAuthenticate
parameter to specify how to handle HTTP authentication or provide credentials used in Web Service Security (WS-Security). Normally, if authentication is required, an interface will handle HTTP authentication challenges for BASIC and DIGEST authentication using the SOAP header, thus making it possible to engage in encrypted or authenticated communication with the web service. This parameter helps to automate the authentication process.
The oAuthenticate
parameter contains two properties:
Username
: A string containing the usernamePassword
: A string containing the authentication credential
Its usage is shown in the following example:
// Create the oAuthenticate object:
var myAuthentication = {
Username: "myUsername",
Password: "myPassword"
};
// Invoke the web service using the username and password:
var response = SOAP.request ({
cURL: myURL,
oRequest: echoStringRequest,
cAction: mySOAPAction
oAuthenticate: myAuthentication
});
Error handling
The SOAP
object provides extensive error handling capabilities within its methods. In addition to the standard JavaScript for Acrobat exceptions, the SOAP
object also provides SOAPError
and NetworkError
exceptions.
A SOAPError
exception is thrown when the SOAP endpoint returns a SOAPFault. The SOAPError
exception object will include information about the SOAP Fault code, the SOAP Actor, and the details associated with the fault. The SOAP
object’s connect
and request
methods support this exception type.
A NetworkError
exception is thrown when there is a problem with the underlying HTTP transport layer or in obtaining a network connection. The NetworkError
exception object will contain a status code indicating the nature of the problem. The SOAP
object’s connect
, request
, and response
methods support this exception type.
DNS service discovery¶
Suppose the exact URL for a given service is not known, but that it is available locally because it has been published using DNS Service Discovery (DNS-SD). You can use the SOAP
object queryServices
and resolveService
methods to locate the service on the network and bind to it for communications.
The queryServices
method can locate services that have been registered using Multicast DNS (mDNS) for location on a local network link, or through unicast DNS for location within an enterprise. The method performs an asynchronous search, and the resultant service names can be subsequently bound to a network location or URL through the SOAP
object resolveService
method.
The queryServices
method accepts the following parameters:
cType
: The DNS SRV service name (such as “http” or “ftp”)oAsync
: A notification object used when services are located or removed (implementsaddServices
andremoveServices
methods). The notification methods accept a parameter containing the following properties:name
: the Unicode display name of the servicedomain
: the DNS domain for the servicetype
: the DNS SRV service name (identical tocType
)aDomains
: An array of domains for the query. The valid domains areServiceDiscovery.local
(searches the local networking link using mDNS) andServiceDiscovery.DNS
(searches the default DNS domain using unicast DNS).
An example of its usage is shown below:
// Create the oAsync notification object:
var myNotifications = {
// This method is called whenever a service is added:
addServices: function(services)
{
for (var i=0; i<services.length; i++) {
var str = "ADD: ";
str += services[i].name;
str += " in domain ";
str += services[i].domain;
console.println(str);
}
},
// This method is called whenever a service is removed:
removeServices: function(servces)
{
var str = "DEL: ";
str += services[i].name;
str += " in domain ";
str += services[i].domain;
console.println(str);
}
};
// Perform the service discovery:
SOAP.queryServices
({
cType: "http",
oAsync: myNotifications,
aDomains: [ServiceDiscovery.local, ServiceDiscovery.DNS]
});
Once a service has been discovered, it can be bound through the SOAP
object resolveService
method to a network address and port so that a connection can be established. The resolveService
method accepts the following parameters:
cType
: the DNS SRV service name (such as “http” or “ftp”).cDomain
: the domain in which the service is located.cService
: the service name to be resolved.oResult
: a notification object used when the service is resolved. It implements aresolve
method that accepts parametersnStatus
(0
if successful) andoInfo
(used if successful, contains aserviceInfo
object). TheserviceInfo
object contains the following properties:target
: the IP address or DNS name of the machine providing the service.port
: the port on the machine.info
: an object with name/value pairs supplied by the service.
Its usage is illustrated in the following example:
// Create the oAsync notification object:
var myNotification = {
// This method is called when the service is bound:
resolve: function(nStatus, oInfo)
{
// Print the location if the service was bound:
if (nStatus == 0){
var str = "RESOLVE: http://";
str += oInfo.target;
str += ":";
str += oInfo.port;
str += "/";
str += oInfo.info.path;
console.println(str);
}
// Display the error code if the service was not bound:
else
console.println("ERROR: " + nStatus);
}
};
// Attempt to bind to the service:
SOAP.resolveService({
cType: "http",
cDomain: "local.",
cService: "My Web Server",
oResult: myNotification
});
Managing XML-based information
JavaScript for Acrobat provides support for XML-based information generated within your workflows by providing an XMLData
object, which represents an XML document tree that may be manipulated via the XFA Data DOM. (For example, it is possible to apply an XSL transformation (XSLT) to a node and its children using the XFA
object). The XMLData
object provides two methods for manipulating XML documents:
parse
: Creates an object representing an XML document tree.applyXPath
: Permits the manipulation and query of an XML document via XPath expressions.
You can convert a string containing XML information into a document tree using the XMLData
object parse
method, and then manipulate and query the tree using its applyXPath
method.
The XMLData
object’s parse
method accepts two parameters:
param1
: A string containing the text in the XML document.param2
: A Boolean value that determines whether the root node should be ignored.
Its usage is illustrated below:
// XML string to be converted into a document tree:
myXML = "<family name = 'Robat'>
<mom id = 'm1' name = 'Mary' gender = 'F'>
<child> m2 </child>
<personal>
<income>75000</income>
</personal>
</mom>
<son id = 'm2' name = 'Bob' gender = 'M'>
<parent> m1 </parent>
<personal>
<income>35000</income>
</personal>
</son>
</family>";
// Generate the document tree:
var myTree = XMLData
.parse(myXML, false);
// Print mom's name:
console.println("Mom: " + myXML.family.mom.value);
// Change son's income:
myXML.family.son.personal.income.value = 40000;
The XMLData
object applyXPath
method accepts two parameters:
oXML
: An object representing the XML document tree.cXPath
: A string containing an XPath query to be performed on the document tree.
In the following example, assume that myXML
is the document tree obtained in the previous example:
// Obtain mom's information:
var momInfo = XMLData
.applyXPath(myXML, "//family/mom");
// Save the information to a string:
momInfo.saveXML('pretty');
// Give mom a raise:
momInfo.personal.income.value = "80000";
Workflow applications
Support for SOAP in JavaScript for Acrobat has a major impact on collaboration workflows. A SOAP-based collaboration server can be used to share comments remotely via a web-based comment repository. Through the DNS Service Discovery support, it is possible to enable dynamic discovery of collaboration servers, initiation workflows, and RSS feeds that can provide customized user interfaces, via XML, directly inside of Acrobat 7.0 or later.
In addition, it is possible to deploy web-based scripts that always maintain the most updated information and processes, and to connect to those scripts via form field actions that invoke web services.
In the following example, a form is submitted to a server using a SOAP-based invocation:
// Populate the content object with form and SOAP information:
var location = "http://example.com/Acrobat/Form/submit/"
var formtype = location + "encodedTypes:FormData";
var content = new Array();
for(var i = 0; i < document.numFields; i++) {
var name = document.getNthFieldName(i);
var field = document.getField(name);
content[i] = new Object();
content[i].soapType = formtype;
content[i].name = field.name;
content[i].val = field.value;
}
// Send the form to the server:
SOAP.request({
cURL: cURL,
oRequest: {
location + ":submitForm":
{
content: content
}
},
cAction: location + "submitForm"
}