September 1, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Web Services-Not Always the Best Solution

  • May 28, 2009
  • By Liviu Tudor
  • Send Email »
  • More Articles »

Using an XML-Based Protocol

As mentioned earlier, another approach is to replace a proprietary TCP/IP-based protocol with an XML-based one. In this approach, server components listen for incoming connections. When a client connects, communication between the client and the server still takes place over sockets; but clients send requests as XML, the server returns XML, and the client parses the reply. To test this approach, here's the "protocol" the client and the server will follow:

Clients send requests using this format:

<request>
   <type>findFriend</type>
   <param>name of the person</param>
</request>

The server will reply with the following format:

<response>
   <results>
      <result>
         <name>person name</name>
         <street>street address</street>
         <city>city address</city>
         <postcode>post code</postcode>
      </result>
   </results>
</response>

If the query doesn't find a match, the <results> element will be empty.

In most cases, an XML Schema or DTD defines the communications protocol, and serves to validate data exchanged on both ends. While validation will find any corrupt messages being passed between the client and the server up-front, it also adds to the execution time. In this case, because you have complete control over both the protocol and the communications API, the code example doesn't validate the messages against a schema or a DTD, it simply assumes that they will always be correct.

Just as before, the system will have two components: a server that listens for incoming connections on TCP port 8899 (you can change this constant in the source code to any other value as long as it doesn't conflict with another service running on that port), and a client that simply connects, sends the request, and then waits for the response, which it parses and returns.

When a client establishes a connection, the server spawns a thread to deal with that connection. The server aggregates the AddressBook service and uses it to search the address book when prompted to do so by the client—in other words, it's just a wrapper for the AddressBook service. The server and client use the XML message formats shown earlier.

Author's Note: The sample code builds the XML manually. It's unlikely you would do that in the real world; in most cases you'd probably want to build an in-memory DOM and then serialize it. However, the concern in this article is performance, so I've taken the liberty of eliminating this extra layer in our code.

protected void sendRequest( String name, OutputStream o ) 
   throws IOException
   {
      PrintWriter w = new PrintWriter( o );
      w.println("<?xml version="1.0" " + 
         "encoding="ISO-8859-1"?><request>" + 
         "<type>findFriend</type><param>" + name + 
         "</param></request>" );
      w.println();
      w.flush();
   }

The server assembles the response in a similar fashion:

protected void sendResponse( AddressBean a, OutputStream s ) 
   throws IOException
   {
      PrintWriter w = new PrintWriter( s );
      w.println( "<?xml version="1.0" " + 
         "encoding="ISO-8859-1"?><response>" );
      if( a == null )
         w.println( "<results/>" );
      else
      {
         w.println( "<results>" );
         w.println( "<result>" );
         w.println( "<name>" + a.getName() + "</name>" );
         w.println( "<street>" + a.getStreet() + "</street>" );
         w.println( "<city>" + a.getCity() + "</city>" );
         w.println( "<postcode>" + a.getPostCode() + "</postcode>" );
         w.println( "</result>" );
         w.println( "</results>" );
      }
      w.println( "</response>" );
      w.println();
      w.flush();
   }

Both client and the server apply the same pattern to process incoming messages: They read all the lines from the incoming stream until they encounter an empty line. They pass the read block through a SAX parser. (The reason for doing that instead of passing the socket InputStream directly to the SAX parser is that the SAX parser reads from the stream until it reaches EOF—which in sockets language means closing the socket—and that ends communication between the client and server, preventing the server from replying to the request.) Instead, you just need to make sure that each request and response is followed by a blank line, which functions as the "end of message" marker. The Utility class implements this little hack:

public static Reader readXML( InputStream s ) throws IOException
{
   BufferedReader br = new BufferedReader( new InputStreamReader(s) );
   StringBuilder sb = new StringBuilder();
   String temp = null;
   boolean stop = false;
   while( !stop )
   {
      temp = br.readLine();
      if( temp != null )
         sb.append( temp );
      if( temp.equals("") )   //read until the first empty line
         stop = true;
   }
   StringReader sr = new StringReader( sb.toString() );
   return sr;
}

You need two SAX parsers: one for the request format (RequestParser) and one for the reply (ResponseParser). Both parsers simply look for certain elements and store the text inside those tags in a buffer used to accumulate the results. While this might not be the most elegant solution, it certainly is one of the fastest as you can find out now by changing AddressBook client to instantiate XMLSerAddressBookClient and use that. Here's the output:

class xmlser.XMLSerAddressBookClient found 
   Liv : Oxford Street , London , SW1 in 584,239,804

The output shows that taking this approach reduced our client execution time to about 0.58 seconds (about half the time required to execute the SOAP client). That shouldn't be surprising, because the XML-based messaging approach simplified the structure of the exchanged messages (the SOAP message are far more complex), which reduced both the message size and also simplified the parsing process. In addition, this approach avoids validating the XML messages, which again reduces the execution time. Finally, the server assembles the reply message assembly by simple text concatenation, rather than operating against a DOM which then gets serialized.

So, eliminating SOAP from the communication loop while still using XML cuts the system response time roughly in half. SOAP partisans will argue that SOAP is a W3C standard, so it's supported "out-of-the-box" by the majority of frameworks, libraries and languages. They'll also probably point out that SOAP offers firewall penetration by using the standard HTTP port (rather than port 8899). However, from an implementation viewpoint, the difference between using a library that supports SOAP out of the box and using a custom XML format is pretty much limited to writing a SAX parser (which is not difficult). As for firewall penetration, there's nothing that prevents you from runnign this service on port 80. Even better, rather than having a TCP/IP server deal with sockets, you could wrap the AddressBook service in a servlet that takes its parameters from the query string. Using that scheme, the execution times should be similar to this example. Because this uses an XML-based protocol, there are parsers available for all the commonly used programming languages—connectivity isn't limited to Java-based implementations for either the client or the server.





Page 3 of 6



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel