Level: Intermediate Benoit Marchal (bmarchal@pineapplesoft.com), Consultant, Pineapplesoft
14 Oct 2003 Benoit continues to develop a lightweight XML client. In this article, he shows you how to create SOAP transactions through XSLT. Combined with XI, a text-to-XML conversion engine, this process makes it easy to create SOAP messages. Ultimately, the goal is to create SOAP messages from data exported by business applications. Share your thoughts on this article with the author and other readers in the accompanying discussion forum.
This is the second part of a new project in the "Working XML" column that aims to build a lightweight XML client, a tool that enables small organizations (up to around 50 people) to exchange XML transactions with an XML server.
Did you say e-business?
I have found the need for such a simple client in most business-to-business (B2B) e-commerce projects. For a detailed discussion of this need, please refer to the previous installment, "A lightweight XML client." Next I'll examine the main argument.
In B2B projects, different business partners join forces to improve their competitiveness by using technology more effectively. The business argument behind those efforts varies greatly. In many cases the goal is to automate the administrative operations (all the paperwork associated with ordering, delivering, invoicing, and paying) and therefore reduce the cost. Simplifying the paperwork may also help reduce warehousing needs (by ordering later) or delivery costs (by having the supplier ship directly to the user or consumer).
In other cases, the goal is to deliver new services or products by combining the services and products of two or more partners. Build-to-order is a prime example in which the consumer orders goods that are customized at the factory. This is most often deployed in direct sales because it's easier to get it right if you control the distribution and the factory. To deploy build-to-order in the reseller channel effectively, you need a strong e-business solution.
Although the business motivations differ, the technical implementation always centers on a more efficient exchange of data between the business partners. In many respects, an e-commerce project is like enterprise application integration (eAI), but it stretches across corporate boundaries (which, incidentally, increases complexity dramatically because you have to deal with different corporate cultures and different business strategies).
It's common for an e-commerce project to include a handful of large corporations and many smaller ones. The differences in size can be significant -- for example, I recall a project where one of the partners was processing more than a million transactions a year as opposed to a thousand to ten thousand for the other partners.
Given the difference in size, it's obvious that the needs are different, but unfortunately most e-commerce tools are geared towards the needs of the largest organizations only. These tools are designed to handle a large number of transactions automatically and, in practice, they end up being too sophisticated and overpriced for most partners. Some projects fail to recognize this difference and never recover (just try to deploy a J2EE server at a mom-and-pop startup). Successful projects, on the other hand, offer some sort of lightweight solution that adequately covers the needs of the smaller partners.
In my previous article, I discussed the requirements for one such lightweight solution, drawing from my experience with e-commerce XML projects. In this article, I will discuss communication protocols, including the Simple Object Access Protocol (SOAP), and build a first version of the lightweight client.
Talk to the server
One of the most important features of a lightweight client is the ability to prepare XML transactions with the data stored by a business application. The solution I proposed previously was to use XI, the XML conversion tool I developed previously in this column (see Resources for a link to earlier installments).
Indeed, most business applications can store their data as comma separated value (CSV) files. It is not difficult to parse this data with the regular expressions that XI uses. Another popular format uses fixed-length fields (also known as flat files). This latter format is particularly popular among users of IBM iSeries (formerly AS/400). Again, it is trivial to parse those files with regular expressions.
Next you need to send the transactions to an XML server. You can send them in several ways, such as:
- E-mail
- Uploading through an HTML form
- Use of SOAP
At first sight, e-mail might appear to be the simplest solution. It is also well suited to organizations that may not have permanent Internet connectivity. One issue, though, is how to structure the e-mail: Do you send the XML transaction in the body of the mail or in an attachment? Do you send the e-mail automatically or insert it in the user's mailbox?
An alternative is to use the <input type="file"> HTML tag and ask the user to upload the XML transaction manually. In practice, though, many users find this solution confusing.
In my opinion, SOAP is the best solution. SOAP is a simple protocol for using HTTP or e-mail to send XML transactions automatically. SOAP supersedes the e-mail and HTML form options.
SOAP primer and its Java API
The Java Community has introduced at least three APIs for working with SOAP:
- Java API for XML-based RPC (JAX-RPC)
- Java API for XML Messaging (JAXM)
- SOAP with Attachments API for Java (SAAJ)
Why three APIs? Because SOAP is a modular specification that can address many different needs. Depending on their goals, developers can use one of these three APIs.
Figure 1 illustrates the SOAP stack:
Figure 1. The SOAP stack
At the bottom of the stack are communication protocols -- mostly HTTP and SMTP, though other protocols (such as HMS) could be used as well. Above the communication protocols are the optional file attachment and security layers that are implemented with MIME. Anything above those two layers is implemented in XML. SOAP messaging is a bare-bones protocol for exchanging XML transactions. It mostly consists of an envelope and error handling.
Last but not least is the RPC layer, which specifies how to encode method calls in XML. SOAP is most famous for this feature.
The different Java APIs target the different layers in the stack:
- SAAJ targets the attachment layer
- JAXM targets the messaging layer
- JAX-RPC targets the RPC layer
Since you will use XI to prepare the transaction, you don't need Java support for RPC. In this project, you will work with JAXM and possibly SAAJ.
Looking into JAXM
If you look into JAXM, you will find that it is a thin wrapper around an HTTP library. JAXM is so thin because you're in charge -- more specifically, you're responsible for creating the XML request and decoding the responses (although JAXM does flag some errors automatically).
This design is in contrast with JAX-RPC, which hides all the XML from the developer. Again, you should choose JAX-RPC if you don't want to see any XML. You should choose JAXM if you want to manage the XML, which is precisely my goal here.
I have added JAXM support to XI in the class SOAPLink. I won't go into much detail on SOAPLink in this article because most of the code will be familiar. Download the source from the online repository (see Resources). Listing 1 is an excerpt of the JAXM integration.
Listing 1. JAXM integration in XI
public synchronized File apply(File source,boolean overwrite)
throws exception, SAXException,
TransformerException, SOAPException
{
File requestFile = requestLink.applyTo(source,overwrite);
SOAPMessage request = messageFactory.createMessage(),
response = null;
SOAPPart part = request.getSOAPPart();
InputStream is = new FileInputStream(requestFile);
try
{
part.setContent(new StreamSource(is));
SOAPConnection connection =
connectionFactory.createConnection();
try
{
response = connection.call(request,endpoint);
}
finally
{
connection.close();
}
}
finally
{
is.close();
}
if(response == null)
return null;
String fname = requestFile.getName();
int pos = fname.lastIndexOf('.');
String base = pos != -1 ? fname.substring(0,pos + 1)
: fname + '.';
File responseFile = new File(output,base + "soap");
int index = 1;
while(responseFile.exists() && !overwrite)
{
responseFile = new File(output,base + index + ".soap");
index++;
}
OutputStream os = new FileOutputStream(responseFile);
try
{
response.writeTo(os);
}
finally
{
os.close();
}
return responseLink.applyTo(responseFile,overwrite);
}
|
To create a SOAP message, simply instantiate a SOAPMessage object. SOAPMessage gives you access to all the elements in the message: the SOAP envelope, attachments, MIME headers, and more.
From SOAPMessage, you can retrieve the SOAPPart object with the SOAP request itself (excluding the attachments). The easiest way to initialize SOAPPart is to call the setContent() method, passing it an XML document.
To send the document, use the call() method on a SOAPConnection object. The call() method returns a SOAPMessage object with the response from the server.
To decode the result in Listing 1, one option is to save it to a file with the writeTo() method and later parse the result. Note that this is unsafe because it will fail if the server replies with an attachment.
This highlights one issue with JAXM: It is not easy to interface JAXM with other XML APIs. For example, the Java language normally uses the DOM API to represent XML trees. JAXM defines a different set of objects (such as SOAPElement) that essentially duplicates DOM. I fail to see how JAXM benefits from this, since it makes it difficult to process JAXM messages with familiar Java XML libraries.
Still, for the time being I'll stick with this solution, since it's good enough for simple requests. I plan to investigate this issue further by the next article and will try to come up with a better solution. If you have any suggestions, I'd love to hear from you. (Use the Discuss button at the top or bottom of this column to send your comments.) One option I'm considering is a DOM-to-JAXM (or SAX-to-JAXM) conversion library.
I use XSLTLink to create the transaction and process the response. XSLTLink should be familiar to regular readers: It is one of the main entry points into XI.
Messages and stylesheets
Now I'll turn to the SOAP transaction itself and the XSLT stylesheets that manage it.
Without going into too much detail, here are the most important aspects of a SOAP message:
- A SOAP message is an XML document.
- The whole transaction is enclosed in an
Envelope element.
- The envelope contains an optional
Header and a mandatory Body.
- The body contains the main transaction, which can be an RPC call or any other XML document.
- The header contains additional information that is useful in processing the call, such as
security, authentication, session information, or any other useful data.
- The server is free to ignore all or some of the header if it does not recognize it (for instance, a stateless server may ignore session information from the header).
- It's not always sensible to ignore the header (for instance, you might not want to proceed with the request unless security information is acted upon). Those header elements that cannot be ignored
must be marked with the
mustUnderstand="1" attribute.
- You may optionally specify how the header was created
with the
encodingStyle attribute.
- When an error occurs, the body will contain a
fault
element.
- The server replies with another message that includes the same envelope, header, and body.
 |
W3C recommendation
SOAP 1.2 was approved as a W3C recommendation in June 2003. Most of changes between
SOAP 1.1 (the non-official version) and SOAP 1.2 are clarifications on the
original specification.
The namespace URI for SOAP elements was also changed from http://schemas.xmlsoap.org/soap/envelope/ to http://www.w3.org/2003/05/soap-envelope.
|
|
To illustrate how to create SOAP requests with XSLT, I used the AXIS toolkit from Apache, version 1.1 (it originated as an IBM project that was later donated to the Apache Foundation). AXIS was primarily designed for JAX-RPC and, for the time being, it only implements a subset of JAXM.
Note that AXIS version 1.1 implements SOAP 1.1, not the newest W3C-approved revision.
AXIS ships with many samples. To test this lightweight client, I decided to re-implement the client for the calculator service. The calculator service is the second example in the AXIS user guide, so it's not too difficult to get started. The service accepts two numbers for addition or subtraction. The stylesheet accepts a text file with two lines only, as illustrated in Listing 2:
Listing 2. Request parameters in a text file
The first line contains the + or - symbol. The second line contains the two numbers separated by a comma.
The text file contains the request parameters, but it's not SOAP-encoded yet. SOAP encoding is done by the stylesheet in Listing 3, which simply creates the envelope, body, and body request, as discussed earlier in this section.
Listing 3. Preparing the SOAP transaction
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xi="http://ananas.org/2002/xi/rules"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://ananas.org/2003/xi/params"
version="1.0">
<xi:rules version="1.0"
defaultPrefix="p"
targetNamespace="http://ananas.org/2003/xi/params">
<xi:ruleset name="data">
<xi:match name="plus" pattern="^\+$"/>
<xi:match name="minus" pattern="^\-$"/>
<xi:match name="line">
<xi:filler pattern="^"/>
<xi:group pattern=".*" name="op1"/>
<xi:filler pattern=","/>
<xi:group pattern=".*" name="op2"/>
<xi:filler pattern="$"/>
</xi:match>
<xi:error message="could not build request, please check file"/>
</xi:ruleset>
</xi:rules>
<xsl:template match="/">
<soapenv:Envelope>
<soapenv:Body>
<xsl:apply-templates/>
</soapenv:Body>
</soapenv:Envelope>
</xsl:template>
<xsl:template match="p:data">
<add soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<op1 xsi:type="xsd:int"><xsl:value-of select="p:line/p:op1"/></op1>
<op2 xsi:type="xsd:int"><xsl:value-of select="p:line/p:op2"/></op2>
</add>
</xsl:template>
<xsl:template match="p:data[p:minus]">
<subtract soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<op1 xsi:type="xsd:int"><xsl:value-of select="p:line/p:op1"/></op1>
<op2 xsi:type="xsd:int"><xsl:value-of select="p:line/p:op2"/></op2>
</subtract>
</xsl:template>
</xsl:stylesheet>
|
The vocabulary for the request body is specified by the server. In this particular case, it's a SOAP RPC request, but I did not spent much time on it; I worked from a sample request and simply inserted the <xsl:value-of> instructions where they made sense.
The response is processed by a second stylesheet, shown in Listing 4. This one is more complex because it also does error processing. Notice the template for the header in the listing that catches fault elements (an error report from the server). It also checks that there is no header element with a mustUnderstand attribute. This is the bare minimum error processing for SOAP. The other templates in Listing 4 extract the response from the document and format it.
Listing 4. Decoding the SOAP response
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
version="1.0">
<xsl:output method="html"/>
<xsl:template match="soapenv:Envelope">
<html>
<head><title>Math result</title></head>
<body>
<xsl:apply-templates
select="soapenv:Header//node()[soapenv:mustUnderstand='1']"/>
<xsl:apply-templates select="soapenv:Body"/>
</body>
</html>
</xsl:template>
<xsl:template match="soapenv:Header//node()">
<p>Unknown header data: <xsl:copy-of select="."/></p>
</xsl:template>
<xsl:template match="soapenv:Body[soapenv:Fault]">
<p>Error, could not process the document.<br/>
Error message was: <xsl:value-of select="soapenv:Fault/faultcode"/><br/>
<xsl:value-of select="soapenv:Fault/faultstring"/></p>
</xsl:template>
<xsl:template match="soapenv:Body">
<p>The answer is: <xsl:value-of select="*/*"/></p>
</xsl:template>
</xsl:stylesheet>
|
Is it worth it?
The value of the lightweight client may not be obvious at this point because I have used a very simple (some would say simplistic) example.
By a lucky coincidence, I'm writing this article after a review meeting with a customer where we implemented a lightweight XML client. Obviously, their process is more complex than what I have shown here. Still, the technical principles are similar. More importantly, they have found that the lightweight client allows them to reach many more business partners, which increases the value of their solution immensely.
Resources
About the author
Rate this page
|