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

From the moment XML was thrown at the IT world, there's been a flurry of activity to port nearly every single communications interface to an XML-based one. In most cases, vendors have provided a SOAP-based interface, but sometimes they also replace the original proprietary protocol with an XML-based one. Exposing a SOAP interface made sense in a lot of cases—especially for middleware components that needed to be "consumed" by a variety of clients implemented in various programming languages. However, in other cases, web services were used to interconnect Java-based components, replacing the older, more "traditional" methods (such as RMI). I've even seen experienced a lot of cases where a proprietary socket-based protocol was replaced by an XML-based one, even though the only way clients would use this protocol was through an in-house-written API supplied to clients.

There's little doubt that some of the interface-replacement work was due to the "hype" factor around XML, and also due to a misunderstanding of what happens under the hood when dealing with XML.

The remainder of this article is a simple comparison among a few differing implementations of a simple service (an address book), along with a discussion of the implications of choosing each solution.

AddressBook Service Entities

First, you define the data entities that the service will store and retrieve from the address book. A simple Java Bean class sufficed, ingeniously named AddressBean:

public class AddressBean implements Serializable
{
   public AddressBean( String name, String street, String city, 
      String postCode )
   {
      this.name = name;
      this.street = street;
      this.city = city;
      this.postCode = postCode;
   }
   
   public AddressBean()
   {
      this( null, null, null, null );
   }
   
   private String       name;
   public String getName() 
   {
      return name;
   }
   public void setName(String name) 
   {
      this.name = name;
   }
   
   private String      street;
   public String getStreet() 
   {
      return street;
   }
   public void setStreet(String street) 
   {
      this.street = street;
   }
   
   private String      city;
   public String getCity() 
   {
      return city;
   }
   public void setCity(String city) 
   {
      this.city = city;
   }
   private String      postCode;
   public String getPostCode() 
   {
      return postCode;
   }
   public void setPostCode(String postCode) 
   {
      this.postCode = postCode;
   }
   public String toString()
   {
      return "" + name + " : " + street + " , " + city + 
         " , " + postCode;
   }
}

The preceding AddressBean implementation has just four properties: name, street address, city and post code You'll find the implementation of this class in the bl (for "business logic") package in the downloadable code. The implementation overrides the toString function to provide a way to "inspect" the object by simply printing it out to the console when needed. Also note that the class is Serializable, so you can use it directly when dealing with communications schemes such as RMI or serialized objects over sockets.

The address book service will manipulate a list of AddressBean references. To keep things simple, the address book will have only one simple function: to search for an address by name. Granted, this approach (and the AddressBean structure) limits the functionality of an address book drastically, however, bear in mind that the point is to investigate ways to connect two systems so they can pass data back and forth, so there's no need to complicate the data structure or the interface.

In addition, a real-world address book would use a persistent data store to hold entries, but again, because data manipulation isn't pertinent to the article, the example uses in-memory storage the address book entries—and not even a structured one—just an array of AddressBean instances. Therefore, the implementation of finding an address based on a name will simply traverse the array and return the first item that has a matching name.

The AddressBook class provides this simple service implementation:

public class AddressBook
{
   private AddressBean[]   friends = { 
      new AddressBean("Liv", "Oxford Street", "London", "SW1"),
      new AddressBean("The Queen", "Buckingham Palace", "London", "WC1")
   };
   
   public AddressBean findFriend( String name )
   {
      if( null == name ) return null;
      for( int i = 0; i < friends.length; i++ )
         if( name.equals(friends[i].getName()) )
            return friends[i];
      return null;
   }
}

This class occasionally gets wrapped by other classes to offer the address book's functionality over different protocols. For each protocol, the example will measure the elapsed time on the client from the moment it places the call until it receives (and unpackages) the result. To do that, the clients need some rudimentary time measurement capabilities. Those of you who read my previous article on recursion versus iteration, are familiar with the SimpleMeasurement interface. Here, however, because the examples need only timing data, the interface exposes only one method, getNanos():

public interface SimpleMeasurement 
{
   public long getNanos();
}

Just as before, here's a SimpleMeasurementImpl class that all the clients will extend to provide simple timing measurements:

public class SimpleMeasurementImpl implements SimpleMeasurement 
{
   protected long timeStart = 0;
   protected long timeEnd    = 0;
   
   public long getNanos() 
   {
      return (timeEnd - timeStart);
   }
}

Each client will implement this class and set the timeStart and timeEnd data member at the beginning and end of each call they make to the service to System.nanoTime(); the rest is automatic. The main client is a simple console-based app that instantiates one of the "specialized client" implementations, invokes it, searches for a particular name, and then displays the results and the time required to retrieve the result ("specialized client" here refers to a class that can invoke the address book service using SOAP, RMI, etc.) Therefore, you need a common behavior for all the specialized clients—an interface that defines the address book service functionality:

public interface AddressBookContract extends SimpleMeasurement
{
   public AddressBean findFriend( String name );
}

Note that the interface also forces the implementation of the SimpleMeasurement interface, to ensure that all the specialized clients provide time measurements.

With that interface in place, you need a way to switch between the various specialized client implementations. A simple factory function in the client class suffices, so the console-based client looks like this:

public class AddressBookClient 
{
   private static final String      NAME   = "Liv";
   
   
   public static AddressBookContract getClient()
   {
      // factory function that will return various specialized client 
      // implementations
      // ...
   }
   
   public static void main( String args[] )
   {
      AddressBookContract client = getClient();      
      AddressBean result = client.findFriend( NAME );
      System.out.println( client.getClass() + " found "  + 
         result + " in " + client.getNanos() );
   }
}

The console client will create a client using the factory function, find a specific contact, and then print its complete class name, the results and the elapsed time.

With the groundwork in place, you can begin creating the specialized implementations for both the service and the client using various approaches.





Page 1 of 6



Comment and Contribute

 


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

 

 


Sitemap | Contact Us

Rocket Fuel