The Directory Manager

The Concordia DirectoryManager class and its corresponding COM.meitca.concordia.service.Directory interface provide a lightweight directory service through which service bridges can register themselves with a central naming service. Concordia agents can then travel to the directory service and retrieve the locations of services of which they are interested. This directory can give an agent application some degree of location transparency. Rather than hard-coding the locations of services, an application can use the directory service to support a more dynamic configuration where services can be relocated around a network or can be duplicated in multiple redundant instances.

The DirectoryManager is a distributed object which can be accessed either remotely using Java RMI (Remote Method Invocation) or directly using standard Java references and method invocations. In typical usage, service bridges will remotely register themselves with the directory service using its registerService method. This registration will occur either in the service bridge's constructor or in its startService method. The startService method is normally used by service bridges to complete initialization tasks that were not or could not be performed in the constructor.

With a few minor changes, we can modify the SalesDatabase example to use the directory service. The first modifications are made to the service bridge implementation class SalesDatabaseService. The modifications are show below:


 1: import java.rmi.*;

 2: import java.net.*; 

 3: import java.sql.*;

 4: import COM.meitca.concordia.service.*;

 5: 

 6: public class SalesDatabaseService extends ServiceBridge implements SalesDatabase {

 7:     public SalesDatabaseService() throws RemoteException {

 8: 	    // No change from prior implementation

 9: 	    .

10: 	    .

11:     }

12:     

13:     /**

14:      * This method performs startup and initialzation of the server 

15:      */

16:     protected void startService() throws ServiceException {

17: 	    // Register ourselves with the directory

18:         try {

19:             // First we	using the java.rmi.Naming classes lookup

20:             // method to retrieve a remote reference to the directory.

21: 	        // We assume that the directory is running on a machine

22: 	        // with the hostname "directory"

23: 	        Directory directory = (Directory)Naming.lookup("//directory/" +  

24: 	    	                        DirectoryImpl.DIRECTORY_MANAGER_NAME);

25: 

26:             // then we make a remote method call to registerService to register 

27:             // ourself with the directory.  We pass two parameters to 

28:             // registerService.  The first is the name of our service, the

29:             // second is our TCP/IP hostname.

30: 	        directory.registerService(SalesDatabase.SERVICE_NAME,

31:                                 InetAddress.getLocalHost().getHostName());

32:         } catch (Exception e) {

33:             System.err.println("Could not register with directory");

34:         }

35:     }

36: 

37:     /**

38:      * This method performs clean shutdown tasks.

39:      */

40:	protected void stopService(java.util.Date timeout)

41:	    throws ServiceException,

42:	    ServiceTimeoutException,

43:	    ServiceWrongStateException {

44:

45:         // unregister with the server.

46:         try {

47:    	        // Retrieve a remote reference to the directory

48: 	        Directory directory = (Directory)Naming.lookup("//directory/" +  

49: 	    	                        DirectoryImpl.DIRECTORY_MANAGER_NAME);

50: 

51:             // then we make a remote method call to removeService to remove

52:             // ourself from the directory.  We pass two parameters to 

53:             // registerService.  The first is the name of our service, the

54:             // second is our TCP/IP hostname.

55: 	        directory.removeService(SalesDatabase.SERVICE_NAME,

56:                                 InetAddress.getLocalHost().getHostName());

57:         } catch (Exception e) {

58:             System.err.println("Could not remove from directory");

59:         }

60:     }

61:         

62: 

63:     /** 

64:      * This method returns the total sales for the region.

65:      * Results are returned in a QueryResults object.

66:      */

67:     public QueryResult querySales(String region) {

68: 	    // No change from prior implementation

69: 	    .

70: 	    .

71:     }

72: }




Figure 9 - SalesDatabase Service Bridge using Directory Service

In this version, we override the startService and stopService methods of the ServiceBridge class to register and deregister the service with the directory. In both cases, we use the Naming.lookup method provided by Java RMI to retrieve a remote reference to the directory. To register the service, we remotely call registerService passing in the name of our service bridge and our hostname. Deregistration is achieved by remotely calling Directory.removeService method and passing in the same parameters.

The second modification we make is to the QueryAgent class. A new method is added to QueryAgent which performs the task of retrieving the location of the SalesDatabase service from the directory. The new version of QueryAgent is shown below:


 1: import java.util.*;

 2: 

 3: import COM.meitca.concordia.*;

 4: import COM.meitca.concordia.service.*;

 5: 

 6: /**

 7:  * An Agent which travels to a SalesDatabase, queries the 

 8:  * database for the total sales, and then reports the sales

 9:  * results.

10:  */

11: public class QueryAgent extends Agent {

12:     // No change to member variables

13:     .

14:     .

15:     

16:     /** 

17:      * Constructs a QueryAgent 

18:      */

19:     public QueryAgent(String region) {

20: 	    // No change from prior implementation

21: 	    .

22: 	    .

23:     }

24:     

25:     /**

26:      * Locates the SalesDatabase in the Directory

27:      */

28:      public void locateDatabase() {

29:         // find a SalesDatabase

30:         try {

31:             // First retrieve a (local) reference to the Directory Service

32:      	Directory   directory = (Directory)ServiceBridge.getServiceBridge(DirectoryImpl.DIRECTORY_MANAGER_NAME);

33:             

34:             // Call the Directory's lookupService method which should return 

35:             // Vector of hosts offering that service

36:             Vector      hosts = directory.lookupService(SalesDatabase.SERVICE_NAME);

37:             

38:             // arbitrarily pick the first host

39:             String      first_host = (String)hosts.elementAt(0);

40:             

41:             // add a stop to our Itinerary to visit that host.

42:             getItinerary().insertDestinationBeforeLast(new Destination(first_host, "queryDatabase"));

43:         } catch (Exception e) {

44:             System.err.println("Error accessing Directory");

45:         }

46:         

47:         // move on

48:      }

49:      

50:     /**

51:      * Queries the sales database

52:      */

53:      public void queryDatabase() {

54: 	    // No change from prior implementation

55: 	    .

56: 	    .

57:      }

58:     

59:     /**

60:      * Report query results to console using System.out.println

61:      * statements.  This method could also report the results

62:      * using an AWT GUI, or had the results off to another 

63:      * Java class for manipulation or reporting.

64:      */

65:     public void reportResults() {

66: 	    // No change from prior implementation

67: 	    .

68: 	    .

69:     }    

70: }

Figure 10 - Modified QueryAgent

In the new version of QueryAgent, we have added a new method called locateDatabase whose task it is to interact with the directory service to locate a SalesDatabase service somewhere on the network. locateDatabase does this by first retrieving a (local) reference to the directory service using ServiceBridge.getServiceBridge (on line 32). A call is then made to the Directory.lookupService method to retrieve the hostname(s) of machine(s) on the network offering the service. lookupService returns the list of hostnames in a Vector. On line 39, we pick the first hostname from the list and construct a new Destination for the agent which is inserted just prior to the last destination using the Itinerary.insertDestinationBeforeLast method (on line 42).

The final modification we make is to the agent launch code to take into account the fact that the agent must travel to the Directory Manager.


 1: import COM.meitca.concordia.*;

 2: 

 3: public class TestLaunch { 

 4: 

 5: 	public static void main(String args[]) { 

 6: 	    try { 

 7: 		// First we construct the agent 

 8: 		QueryAgent agent = new QueryAgent("North America"); 

 9:

10: 		// Second we set up the Agents itinerary. Notice that 

11: 		// we can specify the method to call by name.

12: 

13: 		Itinerary itinerary = new Itinerary();

14: 		itinerary.addDestination(new Destination("directory", "locateDatabase"));

15: 		// if we locate a SalesDatabase, a new Destination will be inserted here.

16: 		itinerary.addDestination(new Destination("workstation", "reportResults"));

17:  

18: 		// we attatch the Itinerary to the Agent using the setItinerary method

19:  		agent.setItinerary(itinerary);

20: 

21: 		// Third set up the URL pointing to the Agents codebase. 

22: 		agent.setHomeCodebaseURL("http://mywebserver/Agents");

23:  

24:  		// Last, we actually launch the agent by calling

25:  		// its launch method

26:  		agent.launch();

27:  

28:	    } catch (Exception e) { 

29:  		System.err.println(e.getMessage()); 	

30:  		e.printStackTrace(); 

31:  	    }

32:  

33:  	}

34: } 


Figure 11 - Agent Launching Code

The only change to the launch code is on line 14 where we set the initial destination of the agent to be the server hosting the directory. Notice that we add the final stop in the Itinerary on line 16. If this agent is able to locate a host offering the SalesDatabase service then a destination will be inserted just prior to this final stop in which the database will be queried. In this case, the Itinerary in its final form would look like the following:

Hostname Method
directory locateDatabase
dbserver queryDatabase
workstation reportResults

In this case, the agent will travel first to the directory then to the database and finally to a user workstation to report the results. If for some reason a SalesDatabase service cannot be found, then the Itinerary will look like:

Hostname Method
directory locateDatabase
workstation reportResults

In this case, the agent will travel to the directory, find no registered database and finally return to the user workstation where it can report that it obtained no results. By structuring the Itinerary in this way, we specify that the agent should return to workstation regardless of whether any valid SalesDatabase was found and give the reportResults method the opportunity to report the failure.

 

Directory Caveats

The Concordia Directory Manager is designed to be a very lightweight naming service for service bridges. It is not intended to be a full-scale directory manager and does not include the type of reliability, scalability, and replication features found in full-scale directory servers. Future versions of Concordia may interoperate with existing commercial directory servers or with the Java Naming and Directory Interface (JNDI) to provide for such a solution.