Prior to this project, I had delivered several technical presentations to user groups and conferences regarding SOAP (Simple Object Access Protocol) and web service architecture. So, when the call came in, it seemed I'd be a natural fit for what the client was looking to accomplish. Once I understood what they really needed, though, I decided that they would be much better off with a set of services exposed through simple GET and POST requests over HTTP, exchanging XML data describing the requests and responses. Although I didn't know it at the time, this architectural style is now commonly referred to as REST, or Representational State Transfer.
How did I decide to use REST over SOAP? Here are a few of the decision points to consider when choosing a Web Services architecture:
How many different systems will require access to these services, and are all of them known at this time?
This manufacturer knew of a single distributor that needed to access its systems, but also acknowledged that others might decide to do the same in the future.
Do you have a tight set of end users that will have advance knowledge of these services, or do these services need to be self-describing for anonymous users to automatically connect to?
Because there has to be a defined relationship between the manufacturer and all its distributors, it is guaranteed that each of the potential users will have advance knowledge of how to access the manufacturer's systems.
What kind of state needs to be maintained throughout a single transaction? Will one request depend on the results of a previous one?
In our case, each transaction will consist of a single request and a corresponding result that doesn't depend on anything else.
Answering the above questions for this project yielded the obvious choice of simply exposing a set of known services over the HTTP protocol and exchanging data using a standard e-business protocol that both systems could understand. If the manufacturer would have liked to allow anonymous users to query product availability, then I might have opted for a full SOAP solution because that would allow systems to discover the services and programmatically interface with them without prior knowledge of the systems.
I currently work in the field of bioinformatics, where there is a definite need for SOAP-style Web Service architectures. We make use of a project called BioMoby (http://www.biomoby.org) to define Web Services and publish them to a central repository that allows other groups to literally drag and drop our services into a workflow that builds data pipelines to help biologists integrate diverse sets of data and perform varied analysis on the results. This is a perfect example of why someone would choose SOAP over REST. Anonymous users can access our data and tools without any prior knowledge that they even existed.
The first step, as I decided how to implement this software, was to determine how the users will make requests and receive responses. After speaking with a technical representative from the distributor (the primary user), I learned that its new system can send XML documents via an HTTP POST request and examine the results as an XML document. The XML had to be in a format following the Rosettanet e-business protocol (more on that later), but for now it was enough to know that it can communicate over HTTP by posting XML-formatted requests and responses. Figure 27-1 illustrates the general interaction between each of the systems.
The manufacturer had recently been acquired by a larger corporation that dictated the use of IBM products throughout the organization. Therefore, I already knew what application server and corresponding technology to use. I implemented the service interface as a Java Servlet running on an IBM WebSphere application server. This decision was made easier by my knowledge that the software would need to access functions running on an AS/400 server using a Java-based API.
The following code is found in the web.xml file describing the servlet that will provide the necessary interface to the users:
<servlet> <servlet-name>HotKeyService</servlet-name> <display-name>HotKeyService</display-name> <servlet-class>com.xxxxxxxxxxxx.hotkey.Service</servlet-class> </servlet> <servlet-mapping> <servlet-name>HotKeyService</servlet-name> <url-pattern>/HotKeyService</url-pattern> </servlet-mapping>
The servlet itself handles only POST requests, which it does by overriding the doPost method of the Servlet interface and providing default implementations of the standard life cycle methods. The following code shows the complete implementation of the service, but when I first start breaking down a problem and designing a solution, I typically write a series of comments in the code as placeholders where I'll insert the real code later. I then systematically attack each pseudocode comment until I have a working implementation. This helps keep me focused on how each piece relates to the entire solution:
public class Service extends HttpServlet implements Servlet {
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// Read in request data and store in a StringBuffer
BufferedReader in = req.getReader();
StringBuffer sb = new StringBuffer();
String line;
while ((line = in.readLine())!= null) {
sb.append(line);
}
HotkeyAdaptor hotkey = null;
if (sb.toString().indexOf("Pip3A2PriceAndAvailabilityRequest") > 0) {
// Price and Availability Request
hotkey = HotkeyAdaptorFactory.getAdaptor(
HotkeyAdaptorFactory.ROSETTANET,
HotkeyAdaptorFactory.PRODUCTAVAILABILITY);
}
else if (sb.toString().indexOf("Pip3A5PurchaseOrderStatusQuery ") > 0) {
// Order Status
hotkey = HotkeyAdaptorFactory.getAdaptor(
HotkeyAdaptorFactory.ROSETTANET,
HotkeyAdaptorFactory.ORDERSTATUS);
}
boolean success = false;
if (hotkey != null) {
/* Pass in the XML request data */
hotkey.setXML(sb.toString());
/* Parse the request data */
if (hotkey.parseXML()) {
/* Execute AS/400 Program */
if (hotkey.executeQuery()) {
/* Return response XML */
resp.setContentType("text/xml");
PrintWriter out = resp.getWriter();
out.println(hotkey.getResponseXML());
out.close();
success = true;
}
}
}
if (!success) {
resp.setContentType("text/xml");
PrintWriter out = resp.getWriter();
out.println("Error retrieving product availability.");
out.close();
}
}
}
Looking through this code, you can see that it first reads in the request data and stores it for later use. It then searches this data to determine which type of request this is: pricing and availability, or an order status inquiry. Once it determines the type of request, the appropriate helper object is created. Notice how I used an interface, HotkeyAdaptor, to allow multiple implementations without having to write a bunch of duplicate code for each type of request.
The rest of this method involves parsing the XML request data, executing the appropriate query on the AS/400 system, creating an XML response, and writing it back to the user via HTTP. In the next section, you'll see how I hid the implementation details using interfaces and the very popular factory design pattern.