VisiBroker for Java Developer’s Guide : Using the Location Service

Using the Location Service
The VisiBroker Location Service provides enhanced object discovery that enables you to find object instances based on particular attributes. Working with VisiBroker Smart Agents, the Location Service notifies you of what objects are presently accessible on the network, and where they reside. The Location Service is a VisiBroker extension to the CORBA specification and is only useful for finding objects implemented with VisiBroker. For more information on the Smart Agent (osagent), see “Using the Smart Agent”.
What is the Location Service?
The Location Service is an extension to the CORBA specification that provides general-purpose facilities for locating object instances. The Location Service communicates directly with one Smart Agent which maintains a catalog, which contains the list of the instances it knows about. When queried by the Location Service, a Smart Agent forwards the query to the other Smart Agents, and aggregates their replies in the result it returns to the Location Service.
The Location Service knows about all object instances that are registered on a POA with the BY_INSTANCE Policy and objects that are registered as persistent on a BOA. The server containing these objects may be started manually or automatically by the OAD. For more information, see “Using POAs”, “Using the BOA with VisiBroker,” and “Using the Object Activation Daemon (OAD)”. (A server specifies an instance's scope when it creates the instance. Only globally-scoped instances are registered with Smart Agents.)
The following diagram illustrates this concept.
Figure 21
The Location Service can make use of the information the Smart Agent keeps about each object instance. For each object instance, the Location Service maintains information encapsulated in the structure ObjLocation::Desc shown below.
struct Desc {
Object ref;
::IIOP::ProfileBodyValue iiop_locator;
string repository_id;
string instance_name;
boolean activable;
string agent_hostname;
};
typedef sequence<Desc> DescSeq;
The IDL for the Desc structure contains the following information:
The object reference, ref, is a handle for invoking the object.
The iiop_locator interface provides access to the host name and the port of the instance's server. This information is only meaningful if the object is connected with IIOP, which is the only supported protocol. Host names are returned as strings in the instance description.
The repository_ id, which is the interface designation for the object instance that can be looked up in the Interface and Implementation Repositories. If an instance satisfies multiple interfaces, the catalog contains an entry for each interface, as if there were an instance for each interface.
The instance_name, which is the name given to the object by its server.
The activable flag, which differentiates between instances that can be activated by an OAD and instances that are started manually.
The agent_hostname, the name of the Smart Agent with which the instance is registered.
The Location Service is useful for purposes such as load balancing and monitoring. Suppose that replicas of an object are located on several hosts. You could deploy a bind interceptor that maintains a cache of the host names that offer a replica and each host's recent load average. The interceptor updates its cache by asking the Location Service for the hosts currently offering instances of the object, and then queries the hosts to obtain their load averages. The interceptor then returns an object reference for the replica on the host with the lightest load. For more information about writing interceptors, see “Using Portable Interceptors” and “Using VisiBroker Interceptors”.
Location Service components
The Location Service is accessible through the Agent interface. Methods for the Agent interface can be divided into two groups: those that query a Smart Agent for data describing instances and those that register and unregister triggers. Triggers provide a mechanism by which clients of the Location Service can be notified of changes to the availability of instances.
What is the Location Service agent?
The Location Service agent is a collection of methods that enable you to discover objects on a network of Smart Agents. You can query based on the interface’s repository ID, or based on a combination of the interface's repository ID and the instance name. Results of a query can be returned as either object references or more complete instance descriptions. An object reference is simply a handle to a specific instance of the object located by a Smart Agent. Instance descriptions contain the object reference, as well as the instance's interface name, instance name, host name and port number, and information about its state (for example, whether it is running or can be activated).
Note
The locserv executable no longer exists since the service is now part of the core VisiBroker ORB.
 
The figure below illustrates the use of interface repository IDs and instance names given the following example IDL:
module Automobile {
interface Car{...};
interface Sedan:Car {...};
}
Figure 22
Given the previous example, the following diagram visually depicts Smart Agents on a network with references to instances of Car. In this example, there are three instances: one instance of Keri's Car and two replicas of Tom's Car.
Figure 23
The following sections explain how the methods provided by the Agent class can be used to query VisiBroker Smart Agents for information. Each of the query methods can raise the Fail exception, which provides a reason for the failure.
Obtaining addresses of all hosts running Smart Agents
Using the String[] in the all_agent_locations() method, you can find out which servers are hosting VisiBroker Smart Agents. In the example shown in the figure below, this method would return the addresses (such as, IP address string) of two servers: Athena and Zeus.
Finding all accessible interfaces
You can query the VisiBroker Smart Agents on a network to find out about all accessible interfaces. To do so, you can use the String[] in the all_repository_ids() method. In the example shown in the following figure, this method would return the repository IDs of two interfaces: Car and Sedan.
Note
Earlier versions of the VisiBroker ORB used IDL interface names to identify interfaces, but the Location Service uses the repository id instead. To illustrate the difference, if an interface name is:
::module1::module2::interface
the equivalent repository id is:
IDL:module1/module2/interface:1.0
For the example shown in the figure above, the repository ID for Car would be:
IDL:Automobile/Car:1.0
and the repository ID for Sedan would be:
IDL:Automobile/Sedan:1.0
Obtaining references to instances of an interface
You can query VisiBroker Smart Agents on a network to find all available instances of a particular interface. When performing the query, you can use either of these methods:
In the example shown in the figure above, a call to either method with the request IDL:Automobile/Car:1.0 would return three instances of the Car interface: Tom's Car on Athena, Tom's Car on Zeus, and Keri's Car. The Tom's Car instance is returned twice because there are occurrences of it with two different Smart Agents.
Obtaining references to like-named instances of an interface
Using one of the following methods, you can query VisiBroker Smart Agents on a network to return all occurrences of a particular instance name.
In the example shown in the previous figure, a call to either method specifying the repository ID IDL:Automobile/Sedan:1.0 and instance name Tom's Car would return two instances because there are occurrences of it with two different Smart Agents.
What is a trigger?
A trigger is essentially a callback mechanism that lets you determine changes to the availability of a specified instance. It is an asynchronous alternative to polling an Agent, and is typically used to recover after the connection to an object has been lost. Whereas queries can be employed in many ways, triggers are special-purpose.
Looking at trigger methods
The trigger methods in the Agent class are described in the following tables:
Both of the Agent trigger methods can raise the Fail exception, which provides a reason for the failure.
The TriggerHandler interface consists of the methods described in the following tables:
Creating triggers
A TriggerHandler is a callback object. You implement a TriggerHandler by deriving from theTriggerHandlerPOA class (or the TriggerHandlerImpl class with BOA), and implementing its impl_is_ready() and impl_is_down() methods. To register a trigger with the Location Service, you use the reg_trigger() method in the Agent interface. This method requires that you provide a description of the instance you want to monitor, and the TriggerHandler object you want invoked when the availability of the instance changes. The instance description (TriggerDesc) can contain combinations of the following instance information: repository ID, instance name, and host name. The more instance information you provide, the more particular your specification of the instance.
struct TriggerDesc {
string repository_id;
string instance_name;
string host_name;
};
Note
If a field in the TriggerDesc is set to the empty string (““), it is ignored. The default for each field value is the empty string.
For example, a TriggerDesc containing only a repository ID matches any instance of the interface. Looking back to our example in the figure above, a trigger for any instance of IDL:Automobile/Car:1.0 would occur when one of the following instances becomes available or unavailable: Tom's Car on Athena, Tom's Car on Zeus, or Keri's Car. Adding an instance name of “Tom's Car” to the TriggerDesc tightens the specification so that the trigger only occurs when the availability of one of the two “Tom's Car” instances changes. Finally, adding a host name of Athena refines the trigger further so that it only occurs when the instance Tom's Car on the Athena server becomes available or unavailable.
Looking at only the first instance found by a trigger
Triggers are “sticky.” A TriggerHandler is invoked every time an object satisfying the trigger description becomes accessible. You may only be interested in learning when the first instance becomes accessible. If this is the case, invoke the Agent's unreg_trigger() method to unregister the trigger after the first occurrence is found.
Querying an agent
This section contains two examples of using the Location Service to find instances of an interface. The first example uses the Account interface shown in the following IDL excerpt:
// Bank.idl
module Bank {
interface Account {
float balance();
};
interface AccountManager {
Account open (in string name);
};
};
Finding all instances of an interface
The following code sample uses the all_instances() method to locate all instances of the Account interface. Notice that the Smart Agents are queried by passing “LocationService” to the ORB.resolve_initial_references() method, then narrowing the object returned by that method to an ObjLocation.Agent . Notice, as well, the format of the Account repository id: IDL:Bank/Account:1.0. To find all instances satisfying the AccountManager interface:
// AccountFinder.java
public class AccountFinder {
public static void main(String[] args) {
try {
// Initialize the ORB.
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args,null);
com.inprise.vbroker.ObjLocation.Agent the_agent = null;
try {
the_agent = com.inprise.vbroker.ObjLocation.AgentHelper.narrow(
orb.resolve_initial_references("LocationService"));
}
catch (org.omg.CORBA.ORBPackage.InvalidName e) {
System.out.println("Not able to resolve references " +
"for LocationService");
System.exit(1);
}
catch (Exception e) {
System.out.println("Unable to locate LocationService!");
System.out.println("Caught exception: " + e);
System.exit(1);
}
org.omg.CORBA.Object[] accountRefs =
the_agent.all_instances("IDL:Bank/AccountManager:1.0");
System.out.println("Agent returned " + accountRefs.length + " object references");
for (int i=0; i < accountRefs.length; i++) {
System.out.println("Stringified IOR for account #" + (i+1) + ":");
System.out.println(orb.object_to_string(accountRefs[i]));
System.out.println();
}
} catch (Exception e) {
System.out.println("Caught exception: " + e);
System.exit(1);
}
}
}
Finding interfaces and instances known to Smart Agents
The following code sample shows how to find everything known to Smart Agents. It does this by invoking the all_repository_ids() method to obtain all known interfaces. Then it invokes the all_instances_descs() method for each interface to obtain the instance descriptions.
Finding everything known to a Smart Agent:
// Find.java
public class Find {
public static void main(String[] args) {
try {
// Initialize the ORB.
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args,null);
com.inprise.vbroker.ObjLocation.Agent agent = null;
try {
agent = com.inprise.vbroker.ObjLocation.AgentHelper.narrow(
orb.resolve_initial_references("LocationService"));
} catch (org.omg.CORBA.ORBPackage.InvalidName e) {
System.out.println("Not able to resolve references " + "for
LocationService");
System.exit(1);
} catch (Exception e) {
System.out.println("Not able to resolve references " + "for
LocationService");
System.out.println("Caught exception: " + e);
System.exit(1);
}
boolean done=false;
java.io.BufferedReader in =
          new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
      while (! done) {
System.out.print("-> ");
System.out.flush();
String line = in.readLine();
if(line.startsWith("agents")) {
java.lang.String[] agentList = agent.all_agent_locations();
System.out.println("Located " + agentList.length + " agents");
for (int i=0; i < agentList.length; i++) {
System.out.println("\t" + "Agent #" + (i+1) + ": " +
agentList[i]);
}
} else if(line.startsWith("rep")) {
java.lang.String[] repIds = agent.all_repository_ids();
System.out.println("Located " + repIds.length + " repository Ids");
for (int i=0; i < repIds.length; i++) {
System.out.println("\t" + "Repository Id #" + (i+1) + ": " +
repIds[i]);
}
} else if(line.startsWith("objects ")) {
String names = line.substring("objects ".length(), line.length());
PrintObjects(names,agent,orb);
} else if(line.startsWith("quit")) {
done = true;
} else {
System.out.println("Commands: agents\n" +
" repository_ids\n" +
" objects <rep Id>\n" +
" objects <rep Id> <obj name>\n" +
" quit\n");
}
}
} catch (com.inprise.vbroker.ObjLocation.Fail err) {
System.out.println("Location call failed with reason " + err.reason);
} catch (java.lang.Exception err) {
System.out.println("Caught error " + err);
err.printStackTrace();
}
}
public static void PrintObjects(String names,
com.inprise.vbroker.ObjLocation.Agent agent,
org.omg.CORBA.ORB orb)
throws com.inprise.vbroker.ObjLocation.Fail {
int space_pos = names.indexOf(' ');
String repository_id;
String object_name;
if (space_pos == -1) {
repository_id = names;
object_name = null;
} else {
repository_id = names.substring(0,names.indexOf(' '));
object_name = names.substring(names.indexOf(' ')+1);
}
org.omg.CORBA.Object[] objects;
com.inprise.vbroker.ObjLocation.Desc[] descriptors;
if (object_name == null) {
objects = agent.all_instances(repository_id);
descriptors = agent.all_instances_descs(repository_id);
} else {
objects = agent.all_replica(repository_id,object_name);
descriptors = agent.all_replica_descs(repository_id,object_name);
}
System.out.println("Returned " + objects.length + " objects");
for (int i=0; i<objects.length; i++) {
System.out.println("\n\nObject #" + (i+1) + ":");
System.out.println("==================");
System.out.println("\tRep ID: " +
((com.inprise.vbroker.CORBA.Object)objects[i])._repository_id());
System.out.println("\tInstance:" +
((com.inprise.vbroker.CORBA.Object)objects[i])._object_name());
System.out.println("\tIOR: " + orb.object_to_string(objects[i]));
System.out.println();
System.out.println("Descriptor #" + (i+1));
System.out.println("=====================================");
System.out.println("Host: " + descriptors[i].iiop_locator.host);
System.out.println("Port: " + descriptors[i].iiop_locator.port);
System.out.println("Agent Host: " + descriptors[i].agent_hostname);
System.out.println("Repository Id: " + descriptors[i].repository_id);
System.out.println("Instance: " + descriptors[i].instance_name);
System.out.println("Activable: " + descriptors[i].activable);
}
}
}
Writing and registering a trigger handler
The following code sample implements and registers a TriggerHandler. The TriggerHandlerImpl's impl_is_ready() and impl_is_down() methods display the description of the instance that caused the trigger to be invoked, and optionally unregister itself.
If it is unregistered, the method calls System.exit() to terminate the program.
Notice that the TriggerHandlerImpl class keeps a copy of the desc and Agent parameters with which it was created. The unreg_trigger() method requires the desc parameter. The Agent parameter is duplicated in case the reference from the main program is released.
Implementing a trigger handler:
// AccountTrigger.java
import java.io.*;
import org.omg.PortableServer.*;
class TriggerHandlerImpl extends
com.inprise.vbroker.ObjLocation.TriggerHandlerPOA {
public TriggerHandlerImpl(com.inprise.vbroker.ObjLocation.Agent agent,
com.inprise.vbroker.ObjLocation.TriggerDesc initial_desc) {
agent = agent;
initial_desc = initial_desc;
}
public void impl_is_ready(com.inprise.vbroker.ObjLocation.Desc desc) {
notification(desc, true);
}
public void impl_is_down(com.inprise.vbroker.ObjLocation.Desc desc) {
notification(desc, false);
}
   private void notification(com.inprise.vbroker.ObjLocation.Desc desc,
boolean isReady) {
if (isReady) {
System.out.println("Implementation is ready:");
} else {
System.out.println("Implementation is down:");
}
System.out.println("\tRepository Id = " + desc.repository_id + "\n" +
"\tInstance Name = " + desc.instance_name + "\n" +
"\tHost Name = " + desc.iiop_locator.host + "\n" +
"\tBOA Port = " + desc.iiop_locator.port + "\n" +
"\tActivable = " + desc.activable + "\n" + "\n");
System.out.println("Unregister this handler and exit (yes/no)?");
try {
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
String line = in.readLine();
if(line.startsWith("y") || line.startsWith("Y")) {
try {
agent.unreg_trigger(_initial_desc, _this());
} catch (com.inprise.vbroker.ObjLocation.Fail e) {
System.out.println("Failed to unregister trigger with
reason=[" + e.reason + "]");
}
System.out.println("exiting...");
System.exit(0);
}
} catch (java.io.IOException e) {
System.out.println("Unexpected exception caught: " + e);
System.exit(1);
}
}
private com.inprise.vbroker.ObjLocation.Agent _agent;
private com.inprise.vbroker.ObjLocation.TriggerDesc _initial_desc;
}
public class AccountTrigger {
public static void main(String args[]) {
try {
// Initialize the ORB.
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args,null);
POA rootPoa =
POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
rootPoa.the_POAManager().activate();
com.inprise.vbroker.ObjLocation.Agent the_agent =
com.inprise.vbroker.ObjLocation.AgentHelper.narrow(
orb.resolve_initial_references("LocationService"));
// Create a trigger description and an appropriate TriggerHandler.
// The TriggerHandler will be invoked when the osagent becomes
// aware of any new implementations of the interface
"Bank::AccountManager"
com.inprise.vbroker.ObjLocation.TriggerDesc desc =
new com.inprise.vbroker.ObjLocation.TriggerDesc
"IDL:Bank/AccountManager:1.0", "", "");
TriggerHandlerImpl trig = new TriggerHandlerImpl(the_agent, desc);
rootPoa.activate_object(trig);
the_agent.reg_trigger(desc, trig._this());
orb.run();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}