SOAP
1.1
Definition of SOAP
SOAP
(Simple Object Access Protocol) is a simple, lightweight protocol for
structured and strong-type information exchange in a decentralized, distributed
environment. The protocol is based on XML (eXtensible Markup Language)
and consists of three parts:
1. An envelope which describes the contents of the message and how to
use it
2. A set of rules for serializing data exchanged between applications
3. A procedure to represent remote procedure calls, that is, the way in
which queries and the resulting responses to the procedure are represented.
Similar
to object distribution models (IIOP, DCOM...), SOAP can call methods,
services, components and objects on remote servers. However, unlike these
protocols, which use binary formats for the calls, SOAP uses text format
(Unicode), with the help of XML to structure the nature of the exchanges.
SOAP
can generally operate with numerous protocols (FTP, SMTP, POP...), but
it is particularly well suited to the HTTP protocol. It defines a reduced
set of parameters which are specified in the HTTP header, making it easier
to pass through proxies and firewalls.
Using
XML in SOAP
SOAP
messages are structured using XML. Within the framework of the remote
procedure call (RPC), it represents the parameters of the methods, the
return values and any potential error messages linked to the processes.
Coding SOAP messages in XML enables universal communication between applications,
services and platforms via the Internet. In order to do this, SOAP makes
use of the descriptive nature of the XML language, thus transforming the
content into an application.
In
more technical terms, just as with an XML fragment, SOAP messages make
references to different namespaces, enabling the content to be validated.
They must therefore include a call to SOAP namespaces, making it possible
to define and specify the use of standard tags in the message and to ensure
compatibility between SOAP versions. As soon as a SOAP message is received,
the SOAP tags are validated, as are the tags that express the subject
of the message. If it fails, an error is generated.
Soap
thus defines two namespaces:
- http://schemas.xmlsoap.org/soap/envelope/
for the envelope
-
http://schemas.xmlsoap.org/soap/encoding/
for the coding
SOAP and HTTP
Deploying
SOAP over HTTP makes it possible to use the SOAP decentralization method
in the well-used environment of HTTP. Using SOAP over HTTP also enables
resources already present on the Web to be unified by using the natural
request/response mode of HTTP. The only constraint is that a SOAP message
via HTTP must use the MIME type "text/xml".
Apache
Soap
Presentation
Apache
Soap is an Apache project which defines the SOAP protocol in Java. This
project is based on the source of IBM's SOAP application, SOAP
4J. The latest version of this implementation, dated May 30, 2001,
is version 2.2 of the project.
Apache
SOAP 2.2 defines almost all of the SOAP1.1
note, in the same way as the whole "SOAP Messages with Attachments" Note
from the W3C.
The
project essentially consists of four parts:
-
The
definition of an API for developing SOAP services
-
A
server-side infrastructure for deploying and running SOAP services.
-
Support
for three forms of encoding: SOAP 1.1, XML, XMI.
-
Use of HTTP, SMTP protocols.
The characteristics of Apache SOAP are very important for interoperability
between systems.
Apache
SOAP limitations
Apache
SOAP 2.2 does not support certain recommendations in the Note, such as
the definition of processing rules by specifying several attributes in
the elements of the SOAP envelope. These attributes are the following:
-
The
mustUnderstand attribute
-
The
actorintermediaries attribute
-
The
root attribute
These
are semantically enriching attributes which make more specific uses possible
(see Soap 1.1 from w3c). Attention must be paid in particular to the choice
of parser. The Xerces 1.3.x parser is not supported by Apache SOAP.
Setting
up a Java Web server
The development of a SOAP service in Java uses the API defined by Apache
SOAP for sending and processing messages. To deploy a Web service from
Apache, we use the deployment infrastructure supplied.
In
order to demonstrate how to use Apache SOAP, we deployed a calculating
service with three methods:
These
methods will be called by a Java SOAP client, executed on the remote server
that will send the response back to the enquirer.
Test
architecture
Setting
up a SOAP architecture solely using HTTP will require at least:
-
JDK 1.2.x or greater
-
An
Apache SOAP implementation
-
A
servlet engine (Websphere, Weblogic, Tomcat ...)
-
A
parser supporting XMLSchema
In our analysis, we set up an architecture using the following components:
It
should be noted that the installation documents for the SOAP service recommend
using the JavaMail API and JAF (Java Activation Framework).
You
can consult the installation documents at:
http://xml.apache.
org/soap/docs/index.html
Deployment
of a Server application
Developing a server application remains a traditional Java development.
For our calculator, we have developed a Calculator class whose methods
will be called by a SOAP envelope.
|
package
webservice;
class Calculator
{
public long add (long nbr1, long nbr2)
{
long result;
result = nbr1 + nbr2;
return result;
}
public long sub (long nbr1, long nbr2)
{...}
public long mul (long nbr1, long nbr2)
{...}
}
|
The
architecture of Apache SOAP provides us with the means to identify our
service. We have two solutions to this. The first involves describing
the Java class by the SOAP Web Interface at the following URL:
|
http://localhost:8080/soap/admin/deploy.js
|

Deployment
Interface
The
second is to invoke an XML file describing the service with a command
line:
|
java
org.apache.soap.server.ServiceManagerClient
http://localhost:8080/soap/servlet/rpcrouter deploy mon_fic.xml
|
This
XML file is called the "deployment descriptor". The deployment descriptor
supplies data to servers regarding the Web service: Which types are defined?
What are the functions or the methods that one can call on it? How to
process the responses sent by the server? All this information is described
by an XML file.
In
our example, we can define the calcul.xml file describing the service
of our Calculator class:
|
<?xml
version="1.0" encoding="ISO-8859-1"?>
<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
id="urn:Calculator">
<isd:provider type="java" scope="Application"
methods="add sub mul">
<isd:java class="webservice.Calculator" static="false"/>
</isd:provider>
</isd:service>
|
Identifying features:
-
name
of the service: urn:Calculator
-
language:
Java
-
name
of the methods: add, sub, mul
-
name
of the service application: webservice.Calculator
The
Calculator class sends back a Long type - a simple type in the XMLSchema
definition. Simple types are processed by default by Apache SOAP (see
Appendix), and do not require a particular definition.
On the other hand, if a service sends back an object as a response, the
object must be described in order to define its XML/JAVA mapping. An object
is defined in a SOAP envelope as an XML hierarchy. Each element or object
type must be defined for the serialization and deserialization of the
response (see Sending a complex request).
Let us take the example of a directory service. We can consult this directory
by requesting an address from a name. An address is represented in Java
as an object. Without returning to the object model, an address is then
made up of a street number, street name, town, etc.
In
order to describe these objects, mappings must be defined in the deployment
descriptor, as follows, for each object:
|
<isd:mappings>
<isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:x="urn:xml-soap-address-demo" qname="x:address"
javaType="samples.addressbook.Address"
java2XMLClassName="org.apache.soap.encoding.soapenc.
BeanSerializerer"
xml2JavaClassName="org.apache.soap.encoding.soapenc.
BeanSerializer"/>
...
</isd:mapping>
|
With
this mapping, the server indicates to Java clients where to find the methods
to understand the server response, with the attributes Java2XMLClassName
and xml2JavaClassName. We will enter into this in more detail in the section
entitled Sending a complex request.
Client
The
Apache SOAP API supplies the methods for creating a SOAP client in Java.
In order to call a Soap service, the client application must know the
definition of the service. For this, we need the following information
:
-
service
URL
-
service identifier
-
coding to use
-
names of the service methods
-
types of parameters to send and to process
All
this information will be included in the SOAP envelope when the request
is made.
Development
of a simple service
The
client consulting the calculator service must import the Apache Soap implementation
classes. When developing a service for processing simple types, the following
classes must be imported, at least:
import
org.apache.soap.rpc.Call;
import org.apache.soap.rpc.Parameter;
import org.apache.soap.rpc.Response;
import org.apache.soap.Fault;
import org.apache.soap.Constants;
import org.apache.soap.SOAPException; |
Construction
and sending of SOAP messages
The
construction of the SOAP message uses the methods in the Call class. This
class defines the set of characteristics which make up an RPC. Amongst
these methods, we can note two phases involved in the call.
The service identification phase represented by:
-
setTargetURI(String
identifiant_du_service)
-
setMethodName(String
nom_des_methodes_du_service)
-
setEncodingStyleURI(String uri_encodage)
The construction and request sending phase:
-
setParams(Vector
mes_parametres)
-
invoke(URL
http://mon.web.service..., String soapActionField)
The method setParams is used to define the variables which will be sent
to the server. This definition uses the Parameter class which instantiates
an object taking as its arguments:
-
name
-
type
-
value
-
coding
(empty by default)
The invoke method triggers the mechanism to send the SOAP request to the
relevant service. This service is identified by a URL. The URL of our
service will be the local server on which the Web service is deployed:
http://localhost:8080/soap/servlet/rpcrouter/.
Thus,
for our calculator example, we will have the following code:
|
Long
nbr1 = Long.valueOf(args[0]);
Long nbr2 = Long.valueOf(args[2]);
URL url = new URL("http://localhost:8080/soap/servlet/rpcrouter");
Call call = new Call();
//Name of service
call.setTargetObjectURI("urn:Calculator");
if (args[1].equals("+")){
// Name of service method
call.setMethodName("add");
}
...
// Coding
call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
Vector params = new Vector();
// Declaration of parameters
params.addElement(new Parameter("nbr1", Long.class, nbr1,
null));
params.addElement(new Parameter("nbr2", Long.class, nbr2,
null));
call.setParams(params);
Response resp = null;
try{
// SOAP request send
resp = call.invoke(url, soapActionField);
}
...
//processes
|
Processing
the SOAP response
The
envelope returned by the server is processed by the Response object. The
object contains the SOAP XML stream, and therefore all the characteristics
of the response. The stream is processed by the Parameter class methods.
By default, the Parameter object processes all the simple data types (Integer,
Long, String,...). The calculator service sends back a Long type, and
therefore has no special coding.
|
//construction
of SOAP message
...
Response resp = null;
try{
resp = call.invoke(url, soapActionField);
}
catch( SOAPException e ){
System.err.println("Caught SOAPException (" + e.getFaultCode()
+ "): " + e.getMessage());
System.exit(-1);
}
// Response retrieval.
if( !resp.generatedFault() ){
Parameter ret = resp.getReturnValue();
Object value = ret.getValue();
System.out.println(value);
}
else{
Fault fault = resp.getFault();
System.err.println("Generated fault: ");
System.out.println (" Fault Code = " + fault.getFaultCode());
System.out.println (" Fault String = " + fault.getFaultString());
}
|
Executing
the program
Executing
the client program sends the HTTP-SOAP stream to the server indicated
by the program. We have asked the server to calculate an addition for
us:
| java
webservice.CalculatorClient 6 + 5 |
The
stream is made up of an HTTP Header, followed by a SOAP envelope.
HTTP Header:
|
POST
/soap/servlet/rpcrouter HTTP/1.0
Host: localhost
Content-Type: text/xml; charset=utf-8
Content-Length: 463
SOAPAction: ""
|
SOAP
envelope:
|
<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd= "http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:add xmlns:ns1="urn:Calculator" SOAP-ENV:encodingStyle=
"http://schemas.xmlsoap.org/soap/encoding/">
<nbr1 xsi:type="xsd:long">6</nbr1>
<nbr2 xsi:type="xsd:long">5</nbr2>
</ns1:add>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
|
The
server receives the SOAP request and sends back the results of this addition:
HTTP
Header
|
HTTP/1.0
200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: 451
Set-Cookie2: JSESSIONID=8qilh88em1;Version=1;Discard;Path="/soap"
Set-Cookie: JSESSIONID=8qilh88em1;Path=/soap
Servlet-Engine: Tomcat Web Server/3.2.2 (JSP 1.1; Servlet 2.2; Java
1.3.0; Windows 2000 5.0 x86; java.vendor=Sun Microsystems Inc.)
|
SOAP
envelope
|
<?xml
version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd= "http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:addResponse xmlns:ns1="urn:Calculator" SOAP- ENV:encodingStyle="http://schemas.xmlsoap.org/soap/
encoding/">
<return xsi:type="xsd:long">11</return>
</ns1:addResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
|

Execution
of the calculator service
Sending
a complex request
As
we have seen, Apache SOAP processes all simple types (see Appendix) by
default. In the case of a response or an object send, this must be defined
so as to be understandable by both parties. For this task, the Apache
SOAP implementation gives us some classes in order to define these objects
easily.
Let
us take the example of a Web service which supplies a directory. Our request
must, from a name sent to the server, find its characteristics - the address,
telephone number, town, and so on. The queried service will send us back
a much more complex response than the calculator service.
According to the description of the service, the server sends back a complex
object, in the form of an XML hierarchy. The main difficulty is to know
how to process the response issued by the server when it does not send
back a known type.
If
we use the same development as before, the SOAP stream will be correctly
formulated on either side, but the response will not be understood in
Java, since it will have no instructions as to how to process the received
stream.
Send:
|
<?xml
version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP- ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd=
"http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:getAddressFromName xmlns:ns1="urn:AddressFetcher"
SOAP- ENV:encodingStyle="http://schemas.xmlsoap.org/
soap/encoding/">
<nameToLookup xsi:type="xsd:string">Bertrand Goupil</nameToLookup>
</ns1:getAddressFromName>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
|

Execution
of the directory service (Click
here for a larger image)
The
Java client does not understand the response and throws an exception.
This exception clearly indicates to us that the program has not found
a method to deserialize the response, which is of the type urn:xml-soap_address-demo:address.
In fact, the server sent back the following response:
|
<return
xmlns:ns2="urn:xml-soap-address-demo" xsi:type="ns2:address">
<phoneNumber xsi:type="ns2:phone">
<exchange xsi:type="xsd:string">0</exchange>
<areaCode xsi:type="xsd:int">33</areaCode>
<number xsi:type="xsd:string">155932600</number>
</phoneNumber>
<zip xsi:type="xsd:int">93200</zip>
<streetNum xsi:type="xsd:int">268</streetNum>
<state xsi:type="xsd:string">France</state>
<streetName xsi:type="xsd:string">ave du président
Wilson</streetName>
<city xsi:type="xsd:string">Saint Denis</city>
</return>
|
The
XML response indicates to us that there are two unknown types belonging
to the namespace ns2.
-
xsi:type="ns2:address"
-
xsi:type="ns2:phone"
Each
response type sent by SOAP must be serialized and deserialized between
the client and the server. If these types do not belong to any known definition,
we must then indicate which deserializer we have to use.
In
order to define a complex type, we use the BeanSerializer class to deserialize
the server response. Each object received or sent must be programmed as
a JavaBean (set and get methods), with XML elements of the return message
as parameters.
In the example below, and according to the previous SOAP message, streetNum
is an int type, and streetName is a String type.
|
...
public void setStreetNum(int streetNum){
this.streetNum = streetNum;
}
public int getStreetNum(){
return streetNum;
}
public void setStreetName(String streetName){
this.streetName = streetName;
}
public String getStreetName(){
return streetName;
}
...
|
The definition of a new type must be included in Apache SOAP's serialization/deserialization
mechanism. To do this, we must use the SOAPMappingRegistry class. In this
way, the object will contain all the information to process new types.
This class enables us to define SOAP/JAVA mapping through the mapTypes
method.
In our example, we have a complex type comprising two objects: Address
and PhoneNumber.
|
import
org.apache.soap.encoding.SOAPMappingRegistry;
import org.apache.soap.encoding.soapenc.BeanSerializer;
...
SOAPMappingRegistry
smr = new SOAPMappingRegistry();
BeanSerializer beanSer = new BeanSerializer();
smr.mapTypes(Constants.NS_URI_SOAP_ENC,
new QName("urn:xml-soap-address-demo", "address"),
Address.class, beanSer, beanSer);
smr.mapTypes(Constants.NS_URI_SOAP_ENC,
new QName("urn:xml-soap-address-demo", "phone"),
PhoneNumber.class, beanSer, beanSer);
Call call = new Call();
call.setSOAPMappingRegistry(smr);
...
|
The setSOAPMappingRegistry method will link the definition of new types
and the processing of the response. Therefore it is vital that it is added.
It
is important to note that the classes Address.class and PhoneNumber.class
need to be present on the client side for the application to function
well. If we develop an application calling a service which has never been
contacted, it is essential to know the structure of the message in order
to define the mapping.
Even
so, it is very difficult to know the structure of a message. If by chance
you do know the XML structure, you do not need to create a JavaBean to
define the mapping, as you can process the XML tags as a character string.
However, when programming the mapping, all elements of the SOAP response
(except the envelope) on all levels will have to be mapped.
In
our service:
|
SOAPMappingRegistry
smr = new SOAPMappingRegistry ();
StringDeserializer sd = new StringDeserializer ();
...
smr.mapTypes (Constants.NS_URI_SOAP_ENC, new QName ("",
"street"), null,
null, sd);
smr.mapTypes (Constants.NS_URI_SOAP_ENC, new QName ("",
"city"), null,
null, sd);
smr.mapTypes (Constants.NS_URI_SOAP_ENC, new QName ("",
"state"), null,
null, sd);
...
|

Serialization
of the response
GUI
Interface
In
order to debug SOAP applications, Apache provides an interface which displays
SOAP message sends. In the case of our directory, this tool is very useful
for finding out the structure of the response message. This interface,
called the GUI Interface, is a TCP tunnel which redirects the HTTP data
flow from a listening port, by way of tunneling, to the port of the machine
being queried. The interface is launched with this command:
|
java
org.apache.soap.util.net.TcpTunnelGui port_ecoute nom_serveur port
|

GUI Interface (Click
here for a larger image)
For our calculator service, deployed on a local server (localhost:8080),
the definition of the URL in the SOAP program will link to the listening
port of the GUI interface.
|
URL
url = new URL("http://localhost:8000/soap/servlet/rpcrouter");
|
The
launch command for the GUI tunnel will lead to the service address:
|
java
org.apache.soap.util.net.TcpTunnelGui 8000 localhost 8080
|
The
only limitation of the GUI is that it does not function with addresses
containing sub-domains.
Performances
CORBA,
DCOM and RMI use binary coding for message exchanges. This process requires
both parties to know the exchange language. This mode of transmission
is very effective, but makes it difficult to tweak the application that
processes the messages, and while each system uses its own binary mode,
these systems do not interoperate easily.
Given
that SOAP uses XML coding, it is easier to process the messages at each
stage of invocation, which requires a significant degree of flexibility
in the development of the SOAP application.
Clearly,
reading and interpreting XML messages is intrinsically slower than processing
binary messages, because of the coding-decoding time for SOAP messages.
Nonetheless
it is possible, in a closed environment such as an intranet, to optimize
the processing of SOAP messages using the same client-server implementation.
For example, a SOAP client could easily add an additional tag to the HTTP
header to indicate that it supports a particular optimization. If the
server also accepts it, this optimization could be set up for subsequent
transmissions.
The
Apache SOAP architecture is fairly slow in comparison to other implementations.
Indeed, communication between two Java applications on the same one machine
works at around thirty round-trips per second, while other implementations
can work at up to 700 round-trips per second (see
http://www.extreme.indiana.edu/soap
/sc00/paper/node13 .html#observations).
Future
Implementation Development
The
Apache SOAP project will remain at version 2.2, as Apache's future Web
services implementation project is called "Axis".
This project will support the whole of the SOAP 1.1 note, but will be
much more comprehensive than the Apache SOAP project.
As
well as being a more reliable and effective implementation, Axis implements
a greater number of protocols (HTTP, HTTPs, SMTP, POP3, JMS, etc). Security
aspects are strengthened, and the implementation provides support for
the description and discovery of Web services by using WSDL. It is worth
noting that there are no releases yet for these products; those who cannot
wait can only download the "nightly" version on CVS .
Conclusion
Apache
SOAP is a comprehensive implementation of SOAP. Nonetheless it moves away
from the rules for referencing services by using its own description language
(deployment descriptor). As regards interoperability, problems are recurrent,
regardless of the implementation. This is caused by the lack of compliance
with standards, both in terms of transport protocols and XML parsing.
Another important issue is the status of the SOAP standard itself. It
is in note status, therefore will move up to a recommendation. Evidently,
as long as a standard is not definitive, some small interoperability problems
will persist.
Appendix
XMLSchema
types supported by Apache Soap 2.2:
|
XMLSchema
1999
|
XMLSchema
2000
|
XMLSchema
2001
|
| string |
Yes
|
Yes
|
Yes
|
| int |
Yes
|
Yes
|
Yes
|
| decimal |
Yes
|
Yes
|
Yes
|
| float |
Yes
|
Yes
|
Yes
|
| double |
Yes
|
Yes
|
Yes
|
| date |
Yes
|
Yes
|
Yes
|
| boolean |
Yes
|
Yes
|
Yes
|
| long |
Yes
|
Yes
|
Yes
|
| short |
Yes
|
Yes
|
Yes
|
| QName |
Yes
|
Yes
|
Yes
|
| timeInstant |
Yes
|
Yes
|
Yes
|
| any
Type |
No
|
Yes
|
Yes
|
| ur-type |
Yes
|
No
|
No
|