VisiBroker for C++ 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)”.
The following diagram illustrates this concept.
Figure 22
Note
A server specifies an instance's scope when it creates the instance. Only globally-scoped instances are registered with Smart Agents.
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 23
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 24
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 HostnameSeq 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 Edition Smart Agents on a network to find out about all accessible interfaces. To do so, you can use the RepositoryIDSeq 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:
#include "corba.h"
#include "locate_c.hh"

// USE_STD_NS is a define setup by VisiBroker to use the std namespace
USE_STD_NS
int main(int argc, char** argv) {
try {
// ORB initialization
CORBA::ORB_var the_orb = CORBA::ORB_init(argc, argv);

// Obtain a reference to the Location Service
CORBA::Object_var obj = the_orb->
resolve_initial_references("LocationService");
if ( CORBA::is_nil(obj) ) {
cout << "Unable to locate initial LocationService" << endl;
return 0;
}
ObjLocation::Agent_var the_agent = ObjLocation::Agent::_narrow(obj);

// Query the Location Service for all implementations of
// the Account interface
ObjLocation::ObjSeq_var accountRefs =
the_agent->all_instances("IDL:Bank/AccountManager:1.0”);
cout << "Obtained " << accountRefs->length()
<< " Account objects" << endl;
for (CORBA::ULong i=0; i < accountRefs->length(); i++) {
cout << "Stringified IOR for account #" << i <<
":" << endl;
CORBA::String_var stringified_ior(the_orb
->object_to_string(accountRefs[i]));
cout << stringified_ior << endl;
cout << endl;
}
}
catch (const CORBA::Exception& e) {
cout << "Caught exception: " << e << endl;
return 0;
}
return 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:
#include "corba.h"
#include "locate_c.hh"

// USE_STD_NS is a define setup by VisiBroker to use the std namespace
// if it exists
USE_STD_NS

int DisplaybyRepID(CORBA::ORB_ptr the_orb,
ObjLocation::Agent_var the_agent,
char * myRepId) {

ObjLocation::ObjSeq_var accountRefs;
accountRefs = the_agent->all_instances(myRepId);
cout << "Obtained " << accountRefs->length()
<< " Account objects" << endl;
for (CORBA::ULong i=0; i < accountRefs->length(); i++) {
cout << "Stringified IOR for account #" << i << ":"
<< endl;
CORBA::String_var stringified_ior(
the_orb->object_to_string(accountRefs[i]));
cout << stringified_ior << endl;
cout << endl;
}
return(1);
}
void PrintUsage(char * name) {
cout << "\nUsage: \n" << endl;
cout << "\t" << name << " [Rep ID]" << endl;
cout << "\n\tWith no argument, finds and prints all objects" << endl;
cout << "\tOptional rep ID searches for specific rep ID\n" << endl;
}
int main(int argc, char** argv) {
char myRepId[255] = "";
if (argc == 2) {
if (!strcmp(argv[1], "-h") >> !strcmp(argv[1], "/?") >>
!strcmp(argv[1], "-?") ) {
PrintUsage(argv[0]);
exit(0);
} else {
strcpy(myRepId, argv[1]);
}
}
else if (argc > 2) {
PrintUsage(argv[0]);
exit(0);
}
try {
CORBA::ORB_ptr the_orb = CORBA::ORB_init(argc, argv);
CORBA::Object_ptr obj = the_orb->
resolve_initial_references("LocationService");
if ( CORBA::is_nil(obj) ) {
cout << "Unable to locate initial LocationService" << endl;
return 0;
}
ObjLocation::Agent_var the_agent = ObjLocation::Agent::_narrow(obj);
ObjLocation::DescSeq_var descriptors;
//Display stringified IOR for RepID requested and exit
if (argc == 2) {
DisplaybyRepID(the_orb, the_agent, myRepId);
exit(0);
}
//Report all hosts running osagents
ObjLocation::HostnameSeq_var HostsRunningAgents =
the_agent->all_agent_locations();
cout << "Located " << HostsRunningAgents->length()
<< " Hosts running Agents" << endl;
for (CORBA::ULong k=0; k<HostsRunningAgents->length(); k++) {
cout << "\tHost #" << (k+1) << ": "
<< (const char*) HostsRunningAgents[k] << endl;
}
cout << endl;
// Find and display all Repository Ids
ObjLocation::RepositoryIdSeq_var repIds = the_agent->all_repository_ids();
cout << "Located " << repIds->length() <<
" Repository Ids" << endl;
for (CORBA::ULong j=0; j<repIds->length(); j++) {
cout << "\tRepository ID #" << (j+1) << ": "
<< repIds[j] << endl;
}
// Find all Object Descriptors for each Repository Id
for (CORBA::ULong i=0; i < repIds->length(); i++) {
descriptors = the_agent->all_instances_descs(repIds[i]);
cout << endl;
cout << "Located " << descriptors->length()
<< " objects for " << (const char*) (repIds[i])
<< " (Repository Id #" << (i+1) << "):"
<< endl;
for (CORBA::ULong j=0; j < descriptors->length(); j++) {
cout << endl;
cout << (const char*) repIds[i] << " #" << (j+1)
<< ":" << endl;
cout << "\tInstance Name \t= " << descriptors[j].instance_name <<endl;
cout << "\tHost \t= " << descriptors[j].iiop_locator.host
<<endl;
cout << "\tPort \t= " << descriptors[j].iiop_locator.port
<<endl;
cout << "\tAgent Host \t= " << descriptors[j].agent_hostname <<endl;
cout << "\tActivable \t= " << (descriptors[j].activable?"YES":"NO") << endl;
}
}

} catch (const CORBA::Exception& e) {
cout << "CORBA Exception during execution of find_all: " << e << endl;
return 0;
}
return 1;
}
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 unregisters itself, the method calls the CORBA::ORB::shutdown() method which directs the BOA to exit the main program's impl_is_ready() method so the program can terminate.
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.c
#include "locate_s.hh"

// USE_STD_NS is a define set up by VisiBroker to use the std namespace
USE_STD_NS
// Instances of this class will be called back by the Agent when the
// event for which it is registered happens.

class TriggerHandlerImpl : public _sk_ObjLocation::_sk_TriggerHandler
{
public:
TriggerHandlerImpl(
ObjLocation::Agent_var agent,
const ObjLocation::TriggerDesc& initial_desc)
: _agent(ObjLocation::Agent::_duplicate(agent)),
_initial_desc(initial_desc) {}

void impl_is_ready(const ObjLocation::Desc& desc) {
notification(desc, 1);
}
void impl_is_down(const ObjLocation::Desc& desc) {
notification(desc, 0);
}

private:
  void notification(const ObjLocation::Desc& desc, CORBA::Boolean isReady) {
if (isReady) {
cout << "Implementation is ready:" << endl;
} else {
cout << "Implementation is down:" << endl;
}
cout << "\tRepository Id = " << desc.repository_id << endl;
cout << "\tInstance Name = " << desc.instance_name << endl;
cout << "\tHost Name = " << desc.iiop_locator.host << endl;
cout << "\tPort = " << desc.iiop_locator.port << endl;
cout << "\tAgent Host = " << desc.agent_hostname << endl;
cout << "\tActivable = " << (desc.activable? "YES" : "NO") << endl;
cout << endl;
cout << "Unregister this handler and exit (yes/no)? " << endl;
char prompt[256];
cin >> prompt;
if ((prompt[0] == 'y') >> (prompt[0] == 'Y')) {
try {
_agent->unreg_trigger(_initial_desc, this);
}
catch (const ObjLocation::Fail& e) {
cout << "Failed to unregister trigger with reason=["
<< (int) e.reason << "]" << endl;
}
cout << "exiting..." << endl;
CORBA::ORB::shutdown();
}
}

private:
ObjLocation::Agent_var _agent;
ObjLocation::TriggerDesc _initial_desc;
};

int main(int argc, char* const * argv)
{
try {
CORBA::ORB_var the_orb = CORBA::ORB_init(argc, argv);
CORBA::BOA_var boa = the_orb->BOA_init(argc, argv);
CORBA::Object_var obj = the_orb->
resolve_initial_references("LocationService");
if ( CORBA::is_nil(obj) ) {
cout << "Unable to locate initial LocationService" << endl;
return 0;
}
ObjLocation::Agent_var the_agent = ObjLocation::Agent::_narrow(obj);

// Create the trigger descriptor to notify us about
// OSAgent changes with respect to Account objects
ObjLocation::TriggerDesc desc;
desc.repository_id = (const char*) "IDL:Bank/AccountManager:1.0”;
desc.instance_name = (const char*) "";
desc.host_name = (const char*) "";

ObjLocation::TriggerHandler_var trig = new TriggerHandlerImpl(the_agent,
desc);
boa->obj_is_ready(trig);
the_agent->reg_trigger(desc,trig);
boa->impl_is_ready();
}
catch (const CORBA::Exception& e) {
cout << "account_trigger caught Exception: " << e << endl;
return 0;
}
return 1;
}