This chapter describes the mechanism for calling SOAP services from the BeVocal VoiceXML interpreter. It describes the binding between the JavaScript interpreter in VoiceXML and the SOAP services. It also documents the method by which the interpreter locates SOAP services.
The interpreter's SOAP API takes advantage of the dynamic nature of JavaScript. There is no need to create separate proxy classes for each SOAP service you wish to call, because the interpreter can perform the conversion between JavaScript and SOAP types and methods on the fly. This makes SOAP much easier to deal with than in most other languages.
This chapter has the following sections:
| | Locating and Identifying SOAP Services |
| | Calling SOAP Methods |
Note: The SOAP Client facility currently supports SOAP services in which messages are RPC-oriented. The facility does not currently support SOAP services in which messages are document-oriented; this will be supported in a future release. For a description of how these message styles differ, refer to http://www.w3.org/TR/wsdl#_soap:body.
Note: The SOAP Client facility is an experimental extension to VoiceXML; its implementation and behavior are subject to change. The current BeVocal VoiceXML implementation contains the feature before it has been standardized so that developers may provide feedback. If this capability becomes a standard part of a future version of VoiceXML, the BeVocal VoiceXML implementation will change as necessary to match the VoiceXML standard.
There is a small API for creating JavaScript objects that act as proxies for SOAP services. It is exposed through the JavaScript object bevocal.soap, which resides in the session scope and has 3 methods. For details on these methods, see Chapter 14, JavaScript Functions and Objects.
The first two methods are most commonly used with SOAP services accessible from the general internet or intranet where the BeVocal platform is installed. The last method is used only for BeVocal SOAP services that are exposed via a service registry and service ID. Briefly, the methods are as follows:
| | bevocal.soap.serviceFromWSDL--Given the URL for a Web Service Definition Language (WSDL) file, create an object to act as a proxy for one of the services described in the file. (This is the preferred way to create a service proxy object, because it provides better type checking.) |
| | bevocal.soap.serviceFromEndpoint--Given the name and endpoint URL of a SOAP service, create an object to act as a proxy for the service. (This method should be used only when the WSDL for a service is not available.) |
| | bevocal.soap.locateService--Given the service identifier and possibly a version, use the BeVocal SOAP service registry to locate either the standard BeVocal SOAP service or a particular version of that service. |
Note: Currently, no BeVocal SOAP services are exposed in the BeVocal SOAP service registry.
There are a number of errors that can happen while locating SOAP services. If an error occurs while creating a SOAP proxy object, an exception of type bevocal.soap.SoapException is thrown.
For example, assume want to use a temperature service available from www.xmethods.net. At that site, you discover that the WSDL for this service is at http://www.xmethods.net/sd/2001/TemperatureService.wsdl.
<?xml version="1.0"?>
<definitions name="TemperatureService"
targetNamespace="http://www.xmethods.net/sd/TemperatureService.wsdl"
xmlns:tns="http://www.xmethods.net/sd/TemperatureService.wsdl"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<message name="getTempRequest">
<part name="zipcode" type="xsd:string"/>
</message>
<message name="getTempResponse">
<part name="return" type="xsd:float"/>
</message>
<portType name="TemperaturePortType">
<operation name="getTemp">
<input message="tns:getTempRequest"/>
<output message="tns:getTempResponse"/>
</operation>
</portType>
<binding name="TemperatureBinding" type="tns:TemperaturePortType">
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="getTemp">
<soap:operation soapAction=""/>
<input>
<soap:body use="encoded" namespace="urn:xmethods-Temperature"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>
<output>
<soap:body use="encoded" namespace="urn:xmethods-Temperature"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</output>
</operation>
</binding>
<service name="TemperatureService">
<documentation>Returns current temperature in a given U.S. zipcode
</documentation>
<port name="TemperaturePort" binding="tns:TemperatureBinding">
<soap:address
location="http://services.xmethods.net:80/soap/servlet/rpcrouter"/>
</port>
</service>
</definitions>
The bold lines in the defintion provide the rest of the information you need to create a proxy object for this service in your VoiceXML application:
<script>
<![CDATA[
var service =
bevocal.soap.serviceFromWSDL(
// WSDL URL
"http://www.xmethods.net/sd/2001/TemperatureService.wsdl",
// name attribute of port
"TemperaturePort",
// targetNamespace attribute of definitions
"http://www.xmethods.net/sd/TemperatureService.wsdl",
// name attribute of service
"TemperatureService",
// location attribute of soap:address child of port element
"http://services.xmethods.com:80/soap/servlet/rpcrouter" //
);
]]>
</script>
Once you have a JavaScript proxy object for a SOAP service, calling the service's methods is easy. For example, if you have a service that performs temperature conversions, the code might look like this:
<script>
var converter = bevocal.soap.locateService("temp_converter", 1.0);
var freezing = converter.fahrenheitToCelsius(32.0);
</script>
The proxy object automatically converts the JavaScript fahrenheitToCelsius function into a call to the SOAP method with the same name. It converts the sole parameter, 32.0, to the SOAP encoding and passes it as a float. If the converter proxy had access to the WSDL for the service when it was created, it also knows the proper name for the argument and encodes that in the SOAP message as well.
When calling a SOAP method, there are currently some limitations regarding streaming out the arguments. A fixed mapping of Java classes to XML element types is used during serialization. Currently, a method's WSDL definition is not used when streaming out the method's arguments; the WSDL is used only when pre-flighting the method call.
This means that the interpreter cannot always stream out complex types with the correct XML type. Most SOAP servers can deal with this, but a few cannot and will return errors.
The biggest problem is with arrays of complex types. In a SOAP array, an attribute of the Array element must contain the type of the contained object. For example:
<SOAP-ENC:Array SOAP-ENC:arrayType="xyz:Order[1]">
<Order>
<Product>Apple</Product>
<Price>1.56</Price>
</Order>
</SOAP-ENC:Array>
In the current implementation, the interpreter is unable to determine the type contained within the array, so it cannot decide what to use as the value of the SOAP-ENC:arrayType attribute. As a default, the interpreter uses xsd:anyType, which causes an error on some servers.
The type mappings currently performed are as follows:
xsd is the XML Schema namespace. The interpreter uses the namespace specified in the WSDL. For services without WSDL information, on output, it uses http://www.w3.org/2001/XMLSchema; on input, it accepts any of the following:
http://www.w3.org/2001/XMLSchema http://www.w3.org/1999/XMLSchema http://www.w3.org/2000/10/XMLSchema
Occasionally, you may need to add information to the header sent with your SOAP requests. For example, if you use any of the BeVocal platform services in your VoiceXML application, you must add security information to the header.
To add headers, each JavaScript proxy object supports the _addHeader method. This method allows you to set an additional SOAP header for all SOAP requests.
Once you use the _addHeader method on a proxy object, all future requests sent using that object have the added header. If later in the same application you need to send a request to the same service that does not include that header, you must create a different proxy object to service the request.
In JavaScript, a method of an object is really just an object property that points to a function. This is true for the interpreter's SOAP proxy objects as well, allowing you to use them as "functors". For example:
<script>
var converter = bevocal.soap.lookupService("temp_converter", 1.0);
var conversion = converter.fahrenheitToCelsius;
...
// Someone else told me which conversion to use
var temperature = conversion(123);
</script>
When attempting to call a function on a SOAP proxy which has WSDL information available, the interpreter can detect many errors before making the actual SOAP call. This leads to earlier and better error detection and the ability to distinguish different types of errors more easily.
Remember that in this JavaScript binding, SOAP functions map to JavaScript objects. Consider this statement:
result = service.method(arg1, arg2);
It first retrieves the method property of the object service, then calls it as a function with the arguments arg1 and arg2, assigning the result to result.
When you refer to a nonexistent method of a service proxy that is backed by WSDL, such as:
var service = bevocal.soap.serviceFromWSDL(wsdlURL, ...); var result = service.missingmethod(1, 2);
the service proxy will return the JavaScript constant undefined when asked for its missingmethod property, and will print a warning to the log. The JavaScript interpreter will then try to treat the undefined value as a function, immediately causing a runtime error with the message. The log will look like this:
JavaScript warning on line 29 of 'soap.vxml: Cannot find operation: missingmethod - none defined ERROR error.semantic: JavaScript error on line 29: missingmethod is not a function.
The implementation returns undefined rather than throwing an exception immediately. However, this is an advantage in certain cases, because it allows you to write code like the following:
// Get the highest 1.x version of "myservice"
var service = bevocal.soap.lookupService("myservice", "1");
if (service.method2 != undefined) {
// Call the new and improved method
service.method2(arg1, arg2);
} else {
// Call the old method
service.method1(arg1);
}
If the interpreter immediately threw a runtime error when you tried to reference a missing method, you could not make this check for an undefined method.
If client code attempts to call a SOAP method using the wrong number of parameters, the interpreter's SOAP client will throw a bevocal.soap.SoapException exception with the cause property set to CALL_ERROR. The message property will contain an error message explaining that there was an argument mismatch. For example:
try {
myService.callMethod("too", "many", "parameters");
}
catch (error if error.type == "bevocal.soap.SoapException") {
if (error.cause == bevocal.soap.SoapException.CALL_ERROR) {
// Probably passed the wrong number of arguments
}
}
The current implementation does not validate parameter types against a method's WSDL definition. It simply serializes each parameter using the XML type that seems to be the best match. If this parameter does not match the type that was specified for that parameter in the WSDL, the server for the request will signal an error and return it as described next. It is also possible that the type-conversion engine will report an error if it is unable to serialize a complex JavaScript type. This is described in Type conversion.
Errors reported by a server are returned in a SOAP message with a <Fault> element. When the interpreter detects a fault message, it throws a bevocal.soap.SoapFault.
When the interpreter does not have the WSDL available for a service proxy, it can't do much pre-flighting of calls. This means that there are only several types of errors that can occur:
| | Type conversion error marshalling or unmarshalling an object (see Type conversion). |
| | Network communication error |
| | All other errors, including invalid method names, bad parameter lists, and so on, are reported by the SOAP server or service and thrown as a bevocal.soap.SoapFault, as described above. |
| Café Home |
Developer Agreement |
Privacy Policy |
Site Map |
Terms & Conditions Part No. 520-0001-02 | © 1999-2007, BeVocal, Inc. All rights reserved | 1.877.33.VOCAL |