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.
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.