| [ directory ] |
|
12.2 Simple Object Access ProtocolSOAP is a messaging layer protocol, as shown in Figure 12.1, and is the most broadly accepted open standard at that layer. The SOAP specification defines the Envelope structure, which wraps application-specific XML contents, a set of encoding rules for expressing instances of application-defined data types, and a convention for representing RPCs. Here, we first review briefly the birth of SOAP (in Section 12.2.1). Then we introduce a simple example for travel reservations (in Section 12.2.2) and review various aspects of SOAP in more detail with the example (in Section 12.2.3). Finally, we discuss when we should use SOAP (in Section 12.2.4). 12.2.1 The Birth of SOAPSOAP was recognized as an "open standard" when SOAP 1.1 was submitted as a Note to W3C jointly with Microsoft, IBM, Lotus, UserLand, and DevelopMentor. Although SOAP existed before as specifications 0.9 and 1.0, those specifications were considered proprietary to Microsoft. For example, previous SOAP specifi cations were based on XML Data Reduced (XDR) instead of XML Schema, were too RPC-oriented, and were not extensible. Furthermore, because Microsoft played a central role, some people thought that SOAP was Windows-specific technology. On the other hand, people have been demanding XML messaging for application integration because XML-based B2B collaboration is increasing. Although SOAP 1.0 was enough for that purpose, people were reluctant to adopt it because it was considered Microsoft proprietary. Therefore, IBM and Lotus joined Micro soft to come up with SOAP 1.1, incorporating W3C standards. The companies submitted SOAP 1.1 as a Note to W3C. This indicates that W3C is going to recognize SOAP as a standard. W3C also started the XML Protocol Working Group based on SOAP 1.1. The working group recently announced SOAP 1.2, clarifying ambiguous portions of SOAP 1.1.[2] Apart from the standardization, the collaboration of IBM and Microsoft contributes to the broad acceptance of SOAP.
12.2.2 Travel Reservation ExampleHere, we introduce a travel reservation example. We assume that the example programs are used to reserve a package tour after a user has searched the tour based on their requirements, such as destination, date, and budget. The main tasks of the example are to receive a reservation request, including a package tour code, date, and user ID; to check the input data; and to return an acceptance message if appropriate. We prepare two kinds of programs for the example. One has an ordinary Java API and is used later for showing how SOAP-RPC works. The other has an XML-in/XML-out杝tyle API; that is, it receives an XML document for a reservation request and returns an XML document for an acceptance. It is used for showing how document-centric messaging works in SOAP. Listing 12.1 shows a travel service program that can be accessed via Java method calls and is accessed via SOAP-RPC. The Java class TravelServiceRPC has only one method, called reserve, which accepts three parameters, a user ID, a tour ID, and the departure date. The logic of the method is simplified to check only whether the specified departure date is later than the current time. The method returns the object Reservation, shown in Listing 12.2, which includes member variables for a reservation, such as a user ID, a tour ID, a departure date, and a reservation ID. As you can see, the class defines only setter and getter methods for these member variables. As we will show later, SOAP defines encoding rules for expressing such objects in XML. Listing 12.1 The travel service program for RPC, chap12/travel/provider/TravelServiceRPC.java
package chap12.travel.provider;
import java.util.Date;
import chap12.travel.Reservation;
public class TravelServiceRPC {
public Reservation reserve(String user,
String tourId,
Date departure)
throws Exception
{
long currentTime = System.currentTimeMillis();
// Only check if the departure is after today
if ( currentTime > departure.getTime() ) {
throw new Exception("Date is wrong: " + departure);
}
Reservation resv=new Reservation();
resv.setUser(user);
resv.setTourId(tourId);
resv.setDate(departure);
resv.setReservationId(tourId+currentTime);
return resv;
}
}
Listing 12.2 The class for maintaining travel reservation information, chap12/travel/Reservation.java
package chap12.travel;
import java.util.Date;
public class Reservation {
private String user;
private String tourId;
private Date date;
private String reservationId;
public String getUser() {
return user;
}
public String getTourId() {
return tourId;
}
public Date getDate() {
return date;
}
public String getReservationId() {
return reservationId;
}
public void setUser(String newUser) {
user = newUser;
}
public void setTourId(String newTourId) {
tourId = newTourId;
}
public void setDate(Date newDate) {
date = newDate;
}
public void setReservationId(String newReservationId) {
reservationId = newReservationId;
}
public String toString() { // This is just a utility
String str ="Reservation: ";
str = str + "user/"+ user + " ";
str = str + "tourId/"+ tourId + " ";
str = str + "date/"+ date + " ";
str = str + "reservationId/"+ reservationId + " ";
return str;
}
}
Listing 12.3 shows another program, which performs the reservation task in an XML-in/XML-out manner. The method reserve in the TravelServiceXML class receives an XML document, which is represented in a DOM object, for a reservation request and returns a DOM object for a reservation acceptance. The reserve method invokes the insertValues method, within which the insertValue method is invoked to copy a property, such as user ID, tour ID, and departure date; and finally a generated acceptance ID is included in the response document. Listing 12.3 The travel service program with an XML-in/XML-out interface, chap12/travel/provider/TravelServiceXML.java
package chap12.travel.provider;
import java.io.IOException;
import java.io.StringReader;
import javax.mail.MessagingException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.apache.xerces.dom.DocumentImpl;
import org.apache.soap.util.xml.DOM2Writer;
import chap12.simplesoap.util.EnvelopeUtil;
import chap12.simplesoap.MessageConsumer;
public class TravelServiceXML {
public Document reserve(Document req) throws Exception {
Document res=
EnvelopeUtil.createDOM(new StringReader(RESP_TMP));
insertValues(res, req);
return res;
}
private void insertValues(Document res, Document req) {
// just copy from request to response
insertValue(USER, res, req);
insertValue(TOUR_ID, res, req);
insertValue(YEAR, res, req);
insertValue(MONTH, res, req);
insertValue(DAY, res, req);
long currentTime = System.currentTimeMillis();
Element accept=
(Element)
res.getElementsByTagNameNS(TOUR_NS,
"accepted")
.item(0);
Element resv=res.createElement("reservation-id");
accept.appendChild(resv);
accept.appendChild(res.createTextNode("\n "));
resv.appendChild(
res.createTextNode(getTourID(req)+currentTime));
}
private void insertValue(String tagName,
Document res,
Document req) {
Element el_out=
(Element)res.getElementsByTagNameNS(TOUR_NS,tagName)
.item(0);
Element el_in=
(Element)req.getElementsByTagNameNS(TOUR_NS,tagName)
.item(0);
NodeList list = el_in.getChildNodes();
for(int i=0; i<list.getLength(); i++) {
Node node = res.importNode(list.item(i), true);
el_out.appendChild(node);
}
}
private String getTourID(Document doc) {
Element el=
(Element)doc.getElementsByTagNameNS(TOUR_NS,TOUR_ID)
.item(0);
NodeList list = el.getChildNodes();
String tourId="";
for(int i=0; i<list.getLength(); i++) {
if (list.item(i).getNodeType()==Node.TEXT_NODE) {
Text textNode = (Text)list.item(i);
tourId += textNode.getData();
}
}
return tourId;
}
final static private String USER="user";
final static private String TOUR_ID="tourId";
final static private String YEAR="year";
final static private String MONTH="month";
final static private String DAY="day";
final static private String TOUR_NS="urn:reserve-tour-msg";
final static private java.lang.String RESP_TMP=
" <ns1:accepted xmlns:ns1=\"urn:reserve-tour-msg\">\n"+
" <ns1:user></ns1:user>\n"+
" <ns1:tourId></ns1:tourId>\n"+
" <ns1:depature>\n"+
" <ns1:year></ns1:year>\n"+
" <ns1:month></ns1:month>\n"+
" <ns1:day></ns1:day>\n"+
" </ns1:depature>\n"+
" </ns1:accepted>\n";
}
Listings 12.4 and 12.5 show examples of XML documents for the program TravelServiceXML. When you look at them closely, you should notice that the request and response XML documents are similar. Accordingly, in the program, all parameters in the request are copied to the response, and only a reservation ID is created to be included in the response. Listing 12.4 Request XML document for travel reservation
<ns1:reserve xmlns:ns1="urn:reserve-tour-msg">
<ns1:user>nakamury</ns1:user>
<ns1:tourId>hawaii55</ns1:tourId>
<ns1:departure>
<ns1:year>2001</ns1:year>
<ns1:month>9</ns1:month>
<ns1:day>15</ns1:day>
</ns1:departure>
</ns1:reserve>
Listing 12.5 Response XML document for travel reservation
<ns1:accepted xmlns:ns1="urn:reserve-tour-msg">
<ns1:user>nakamury</ns1:user>
<ns1:tourId>hawaii55</ns1:tourId>
<ns1:departure>
<ns1:year>2001</ns1:year>
<ns1:month>9</ns1:month>
<ns1:day>15</ns1:day>
</ns1:departure>
<ns1:reservation-id>hawaii55988028016497</ns1:reservation-id>
</ns1:accepted>
In this section, we showed two kinds of travel reservation programs: TravelServiceRPC and TravelServiceXML. In the following sections, we describe two kinds of SOAP messaging styles with these programs: RPC and document-centric messaging. 12.2.3 Basic Concepts of SOAPNext we cover the key aspects of SOAP. First we introduce the SOAP Envelope. Besides describing its syntax, we take a close look at a unique concept in SOAP called "intermediary," and we explain header processing. Also, we discuss another important feature of SOAP, SOAP encoding rules. SOAP EnvelopesSOAP defines a means to envelop application-specific XML documents. A SOAP Envelope specifies a framework within which you can include both application-specific and application-independent information in an XML document. SOAP also defines a set of rules for processing each entry of the XML document. Figure 12.2 depicts the structure of a SOAP Envelope. A SOAP Envelope is an XML document that has a required Body element and an optional Header element as its children. Header is intended to include application-independent information and can include multiple header entries. In the SOAP 1.1 specification, a transaction ID is shown as an example of a header entry. In addition, we may also include security information in a header entry, as detailed in Chapter 14. On the other hand, Body includes application-specific information, such as the travel reservation request in our example. Like the Header element, Body also can have multiple body entries, as shown in Figure 12.2. Figure 12.2. Structure of a SOAP Envelope
An example of a SOAP message is shown in Listing 12.6. This message comes from a combination of two things: a request message for TravelServiceXML and a transaction ID, described in the SOAP specification. The namespace URI for SOAP Envelopes is http://schemas.xmlsoap.org/soap/envelope/, and it is found in the xmlns:SOAP-ENV attribute of the Envelope element.[3]
Listing 12.6 Example of a SOAP message
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<t:Transaction xmlns:t="some-URI"
SOAP-ENV:mustUnderstand="1">
5
</t:Transaction>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:reserve xmlns:ns1="urn:reserve-tour-msg">
<ns1:user>nakamury</ns1:user>
<ns1:tourId>hawaii55</ns1:tourId>
<ns1:departure>
<ns1:year>2001</ns1:year>
<ns1:month>9</ns1:month>
<ns1:day>15</ns1:day>
</ns1:departure>
</ns1:reserve>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The transaction ID in a header entry serves as a correlation of multiple SOAP messages, according to the specification. In our travel reservation example, a customer may first search a package tour, reserve it, and finally pay with a credit card. Assuming that these three steps are performed via SOAP messaging, we may be able to correlate these three messages with the transaction ID. The mustUnderstand attribute in the header entry indicates that the recipient of the message must process the header entry. Because the processing rule for the attribute is tightly related to the intermediary, further details are discussed in the section Header Processing and the Intermediary. As for the body entry, you can see that the XML document in Listing 12.4 is embedded as it is in the Body element. As for errors, the SOAP Fault element is defined to include information such as a fault code, string, and details. Assume that a past date is specified in a travel reservation request. In that case, a receiver application can return a SOAP message including the SOAP Fault element to the requestor. In addition, the SOAP Fault can also be used when a program encounters an error during header entry processing. You may ask, why is the SOAP Envelope necessary? You could develop a travel reservation program as in Chapter 10, making the program exchange XML documents without the Envelope. However, such a program would not be flexible and extensible. Assume that you take the non-Envelope approach. Although it would be enough, you later would be required to insert a digital signature into the XML documents. Such modification should be performed easily. If you develop your applications with SOAP, you do not have to modify a module that processes SOAP body entries梩hat is, XML documents for travel reservation requests. Instead, you can extend your applications by adding a digital signature杙rocessing module. As for error handling, because the SOAP engine processes application-independent errors, you can focus on only application-specific errors. Header Processing and the IntermediaryOne of the most interesting features of SOAP is the concept of the intermediary. And the processing rules for the SOAP Envelope take account of the intermediary explicitly. As shown in Figure 12.3, a SOAP message is sent by an initial sender, travels through potentially multiple intermediaries, and arrives at the final destination. The path on which a SOAP message travels is called the message path. Even if we combine multiple transports, we have only a single message path. Such an intermediary concept seems redundant in some cases. However, it is very flexible because you can place additional processing components on intermediaries without modifying the applications of the initial sender and the ultimate destination. Figure 12.3. SOAP intermediaries and transports.
In reality, enterprises often have two firewalls between the intranet and the Internet. The area between two firewalls is called the Demilitarized Zone (DMZ). One typical configuration is that an application server is located in the DMZ, and it communicates with backend servers via IBM MQSeries. Even when you are required to handle a digital signature in the DMZ, you may not have to update your backend applications, thanks to the transport-agnostic nature and the intermediary concept of SOAP. The definition of the SOAP Envelope includes two attributes for header entries: actor and mustUnderstand. The processing rules for them are specified based on the message path concept, as shown in Figure 12.3. The actor attribute is used to indicate who must process the header entry. Referring to Figure 12.3, we assume that the first intermediary is http://xmlbook/inter1, and the second is http://xmlbook/inter2. In this assumption, the following header entry is targeted at the second intermediary, but not the first one.
<t:Transaction xmlns:t="some-URI"
SOAP-ENV:actor="http://xmlbook/inter2">
5
</t:Transaction>
Note that when the actor attribute is omitted, the header entry is targeted at the final destination. The mustUnderstand attribute indicates whether the application specified by the actor attribute must process the header entry. The following example indicates that the header entry is targeted at the second intermediary, and the intermediary must process the header entry.
<t:Transaction xmlns:t="some-URI" SOAP-ENV:actor="http://xmlbook/
inter2"
SOAP-ENV:mustUnderstand="1">
5
</t:Transaction>
If the mustUnderstand attribute is not specified, its default value is 0, and the intermediary does not have to process the header entry. The concept of intermediaries is interesting and is expected to be useful in many cases. However, there are some ambiguities about the actor and mustUnderstand attributes. For example, SOAP does not prescribe what entity should be indicated by the URI of the actor attribute. Although we assume that actor attributes indicate particular nodes, it is possible that an actor indicates a particular handler, assuming that multiple handlers are located at each node. In that case, we would adopt http://foo-vendor/transaction-handler as the value of the actor attribute. As for mustUnderstand, some people are criticizing the fact that there is no definition of "processing" in the specification. In spite of such issues, the concept of the SOAP intermediary is extremely important and demanded. Recently, enterprise systems have been located within firewalls, so they are accessed via intermediary nodes such as routers and proxies. Accordingly, XML messages travel to a final destination via multiple intermediary nodes. We can expect that we will have requirements for processing XML messages at intermediary nodes very often. SOAP EncodingHow can we access the TravelServiceRPC class in Listing 12.1 from a remote host? According to the concept of RPC, we can remotely invoke such a program as if it were a local call. To implement the RPC mechanism, we have to somehow encode the method invocation of the requestor, transmit the encoded data over the network, and decode it to invoke a method at the destination. As for encoding, XML can be used to express method invocations, as you can imagine. Now, how can we encode method invocations? As illustrated in Figure 12.1, XML messaging is used two different ways: through document-centric messaging and RPC. The SOAP Envelope is not intended either; only body entries are concerned with what the purpose is. SOAP encoding rules especially concern RPC, prescribing how to encode application-specific data in XML format. In the SOAP Envelope, the attribute encodingStyle is defined to specify a particular encoding rule, and http://schemas.xmlsoap.org/soap/encoding/ is substituted when you use SOAP encoding rules. However, note that the SOAP encoding URI is not a default value of the attribute. In other words, you can take or define another encoding rule to specify in the attribute. Here we discuss only SOAP encoding. Other encoding approaches are discussed in Chapter 15. Let us begin by showing examples of SOAP encoding. Assume that you access our TravelServiceRPC program (see Listing 12.1). In that case, the invocation of the reserve method should be represented in XML to transmit to the destination. And as for the response, the Reservation object should be represented in XML to reply to the requestor. SOAP encoding serves such a purpose. Listing 12.7 shows the request SOAP message, and Listing 12.8 shows the response SOAP message. They are both generated from Apache SOAP, which we describe later. In Listing 12.7, a method invocation, reserve(nakamury,hawaii55,2001-08-15T00:00:00Z), is encoded. The method invocation is represented in XML, viewing it as structured data; that is, the method name is a parent element, and the parameters are its child elements. In this encoding, we have to specify a parameter name to represent a parameter as an XML element, but we do not have to specify its type if there is some agreement between the requestor and the provider. Listing 12.8 shows a response SOAP message, which encodes the Reservation object. The value xsi:type="ns2:reservation" in the return element indicates that an object of the type ns2:reservation is encoded here. Furthermore, child elements of the return element correspond to member variables of the Reservation class. Note that there is no definition of the correspondence relationship between ns2:reservation and the Reservation class in the message itself. Such mapping is platform-dependent. In Apache SOAP, such mapping is specified in a deployment descriptor, described in the Apache SOAP section. Listing 12.7 SOAP-RPC request message
<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:reserve xmlns:ns1="urn:reserve-tour-rpc"
SOAP-ENV:encodingStyle=
"http://schemas.xmlsoap.org/soap/encoding/">
<user xsi:type="xsd:string">nakamury</user>
<tourId xsi:type="xsd:string">hawaii55</tourId>
<departure xsi:type="xsd:timeInstant">
2001-08-15T00:00:00Z
</departure>
</ns1:reserve>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Listing 12.8 SOAP-RPC response message
<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:reserveResponse xmlns:ns1="urn:reserve-tour-rpc"
SOAP-ENV:encodingStyle=
"http://schemas.xmlsoap.org/soap/encoding/">
<return xmlns:ns2="urn:xml-soap-travel-demo"
xsi:type="ns2:reservation">
<tourId xsi:type="xsd:string">hawaii55</tourId>
<user xsi:type="xsd:string">nakamury</user>
<date xsi:type="xsd:timeInstant">
2001-08-15T00:00:00Z
</date>
<reservationId xsi:type="xsd:string">
hawaii55987928919559
</reservationId>
</return>
</ns1:reserveResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
SOAP encoding rules assume an abstract type system and define a mapping between the type system and XML syntax. The type system consists of a set of basic types such as integer and string, complex types, array types, and so on. With these constructs, we can define data models. For example, our Reservation class can be viewed as a complex type that has four properties, such as user of type string and date of type timeInstant. Listing 12.9 shows XML Schema and a XML document for the Reservation class. Listing 12.9 XML Schema and document for Reservation class
<complexType name="Reservation">
<element name="tourId" type="xsd:string"/>
<element name="user" type="xsd:string"/>
<element name="date" type="xsd:timeInstant"/>
<element name="reservation" type="xsd:string"/>
</complexType>
<Reservation>
<tourId xsi:type="xsd:string">hawaii55</tourId>
<user xsi:type="xsd:string">naka</user>
<date xsi:type="xsd:timeInstant">2001-08-15T00:00:00Z</date>
<reservationId xsi:type="xsd:string">hawaii55987928919559
</reservationId>
</Reservation>
The mapping between the Reservation class and the schema must be obvious. Accordingly, you can imagine how the Reservation object is translated into an XML document via the Reservation class and XML Schema. Now you may ask, how can very complicated Java objects be encoded? The issue involved is called data binding, explained in Chapter 15 in detail. Instead, here we consider one complicated example, as shown in Figure 12.4. Figure 12.4. An object model with multi-references
Figure 12.4 shows that two objects refer to a common object, and this is called a multi-reference. In this case, it is not sufficient to encode each object separately; we have to encode objects by including the relationship between them. According to SOAP encoding rules, objects that are referred to by multiple objects should be separated and be placed at the top level. Then the reference is represented by the href and id attributes. Figure 12.4 is encoded as follows: <AirTicketReservation> <airline>United</airline> <customer href="#customer-1"/> </AirTicketReservation> <HotelReservation> <hotel>Hilton</hotel> <customer href="#customer-1"/> </HotelReservation> <Customer id="customer-1"> <name>Henry Ford</name> </Customer> If you look at the customer element under AirTicketReservation and HotelReservation, you notice that each of them has an href attribute whose value is #customer-1. On the other hand, the Customer element has an id attribute, customer-1, which can be referred by #customer-1. With SOAP encoding rules, you can specify what kind of XML documents are transmitted over the network for RPC invocation. However, we also have to allow users to invoke remote methods as if they were local methods. To provide such a function, you have to define an IDL and generate a client stub from it. We discuss that in more detail in Chapter 13, Section 13.2. 12.2.4 To Use SOAP or Not?As we have seen, SOAP provides various functions and is convenient in many cases. On the other hand, its performance should be much worse than traditional tightly coupled technologies such as CORBA. Here, we discuss in which situations we should use SOAP. We can summarize that we should use SOAP for B2B and use existing technologies like CORBA and EJB for application integration within an intranet. Here is a list of situations in which SOAP could be useful. Supporting different types of clientsAssume that you have server applications developed with Java, and they are running in a UNIX environment. And for some reason, you have to develop client applications with Visual Basic in Windows. In this case, SOAP is the most appropriate alternative. We already have SOAP for both platforms, so you can start developing now. Supporting multiple transportsEven if you use a single transport now, you may have to adopt multiple transports in the future. In this case, with SOAP you can minimize the changes to your application when you have to use multiple transports. End-to-end securityEnd-to-end security is one of the most appropriate reasons for using SOAP. Security information can be placed within a SOAP header; accordingly, it can travel through multiple hosts. Such security information can be handled by SOAP platforms. A gateway for the enterpriseEnterprises can provide many services. In this case, you have a gateway server, which plays the role of SOAP intermediary. The SOAP intermediary can perform "intermediate" tasks such as routing, security, and session management. From the requestor side, customers do not have to manage access points for services. At the provider side, you can even change the transport without more coding. The concepts of SOAP header and actor are useful in this situation. |
| [ directory ] |
|