The ZwiftBooks Challenge
Having seen how REST and SOAP can be used to define a web services infrastructure, let’s look at how we can use these technologies to build a ZwiftBooks web service. As resident XML guru, you’ve been tasked with working with the IT department to establish a quote system based on web services. The goal is for clients to be able to query the ZwiftBooks server with ISBN and ZIP code and get back a price and time quote for any book.
Gary, the chronically negative IT manager, hasn’t taken fondly to this new initiative. He claims that the project will require ZwiftBooks to build a database of all books in print, and to integrate it with an auxiliary database of list prices. He estimates that will take at least 12 months and two additional programmers to implement such a system. Derisively, he turns to the VP of technology and sneers, "Unless maybe our XML guru here has one of his XML solutions."
All eyes are on you as you calmly walk up to the whiteboard and draw the diagram shown in Figure 3, illustrating how ZwiftBooks can leverage the existing Amazon Web Services to obtain the list price of any book based on ISBN.
Figure 3 The ZwiftBooks price/time web service based on repackaging price information from Amazon.com.
Your recommendation is to set up the ZwiftBooks web service as a front end to the Amazon.com service, extracting the list price of the book and adding a fixed percentage for delivery and a fixed delivery time. This will allow ZwiftBooks to return a price quote for any ISBN in the Amazon.com database. Once the basic system is in place, a little additional work can be done to filter the data based on books in the warehouse and location, but the essential infrastructure can be set up in the time it takes to deploy Java servlet code that queries the Amazon server, adds $50 to the list price, and defaults to some arbitrary but doable delivery time. The basic code is shown in Listing 3. Here’s the output:
<?xml version=’1.0’ encoding=’utf-8’?> <quote xmlns="http://www.zwiftbooks.com"> <price>89.00</price> <time>2 days</time> <priceTime>89.00 in 2 days</priceTime> </quote>
Gary excuses himself for a cup of coffee as you enter the program and proceed to demo it. The VP of technology is impressed. To see your prototype in action, enter the following REST query string into a browser and point it at the ZwiftBooks web service.
http://www.zwiftbooks.com/ws/pricetime?isbn=0201776413&zip=90210
***Don’t link the code line above.
Pretty amazing what a little XML can do!
Listing 3 Java code for the ZwiftBooks price/time quote web service.
import java.io.FileReader; import java.util.Iterator; import javax.xml.stream.*; import javax.xml.namespace.QName; import com.bea.xml.stream.util.ElementTypeNames; import java.io.ByteArrayInputStream; import javax.xml.stream.*; import java.net.*; import java.io.*; /** * Java code that repackages price information obtained * from an Amazon.com web service * */ public class ZBGetPriceTimeQuote { private static boolean foundPriceElement = false; private static boolean foundPriceValue = false; public static void main(String[] args) throws Exception { String isbn = "0201776413"; // Any valid ISBN will do // From Listing 1 String xmlstring = executeAmazonWebService(isbn); String price = extractPrice(xmlstring); System.out.println("Amazon price = " + price); // Add $50 to the Amazon price int amazonPrice = (int)Double.parseDouble(price.substring(1, price.length()) ); amazonPrice+=50; String zbPrice = String.valueOf(amazonPrice) + ".00"; // Generate XML based on the computed price generateZBPriceTimeQuote(zbPrice); } public static String extractPrice(String xmlstring) throws Exception { String resultString = "Not Found"; // Specify the Parser Factory we want to use System.setProperty("javax.xml.stream.XMLInputFactory", "com.bea.xml.stream.MXParserFactory"); // Get an input factory XMLInputFactory xmlif = XMLInputFactory.newInstance(); // Turn our string of XML into a byte array and feed // it directly to our parser/reader byte [] buf = xmlstring.getBytes(); ByteArrayInputStream xmlstream = new ByteArrayInputStream(buf); // Instantiate a reader XMLStreamReader xmlr = xmlif.createXMLStreamReader(xmlstream); // Use StAX to parse the XML - looking for Amazon ListPrice element while(xmlr.hasNext()) { switch (xmlr.getEventType()) { case XMLStreamConstants.START_ELEMENT: if(xmlr.hasName()){ String localName = xmlr.getLocalName(); if (localName.equals("ListPrice")) { foundPriceElement = true; } // end if } break; case XMLStreamConstants.END_ELEMENT: if(xmlr.hasName()){ String localName = xmlr.getLocalName(); if (localName.equals("ListPrice")) { foundPriceElement = false; } // end if } break; case XMLStreamConstants.CHARACTERS: if (foundPriceElement) { resultString = xmlr.getText(); foundPriceValue = true; } break; default: break; } // end switch // If we have located the price, no need to keep parsing, // one of the advantages of StAX if (foundPriceValue) break; else xmlr.next(); // Keep parsing } // end parsing loop return resultString; } public static void generateZBPriceTimeQuote (String zbPrice throws Exception { String ZWIFTBOOKS = "http://www.zwiftbooks.com"; // Create an output factory XMLOutputFactory xmlof = XMLOutputFactory.newInstance(); // Create an XML stream writer XMLStreamWriter xmlw = xmlof.createXMLStreamWriter(System.out); // Write XML prologue xmlw.writeStartDocument(); // Now start with root element xmlw.writeStartElement("quote"); // Set the namespace definitions to the root element // Declare the default namespace in the root element xmlw.writeDefaultNamespace(ZWIFTBOOKS); xmlw.writeStartElement("price"); xmlw.writeCharacters(zbPrice); xmlw.writeEndElement(); xmlw.writeStartElement("time"); xmlw.writeCharacters("2 days"); xmlw.writeEndElement(); xmlw.writeStartElement("priceTime"); xmlw.writeCharacters(zbPrice + " in 2 days"); xmlw.writeEndElement(); xmlw.writeEndElement(); // Write document end. This closes all open structures xmlw.writeEndDocument(); // Close the writer to flush the output xmlw.close(); } // Method from Listing 2 public static String executeAmazonWebService(String isbnString) throws Exception { .... } }