October 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 »

Exchanging Text Messages

Last, but not least, here's a humanly readable TCP/IP-based protocol where the server and client exchange plain text messages. The client writes text characters to the server and gets back some characters that follow this format:

name : street , city , postcode.

You may notice that the preceding format is the same as the output of the AddressBean.toString method. In this example, when the server finds the AddressBean, it simply writes the characters resulting from the toString call back to the client. The client parses the string and re-assembles an AddressBean based on the data.

Using this simple approach, the server will look like this:

public class TextSerAddressBookServer 
{
   public static final int SERVER_PORT = 8899;
   private AddressBook book;
   private ServerSocket srvSock;
   
   
   public TextSerAddressBookServer() throws IOException
   {
      book = new AddressBook();
      srvSock = new ServerSocket( SERVER_PORT );
   }
   
   public void startListening() 
   {
      Socket s = null;
      while( true )
      {
         try
         {
            s = srvSock.accept();
            Thread t = new Thread( new WorkerThread(s) );
            t.start();
         }
         catch( IOException e )
         {
            e.printStackTrace();
         }
      }
   }
   
   public static void main(String[] args) 
   {
      try
      {
         TextSerAddressBookServer server = new 
            TextSerAddressBookServer();
         server.startListening();
      }
      catch( IOException e )
      {
         e.printStackTrace();
      }
   }
   class WorkerThread implements Runnable
   {
      private Socket s;
      private String name;
      
      public WorkerThread( Socket s )
      {
         this.s = s;
      }
      
      public void run()
      {
         try
         {
            processRequest( s.getInputStream() );
            
            AddressBean a = book.findFriend( name );
            sendResponse( a, s.getOutputStream() );
         }
         catch( Exception e )
         {
            e.printStackTrace();
         }
         finally
         {
            try
            {
               s.close();
            }
            catch( IOException ioe )
            {
            }
         }
      }
      
      protected void processRequest( InputStream s ) throws IOException
      {
         BufferedReader br = new BufferedReader(
            new InputStreamReader(s) );
         name = br.readLine();
      }
      
      protected void sendResponse(AddressBean a, OutputStream s) 
         throws IOException
      {
         PrintWriter pw = new PrintWriter( s );
         pw.println( a.toString() );
         pw.flush();
      }
   }
}

The client will have to parse the response from the server, as discussed before. The easiest way to do that is by using a regular expression like this:

(w+)s*:s*([0-9a-zA-Zs]+)s*,s*(w+)s*,s*(.*)

To do that, use the java.util.regex package, and precompile the regex in a static member in the client class:

private static final Pattern p = Pattern.compile(
   "(\w+)\s*\:\s*([0-9a-zA-Z\s]+)\s*\,\s*(\w+)\s*\,\s*(.*)" 
);

With that in place, when the client receives a reply from the server, it just matches and retrieves the four matched groups, which it then assembles back into an AddressBean. The TextSerAddressBookClient's parseResponse method looks like this:

protected AddressBean parseResponse( InputStream s ) 
   throws IOException
   {
      BufferedReader br = new BufferedReader( new InputStreamReader(s) );
      String line = br.readLine();
      Matcher m = p.matcher( line );
      if( m.matches() )
         return new AddressBean( m.group(1), 
            m.group(2), m.group(3), m.group(4) );
      else 
         return null;
   }

Running this implementation renders the following:

class text.TextSerAddressBookClient found 
   Liv : Oxford Street  , London , SW1 in 39,210,032

That is approximately about half the time taken by the approach using the in-build serialization mechanism, and you can imagine that the time saved is due to the fact that messages sent in between client and server are shorter and the structure of the messages is not that complex, therefore the parsing of these messages is much lighter. The time it takes is still significantly higher than the RMI version, but that is because we spend a lot of time in opening the socket to the server -- if you move the statement that intializes timeStart after the line that opens the socket:

   @Override
   public AddressBean findFriend(String name) 
   {
      Socket s = null;
      try
      {
         s = new Socket( "localhost", TextSerAddressBookServer.SERVER_PORT );
         timeStart = System.nanoTime();
         sendRequest( name, s.getOutputStream() );
         AddressBean a = parseResponse( s.getInputStream() );
         return a;
      }
      catch( Exception e )
      {
         e.printStackTrace();
         return null;
      }
      finally
      {
         try
         {
            s.close();
         }
         catch(Exception e){}
         timeEnd = System.nanoTime();
      }
   }

you will find out that the timing decreases dramatically:

class text.TextSerAddressBookClient found Liv : Oxford Street  , London , SW1 in 4,373,359

In other words it takes about 0.004 seconds -- compared to 0.003 in the case of RMI -- and that is due to the fact that we offloaded a lot more preparation work into the RMI client constructor.

Comparing All Solutions

As a recap, Table 1 shows the timings for all the solutions discussed here.

Table 1. Solutions Compared: The table shows the timings for all the solutions discussed in this article.
Technology Duration (in ns) Duration (in seconds)
Web services (SOAP) 1,257,869,940 1.2
XML + TCP/IP 584,239,804 0.5
RMI 3,789,190 0.003
Serialized objects 73,613,300 0.07
Text over TCP/IP 4,373,359 0.004

So while web services, SOAP, and XML certainly have advantages for interconnecting components or applications regardless of platform or programming language used, they are not even close to the fastest method for communicating. As the timings show clearly, when you are purely interconnecting Java components, you might want to think twice before choosing SOAP as a communication mechanism.

About the Author

Liviu Tudor is a Java consultant living in the UK who has worked extensively with high-availability systems, mostly in the online media sector. Recently, he's come to realize that for performance, it's the "low level" core Java that makes an application deliver rather than a bloated middleware framework. While healing from injuries acquired playing rugby with his London team The Phantoms (http://www.phantomsrfc.com), he writes articles on Java technology for developer.com.





Page 6 of 6



Comment and Contribute

 


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

 

 


Sitemap | Contact Us

Rocket Fuel