The Post Manager

   

Post is the first software installed at the seller site. It consists of a servlet and a style sheet.

Accepting Post

The servlet is presented in Listing 7.2. It waits for POST requests for XML documents and stores them in a database.

Listing 7.2 Post.java
 package com.psol.xcommerce; import java.io.*; import java.sql.*; import org.xml.sax.*; import javax.servlet.*; import javax.servlet.http.*; import org.apache.xalan.xslt.*; public class Post    extends HttpServlet {    public void init()       throws ServletException    {       try       {          Class.forName(getInitParameter("driver"));       }       catch(ClassNotFoundException e)       {          throw new ServletException(e);       }    }    protected String style(String document,                           String stylesheet)       throws IOException, SAXException    {       XSLTProcessor processor =          XSLTProcessorFactory.getProcessor();       XSLTInputSource source =          new XSLTInputSource(new StringReader(document));       XSLTInputSource styleSheet =          new XSLTInputSource(new FileInputStream(stylesheet));       StringWriter writer = new StringWriter();       XSLTResultTarget target = new XSLTResultTarget(writer);       processor.process(source,styleSheet,target);       return writer.toString();    }    public void doPost(HttpServletRequest request,                       HttpServletResponse response)       throws IOException    {       Writer writer = response.getWriter();       response.setContentType("text/xml");       try       {          String original = request.getParameter("document"),                 address = request.getParameter("address");          String url = getInitParameter("url"),                 username = getInitParameter("username"),                 password = getInitParameter("password"),                 stylesheet = getInitParameter("stylesheet");          Connection connection =             DriverManager.getConnection(url,username,password);          try          {             PreparedStatement stmt =                connection.prepareStatement("insert into " +                     "documents (original, document, address, new) " +                     "values (?,?,?,true)");             try             {                stmt.setString(1,original);                if(stylesheet != null)                {                   String document = style(original,stylesheet);                   stmt.setString(2,document);                }                else                   stmt.setNull(2,Types.VARCHAR);                stmt.setString(3,address);                stmt.executeUpdate();                connection.commit();             }             finally             {                stmt.close();             }             writer.write("<result error='false'>ok</result>");          }          finally          {             connection.close();          }       }       catch(SQLException e)       {          writer.write("<result error='true'><![CDATA[");          writer.write(e.getMessage());          writer.write("]]></result>");       }       catch(SAXException e)       {          writer.write("<result error='true'><![CDATA[");          writer.write(e.getMessage());          writer.write("]]></result>");       }       writer.flush();    } } 

The servlet expects two parameters: the XML document itself and a return address. The return address will be used to send invoices and other documents related to this order:

 String original = request.getParameter("document"),          address = request.getParameter("address"); 

Usually, a company has several customers. So, in practice, not all these customers will use the same XML documents. Some might use the invoice model introduced previously, while others might use completely different models. Furthermore, even if everybody uses the same format, different versions will coexist. This is illustrated in Figure 7.6.

The easiest solution to dealing with this multitude of formats is to convert them to the company's own format as soon as they are received. So only one conversion is necessary, upon receiving the message.

Figure 7.6. You face a multitude of slightly different variations.

graphics/07fig06.gif

Therefore, the servlet starts by applying a style sheet. It also stores the document twice ”in its original form and in the converted version. It is good practice to store the document as you received it, in case of later disputes:

 stmt.setString(1,original);   if(stylesheet != null)   {      String document = style(original,stylesheet);      stmt.setString(2,document);   }   else      stmt.setNull(2,Types.VARCHAR);   stmt.setString(3,address);   stmt.executeUpdate();   connection.commit(); 

Note

An alternative would be to parse the document and extract useful information, as we saw in Chapter 1, "Lightweight Data Storage." However, it is still advisable to store the original XML document, in case of later disputes.


Finally, the servlet returns an XML document to indicate success or failure:

 writer.write("<result error='false'>ok</result>"); 

Tip

A simple solution to enable security is to activate HTTP username/password on the Web server.


The Post Manager Style Sheet

The conversion style sheet is illustrated in Listing 7.3. It outputs an XML document; this is a conversion between two XML models.

Listing 7.3 tointernal.xsl
 <?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"                 version="1.0"> <xsl:output method="xml"/> <xsl:template match="/Order">    <Order>       <xsl:apply-templates select="@*node()" mode="identity"/>    </Order> </xsl:template> <xsl:template match="@*node()" mode="identity">   <xsl:copy>     <xsl:apply-templates select="@*node()" mode="identity"/>   </xsl:copy> </xsl:template> <xsl:template match="/PurchaseOrder">    <Order>       <Date><xsl:value-of select="PODate"/></Date>       <Reference><xsl:value-of select="Reference"/></Reference>       <Buyer>          <Name><xsl:value-of select="From/Company"/></Name>          <Address>             <Street><xsl:value-of                select="From/Address"/></Street>             <Locality><xsl:value-of                select="From/City"/></Locality>             <PostalCode><xsl:value-of                select="From/ZIP"/></PostalCode>             <Region><xsl:value-of                select="From/StateProvince"/></Region>             <Country>US</Country>          </Address>       </Buyer>       <Seller>          <Name><xsl:value-of select="To/Company"/></Name>          <Address>             <Street><xsl:value-of                select="To/Address"/></Street>             <Locality><xsl:value-of                select="To/City"/></Locality>             <PostalCode><xsl:value-of                select="To/ZIP"/></PostalCode>             <Region><xsl:value-of                select="To/StateProvince"/></Region>             <Country>US</Country>          </Address>       </Seller>       <Lines>          <xsl:for-each select="Books/Book">             <xsl:variable name="quantity"                           select="Unit"/>             <xsl:variable name="price"                           select="Price"/>             <Product>                <Description><xsl:value-of                   select="Title"/></Description>                <Quantity><xsl:value-of                   select="$quantity"/></Quantity>                <Price><xsl:value-of                   select="$price"/></Price>                <Total><xsl:value-of     select="format-number($quantity * $price,'#0.00')"/></Total>            </Product>          </xsl:for-each>       </Lines>       <Total><xsl:value-of select="ToPay"/></Total>    </Order> </xsl:template> </xsl:stylesheet> 

The style sheet uses template matches to control the conversion. This single style sheet can convert between any number of XML documents. The following template matches the seller's own format (it simply copies the document unmodified):

 <xsl:template match="/Order">    <Order>       <xsl:apply-templates select="@*node()" mode="identity"/>    </Order> </xsl:template> 

But this template matches a different structure:

 <xsl:template match="/PurchaseOrder">    <Order>       <Date><xsl:value-of select="PODate"/></Date>       <Reference><xsl:value-of select="Reference"/></Reference>       <!-- some part deleted --> 

Note

The selection procedure can be more involved. Remember that a match attribute can contain any XPath. For example, matching an element, its namespace, and the value of one of its attributes is as simple as

 <xsl:template match="/xmli:PurchaseOrder[@version='1.0']">        <!-- template deleted -->        <xsl:template match="/xmli:PurchaseOrder[@version='2.0'">        <!-- template deleted --> 

Tip

If writing many style sheets, you should acquire a visual XSL editor. For example, XSL Editor, from IBM (illustrated in Figure 7.7) has a drag-and-drop editor that greatly simplifies editing the style sheet. At the time of writing, XSL Editor is in pre-release and available from http://www.alphaworks.ibm.com. IBM will probably turn it into a commercial product, however.


Figure 7.7. Editing a style sheet is as simple as dragging and dropping elements.

graphics/07fig07.gif

Tip

To learn more about accepting non-XML documents, such as EDI orders, see Chapter 5, "Export to Any Format," and Chapter 6.


The Customer's End

The customer will have its own application to generate orders. The application is typically integrated with the ERP solution. However, in this chapter, we won't write an ERP package for the sake of demonstrating integration. Instead, we'll use a simple JavaScript-based editor (see Listing 7.8).

Warning

The JavaScript editor is a simplification. In practice, the buyer probably relies on a solution that is integrated with its own ERP package. However, in this chapter, we concentrate on the merchant, not the customer.


Listing 7.8 editorder.html
 <HTML> <HEAD><TITLE>Order</TITLE> <SCRIPT LANGUAGE="JavaScript"><!-- var products = new Array(); function addProduct(form) {    // collects data from the form    var title = form.title.value,        quantity = form.quantity.value,        price = form.price.value;    doAddProduct(form,title,quantity,price); } function doAddProduct(form,title,quantity,price) {    var productList = form.productlist,        product = new Product(title,quantity,price);    // arrays are zero-based so products.length points    // to one past the latest product    // JavaScript automatically allocates memory    var pos = products.length;    products[pos] = product;    var option = new Option(title + " (" + price +  ")",pos);    productList.options[productList.length] = option; } function deleteProduct(form) {    var productList = form.productlist,        pos = productList.selectedIndex;    if(pos != -1)    {       var product = productList.options[pos].value;       productList.options[pos] = null;       products[product] = null;    } } function exportProduct(form) {    var books = "";    var i,        total = 0;    for(i = 0;i < products.length;i++)       if(products[i] != null)       {          books += products[i].toXML();          total += products[i].quantity * products[i].price;       }    books = element("Books",books);    var from = element("Company",escapeXML(form.company.value));    from += element("Address",escapeXML(form.street.value));    from += element("City",escapeXML(form.city.value));    from += element("ZIP",escapeXML(form.zip.value));    from += element("StateProvince",escapeXML(form.state.value));    from = element("From",from);    var to = "<To><Company>QUE</Company>";    to += "<Address>201 West 103RD Street</Address>";    to += "<City>Indianapolis</City>";    to += "<ZIP>46290</ZIP>";    to += "<StateProvince>IN</StateProvince></To>";    var header = element("PODate",escapeXML(form.date.value));    header += element("Reference",                      escapeXML(form.reference.value));    var toPay = element("ToPay",escapeXML(String(total)));    var doc = element("PurchaseOrder",                      header + from + to + books + toPay);    form.document.value = "<?xml version='1.0'?>" + doc; } function element(name,content) {    var result = "<" + name +">";    result += content;    result += "</" + name +">\ r";    return result; } function escapeXML(string) {    var result = "",        i,        c;    for(i = 0;i < string.length;i++)    {       c = string.charAt(i);       if(c == '<')          result += "&lt;";       else if(c == '&')          result += "&amp;";       else          result += c;    }    return result; } // declares product object function Product(title,quantity,price) {    this.title = title;    this.quantity = quantity;    this.price = price;    this.toXML = product_toXML; } function product_toXML() {    var result = element("Title",escapeXML(this.title));    result += element("Unit",escapeXML(this.quantity));    result += element("Price",escapeXML(this.price));    return element("Book",result); } function load(form) {    doAddProduct(form,"XML by Example","5","24.99");    doAddProduct(form,"Applied XML Solutions","10","44.99"); } // --></SCRIPT> </HEAD> <BODY ONLOAD="load(document.controls)">    <CENTER>       <FORM NAME="controls" METHOD="POST"             ACTION="http://localhost:8080/post">          Title: <INPUT TYPE="TEXT" NAME="title"><BR>          Quantity: <INPUT TYPE="TEXT" NAME="quantity"><BR>          Price: <INPUT TYPE="TEXT" NAME="price"><BR>          <SELECT NAME="productlist" SIZE="5"                  WIDTH="250"></SELECT><BR>          <INPUT TYPE="BUTTON" VALUE="Add"                 ONCLICK="addProduct(controls)">          <INPUT TYPE="BUTTON" VALUE="Delete"                 ONCLICK="deleteProduct(controls)"><BR>          Date: <INPUT TYPE="TEXT" NAME="date"                          VALUE="2000-03-31"><BR>          Reference: <INPUT TYPE="TEXT" NAME="reference"                          VALUE="AGL153"><BR>          Company: <INPUT TYPE="TEXT" NAME="company"                          VALUE="Books and More"><BR>          Address: <INPUT TYPE="TEXT" NAME="street"                          VALUE="43 Fountain Street"><BR>          City: <INPUT TYPE="TEXT" NAME="city"                       VALUE="Cincinnati"><BR>          ZIP: <INPUT TYPE="TEXT" NAME="zip" VALUE="45202"><BR>          State/Province: <INPUT TYPE="TEXT" NAME="state"                                 VALUE="OH"><BR>          Return address: <INPUT TYPE="TEXT"                                 VALUE="http://localhost:8081"                                 NAME="address"><BR>          <INPUT TYPE="SUBMIT" VALUE="Post"                 ONCLICK="exportProduct(controls)">          <INPUT TYPE="HIDDEN" NAME="document">       </FORM>    </CENTER> </BODY></HTML> 

This Web page is a poor man's editor for purchase order. It is illustrated in Figure 7.8.

Note

This editor is written in portable JavaScript. It does not rely on the availability of an XML parser, so it should run with most browsers.


Figure 7.8. The editor to create and edit purchase orders.

graphics/07fig08.gif

Let's review Listing 7.8 step by step. The script maintains an array of Product objects, where Product s have a title, quantity, and price:

 function Product(title,quantity,price)   {      this.title = title;      this.quantity = quantity;      this.price = price;      this.toXML = product_toXML;   } 

The script uses functions to add and remove products from the array. The functions take care to synchronize the display, in the HTML form, and the content of the array:

 function doAddProduct(form,title,quantity,price)    {       var productList = form.productlist,           product = new Product(title,quantity,price);       // arrays are zero-based so products.length points       // to one past the latest product       // JavaScript automatically allocates memory       var pos = products.length;       products[pos] = product;       var option = new Option(title + " (" + price + ")",pos);       productList.options[productList.length] = option;    } 

To send the purchase order, the script writes the corresponding XML document in a hidden field of the form. The content of the form is posted to the server by the Web browser and, of course, includes the hidden field and XML document:

 function exportProduct(form)    {       var books = "";       var i,           total = 0;       for(i = 0;i < products.length;i++)          if(products[i] != null)          {             books += products[i].toXML();             total += products[i].quantity * products[i].price;          }       books = element("Books",books);       var from = element("Company",escapeXML(form.company.value));       from += element("Address",escapeXML(form.street.value));       from += element("City",escapeXML(form.city.value));       from += element("ZIP",escapeXML(form.zip.value));       from += element("StateProvince",escapeXML(form.state.value));       from = element("From",from);       var to = "<To><Company>QUE</Company>";       to += "<Address>201 West 103RD Street</Address>";       to += "<City>Indianapolis</City>";       to += "<ZIP>46290</ZIP>";       to += "<StateProvince>IN</StateProvince></To>";       var header = element("PODate",escapeXML(form.date.value));       header += element("Reference",                         escapeXML(form.reference.value));       var toPay = element("ToPay",escapeXML(String(total)));       var doc = element("PurchaseOrder",                         header + from + to + books + toPay);       form.document.value = "<?xml version='1.0'?>" + doc;    } 
   


Applied XML Solutions
Applied XML Solutions
ISBN: 0672320541
EAN: 2147483647
Year: 1999
Pages: 142

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net