The Stock Server

   

The Stock Client

This section presents a simple client. In practice, the wholesaler would not write the client. It would be left to the retailers to try to integrate the SOAP server in their existing applications. However, for completeness and to enable testing, we will write a simple SOAP client.

Figure 9.6 illustrates the stock client. As always, little effort has gone into the user interface so that the XML is more visible. The beauty of this client, however, is that it integrates the local (retailer) and remote (wholesaler) databases.

Figure 9.6. The client provides an integrated view of the retailer and wholesaler data bases.

graphics/09fig06.gif

The client application starts by looking in the local database. It is only if the product is not available locally that it connects to the wholesaler site and inquires about remote availability. The client interprets the results as follows :

  • If the product is in stock locally, the client announces shipment within 24 hours.

  • If the product is not available locally but is available from the wholesaler, the client reports shipment within 2 “3 days.

  • If the product is back-ordered both locally and at the wholesaler, the client announces shipment within 3 “5 weeks.

  • Finally, if the product is not available with the wholesaler, the client announces a special order.

SoapRequest

SoapRequest , in Listing 9.10, implements the SOAP protocol for requests : It prepares the request, sends it over HTTP (as a POST request, ensuring it includes the required SOAPAction header), and uses SoapEnvelope to decode the response.

Listing 9.10 SoapRequest.java
 package com.psol.stockq; import java.io.*; import java.net.*; import java.util.*; import org.xml.sax.*; import org.xml.sax.helpers.*; public abstract class SoapRequest    extends DefaultHandler {    protected String soapAction;    public SoapRequest(String soapAction)    {       if(null == soapAction)          soapAction = "\ "\ "";       this.soapAction = '"'+ soapAction + '"';    }    public void invoke(URL server)       throws IOException, SoapException, SAXException    {       HttpURLConnection conn =          (HttpURLConnection)server.openConnection();       conn.setDoOutput(true);       conn.setDoInput(true);       CharArrayWriter payload = new CharArrayWriter();       payload.write("<?xml version='1.0'?>");       payload.write("<SOAP-ENV:Envelope xmlns:SOAP-ENV='");       payload.write(Constants.SOAPENV_URI);       payload.write("'><SOAP-ENV:Body>");       writeRequest(new XMLWriter(payload));       payload.write("</SOAP-ENV:Body></SOAP-ENV:Envelope>");       conn.setRequestProperty("Content-Length",                               String.valueOf(payload.size()));       conn.setRequestMethod("POST");       conn.setFollowRedirects(true);       conn.setRequestProperty("Content-Type","text/xml");       conn.setRequestProperty("SOAPAction",                               '"'+ soapAction + '"');       Writer writer =          new OutputStreamWriter(conn.getOutputStream(),"UTF-8");       payload.writeTo(writer);       writer.flush();       conn.connect();       XMLReader xmlReader =          XMLReaderFactory.createXMLReader(Constants.SAXPARSER);       xmlReader.setFeature(Constants.SAXNAMESPACES,true);       SoapEnvelope soapEnvelope = new SoapEnvelope();       soapEnvelope.setParent(xmlReader);       soapEnvelope.setContentHandler(this);       InputSource is = new InputSource(conn.getInputStream());       soapEnvelope.parse(is);    }    public abstract void writeRequest(XMLWriter writer)       throws IOException; } 

Actual requests must inherit from SoapRequest and overwrite its writeRequest() method.

Note that SoapRequest inherits from DefaultHandler and registers itself as a ContentHandler while parsing the response. Therefore, descendants should implement ContentHandler to extract data they need:

 XMLReader xmlReader =    XMLReaderFactory.createXMLReader(Constants.SAXPARSER); xmlReader.setFeature(Constants.SAXNAMESPACES,true); SoapEnvelope soapEnvelope = new SoapEnvelope(); soapEnvelope.setParent(xmlReader); soapEnvelope.setContentHandler(this); InputSource is = new InputSource(conn.getInputStream()); soapEnvelope.parse(is); 

Warning

SoapRequest uses HttpURLConnection to execute the POST request. There is, however, one caveat in using HttpURLConnection : If the server replies with a 500 code (for example, if a SOAP Fault has occurred), HttpURLConnection throws an exception, and the application does not have a chance to parse the Fault object.

This is a hotly debated issue with SOAP. SOAP mandates the use of HTTP return codes, but many proxies and libraries already interpret these codes and might choose to discard the SOAP-ENV:Fault content.


StockRequest

As its name implies, StockRequest , in Listing 9.11, enables SoapRequest to handle stock requests.

Listing 9.11 StockRequest.java
 package com.psol.stockq; import java.io.*; import java.awt.*; import org.xml.sax.*; import org.xml.sax.helpers.*; public class StockRequest    extends SoapRequest {    protected StringBuffer available = null,                           level = null;    protected String manufacturer = null,                     sku = null,                     responseTag = null,                     responseNamespaceURI = null,                     resultTag = null,                     resultNamespaceURI = null;    // ignore the manufacturer & sku    protected final static int NONE = 0,                               RESPONSE = 1,                               RESULT = 2,                               AVAILABLE = 3,                               LEVEL = 4;    protected int status = NONE;    protected Label message;    public StockRequest(Label message)    {       super(Constants.PSOL_URI);       this.message = message;    }    public void startDocument()       throws SAXException    {       status = NONE;       available = null;       level = null;       manufacturer = null;       sku = null;       responseTag = null;       resultTag = null;    }    public void startElement(String namespaceURI,                             String localName,                             String rawName,                             Attributes atts)       throws SAXException    {       if(NONE == status)       {          status = RESPONSE;          responseNamespaceURI = namespaceURI;          responseTag = localName;       }       else if(RESPONSE == status &&               null == resultTag)       {          resultNamespaceURI = namespaceURI;          resultTag = localName;          status = RESULT;       }       else if(RESULT == status &&               rawName.equals("available") &&               null == available)       {          status = AVAILABLE;          available = new StringBuffer();       }       else if(RESULT == status &&               rawName.equals("level") &&               null == level)       {          status = LEVEL;          level = new StringBuffer();       }    }    public void endElement(String namespaceURI,                           String localName,                           String rawName)       throws SAXException    {       if(namespaceURI.equals(resultNamespaceURI) &&          localName.equals(resultTag) &&          RESPONSE == status)          status = NONE;       else if(namespaceURI.equals(responseNamespaceURI) &&               localName.equals(responseTag) &&               RESULT == status)       {          status = RESPONSE;          if(Boolean.valueOf(                available.toString()).booleanValue())          {             int lvl = Integer.parseInt(level.toString());             if(lvl <= 0)                message.setText("Ships in 3-5 weeks");             else                message.setText("Ships in 2-3 days");          }          else             message.setText("Special order");       }       else if(rawName.equals("available") &&               status == AVAILABLE)          status = RESULT;       else if(rawName.equals("level") &&               status == LEVEL)          status = RESULT;    }    public void characters(char[] ch,int start,int len)       throws SAXException    {       if(AVAILABLE == status)          available.append(ch,start,len);       else if(LEVEL == status)          level.append(ch,start,len);    }    public void setManufacturer(String manufacturer)    {       this.manufacturer = manufacturer;    }    public void setSku(String sku)    {       this.sku = sku;    }    public void writeRequest(XMLWriter writer)       throws IOException    {       writer.write("<psol:getStock xmlns:psol='");       writer.write(Constants.PSOL_URI);       writer.write("'SOAP-ENV:encodingStyle='");       writer.write(Constants.SOAPENCODING_URI);       writer.write("'><manufacturer>");       writer.escape(manufacturer);       writer.write("</manufacturer><sku>");       writer.escape(sku);       writer.write("</sku></psol:getStock>");    } } 

StockRequest parses getStockResponse . It does not worry about SoapEnvelope because the filter has already taken care of these events.

Parsing the response is not as easy as parsing the request. The SOAP protocol specifies that the names of the response and result elements are irrelevant ”they could be anything. startElement() and endElement() must be careful not to make any assumptions about them. In practice, startElement() uses variables to dynamically record the names of the elements (an alternative would be to manage a stack):

 public void startElement(String namespaceURI,                          String localName,                          String rawName,                          Attributes atts)    throws SAXException {    if(NONE == status)    {       status = RESPONSE;       responseNamespaceURI = namespaceURI;       responseTag = localName;    }    else if(RESPONSE == status &&            null == resultTag)    {       resultNamespaceURI = namespaceURI;       resultTag = localName;       status = RESULT;    }    else if(RESULT == status &&            rawName.equals("available") &&            null == available)    {       status = AVAILABLE;       available = new StringBuffer();    }    else if(RESULT == status &&            rawName.equals("level") &&            null == level)    {       status = LEVEL;       level = new StringBuffer();    } } 

endElement() analyzes the result and computes the expected shipment dates for the user. Note that this ContentHandler ignores the manufacturer and sku elements in the response:

 else if(namespaceURI.equals(responseNamespaceURI) &&         localName.equals(responseTag) &&          RESULT == status) {    status = RESPONSE;    if(Boolean.valueOf(          available.toString()).booleanValue())    {       int lvl = Integer.parseInt(level.toString());       if(lvl <= 0)          message.setText("Ships in 3-5 weeks");       else          message.setText("Ships in 2-3 days");    }    else       message.setText("Special order"); } 

StockQPanel

StockQPanel , in Listing 9.12, supports the user interface for the client application.

Listing 9.12 StockQPanel.java
 package com.psol.stockq; import java.io.*; import java.sql.*; import java.net.*; import java.awt.*; import org.xml.sax.*; import java.awt.event.*; public class StockQPanel    extends Panel {    protected Connection connection;    protected Choice products;    protected TextComponent server;    protected Label message;    public StockQPanel(Connection connection)       throws ClassNotFoundException, SQLException    {       this.connection = connection;       setLayout(new BorderLayout());       Panel topFields = new Panel();       topFields.setLayout(new GridLayout(2,2));       topFields.add(new Label("Server:"));       server = new TextField("http://localhost:8080/stockq");       topFields.add(server);       topFields.add(new Label("Products:"));       products = new Choice();       topFields.add(products);       Panel bottomFields = new Panel();       bottomFields.setLayout(new GridLayout(2,1));       Button check = new Button("Check");       bottomFields.add(check);       message = new Label("No product selected");       bottomFields.add(message);       add(topFields,"Center");       add(bottomFields,"South");       check.addActionListener(new ActionListener()       {          public void actionPerformed(ActionEvent evt)          {             checkStockLevel();          }       } );       Statement stmt = connection.createStatement();       try       {          ResultSet rs = stmt.executeQuery(             "select name from products");          try          {             while(rs.next())                products.addItem(rs.getString(1));          }          finally          {             rs.close();          }       }       finally       {          stmt.close();       }    }    public void checkStockLevel()    {       message.setText("Checking...");       try       {          PreparedStatement stmt =             connection.prepareStatement("select level, " +                "manufacturer, sku from products where name=?");          try          {             stmt.setString(1,products.getSelectedItem());             ResultSet rs = stmt.executeQuery();             try             {                if(rs.next())                {                   if(rs.getInt(1) > 0)                      message.setText("Ships in 24 hours");                   else                   {                      URL url = new URL(server.getText());                      StockRequest request =                         new StockRequest(message);                      request.setManufacturer(                         rs.getString(2));                      request.setSku(rs.getString(3));                      request.invoke(url);                   }                }             }             finally             {                rs.close();             }          }          finally          {             stmt.close();          }       }       catch(SQLException e)       {          message.setText(e.getMessage());       }       catch(IOException e)       {          message.setText(e.getMessage());       }       catch(SoapException e)       {          message.setText(e.getCode() + ''+                          e.getMessage());       }       catch(SAXException e)       {          Exception ex = null == e.getException() ?                         e : e.getException();          message.setText(e.getMessage());       }    } } 

checkStockLevel() packs all the fun. When the user clicks the button, it queries the local database for the local stock level. If it finds that the product is in back order locally, it sends a SOAP request to the wholesaler:

 PreparedStatement stmt =    connection.prepareStatement("select level, " +       "manufacturer, sku from products where name=?"); try {    stmt.setString(1,products.getSelectedItem());    ResultSet rs = stmt.executeQuery();    try    {       if(rs.next())       {          if(rs.getInt(1) > 0)             message.setText("Ships in 24 hours");          else          {             URL url = new URL(server.getText());             StockRequest request = new StockRequest(message);             request.setManufacturer(rs.getString(2));             request.setSku(rs.getString(3));             request.invoke(url);          }       }    }    finally    {       rs.close();    } } finally {    stmt.close(); } 

StockQClient

StockQClient , in Listing 9.13, is the main() method of the application. It opens a window, opens a connection to the local database, and adds the StockQPanel to the window.

Listing 9.13 StockQClient.java
 package com.psol.stockq; import java.io.*; import java.net.*; import java.sql.*; import java.awt.*; import java.util.*; import org.xml.sax.*; import java.awt.event.*; import org.xml.sax.helpers.*; public class StockQClient {    public final static void main(String args[])       throws IOException, SoapException, SAXException,              ClassNotFoundException, SQLException    {       Properties properties = new Properties();       properties.load(new FileInputStream("./cfg/client.prp"));       Class.forName(properties.getProperty("driver"));       String url = properties.getProperty("url"),              username = properties.getProperty("username"),              password = properties.getProperty("password");       Connection connection =          DriverManager.getConnection(url,username,password);       try       {          Frame frame = new Frame();          frame.add(new StockQPanel(connection));          frame.pack();          frame.setTitle("StockQ Client");          frame.addWindowListener(new WindowAdapter()          {             public void windowClosing(WindowEvent evt)             {                System.exit(0);             }          } );          frame.show();          try          {             Thread.currentThread().join();          }          catch(InterruptedException e)             { }       }       finally       {          connection.close();       }    } } 

Constants

Constants , in Listing 9.14, is a list of constants used throughout the project.

Listing 9.14 Constants.java
 package com.psol.stockq; public class Constants {    public static final String PSOL_URI =       "http://www.psol.com/xmlns/stockq";    public static final String SOAPENV_URI =       "http://schemas.xmlsoap.org/soap/envelope/";    public static final String SOAPENCODING_URI =       "http://schemas.xmlsoap.org/soap/encoding/";    public static final String SAXPARSER =       "org.apache.xerces.parsers.SAXParser";    public static final String SAXNAMESPACES =       "http://xml.org/sax/features/namespaces"; } 
   


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