Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Advanced CORBA Programming wit C++ - M. Henning, S. Vinoski.pdf
Скачиваний:
65
Добавлен:
24.05.2014
Размер:
5 Mб
Скачать

IT-SC book: Advanced CORBA® Programming with C++

because you are free to choose whatever topology suits you best instead of being forced to adapt to a particular topology required by the service.

Your choice of topology for a federation should be governed by two considerations.

The federation structure should reflect the partitioning of your organization into administrative domains. The closer this match is, the easier it will be to maintain and modify the federation.

The federation structure should reflect the frequency distribution of names used by clients. The most frequently used names should be resolved locally, and only less frequently used names should involve more than one server in the federation. This leads to better performance, scalability, and fault tolerance.

As always, if you spend some time analyzing your federation requirements early, you will find that time amply repaid over the lifetime of a system that uses the federated service.

18.14 Adding Naming to the Climate Control System

The climate control system we have developed so far has the problem that the reference to the controller object is passed from the server to the client via a file. Clearly, this is not a distributed solution because either client and server must share a common file system, or the stringified reference to the controller must be copied from the server machine to the client machine.

The Naming Service offers a clean solution to the problem. The server advertises the controller reference in the Naming Service, and the client locates the reference using its name. There still is some coupling between client and server because we assume that both client and server machines either use the same initial naming context or at least use initial naming contexts that are part of the same federation. However, the important point is that the coupling between client and server is much looser now. The client and server are coupled via an external service instead of having to share file systems.

For the climate control system, we advertise only the controller reference in the Naming Service but do not advertise individual thermometers and thermostats. This makes sense because we already have the find operation, which allows clients to locate devices by their name (the asset number or room name). As we discuss in Section 18.11, this is not necessarily the only way to approach naming. Depending on your requirements and how much you are prepared to rely on the availability of the Naming Service, you may choose to advertise more than just a single bootstrapping object in the Naming Service.

18.14.1 Generic Helper Functions

706

IT-SC book: Advanced CORBA® Programming with C++

Before we show the details of how to update the client and the server, we present two helper functions to simplify the source code. Consider the typical sequence of steps to resolve a reference.

Call resolve_initial_references to get a reference to the initial naming context.

Narrow the returned reference to CosNaming::NamingContext. Test for nil to ensure that the reference is of the correct type.

Create a name.

Call resolve to obtain the reference corresponding to the name. Narrow the returned reference to its expected type.

Test for nil to ensure that the reference is of the correct type.

If you go through these steps as in-line code, you will find yourself writing similar code again and again. This not only makes your code harder to test and maintain but also makes it harder to understand because all the extra lines of code can obscure the intent (namely, to use a name to obtain an object reference).

As always in such cases, you can use simple helper functions to improve your code considerably.

Obtaining Initial References Generically

We can create a simple resolve_init helper function that, given a token, returns the reference to the specified initial reference as its correct type. In other words, resolve_init not only obtains the reference but also calls _narrow. To obtain an initial reference—for example, to the Naming Service—we call resolve_init this way:

CosNaming::NamingContext_var inc;

inc = resolve_init<<CosNaming::NamingContext>(orb, "NameService");

Because resolve_init is a template function, we can use it to obtain other initial references—for example, for the Root POA:

PortableServer::POA_var poa;

poa = resolve_init<<PortableServer::POA>(orb, "RootPOA");

Following is the code for resolve_init. We include simple error handling here. As usual, we throw zero for handled exceptions that should terminate the program:

template<<class T> typename T::_ptr_type

resolve_init(CORBA::ORB_ptr orb, const char * id)

{

CORBA::Object_var obj; try {

707

IT-SC book: Advanced CORBA® Programming with C++

obj = orb->resolve_initial_references(id);

}

catch (const CORBA::ORB::InvalidName & e){ throw;

}

catch (const CORBA::Exception & e) {

cerr << "Cannot get initial reference for" << id << ": " << e << endl;

throw 0;

}

assert(!CORBA::is_nil(obj)); typename T::_var_type ref; try {

ref = T::_narrow(obj);

}

catch (const CORBA::Exception & e) { cerr << "Cannot narrow reference for"

<< id << ": " << e << endl; throw 0;

}

if (CORBA::is_nil(ref)) {

cerr << "Incorrect type of reference for" << id << endl;

throw 0;

}

return ref._retn();

}

This code illustrates use of the _ptr_type and _var_type aliases you saw in Section 7.6.1. The aliases permit us to use _ptr and _var references in the template function without having to declare additional template parameters for these types. Without the aliases, resolve_init would require three template parameters instead of one:

template<<class T, class T_ptr, class T_var> T_ptr

resolve_init(CORBA::ORB_ptr orb, const char * id)

{

// ...

}

// ...

CosNaming::NamingContext_var inc; inc = resolve_init<<

CosNaming::NamingContext, CosNaming::NamingContext_ptr, CosNaming::NamingContext_var

>(orb, "NameService");

The _ptr_type and _var_type definitions allow us to avoid such verbose template instantiations.[9]

[9] The _ptr_type and _var_type aliases were added to the mapping only recently. If your ORB does not yet provide them, you must use the three-parameter version of resolve_init.

708

IT-SC book: Advanced CORBA® Programming with C++

Note that the C++ mapping also generates _var_type definitions for structures, unions, and sequences. If you want to create template functions that deal with these types, you can refer to the corresponding _var type from inside the template.

Resolving Bindings Generically

You can use a similar helper function to resolve bindings in a naming context. Again, the helper function hides the call to _narrow and provides error handling. The client calls it this way:

CosNaming::NamingContext_var inc = ...; // Get initial context

CosNaming::Name n; n.length(2);

n[0].id = CORBA::string_dup("CCS"); n[1].id = CORBA::string_dup("Controller"); CCS::Controller_var ctrl;

ctrl = resolve_name<<CCS::Controller>(inc, n);

The resolve_name template function is quite similar to resolve_init:

template<<class T> typename T::_ptr_type resolve_name(

CosNaming::NamingContext_ptr nc, const CosNaming::Name & name)

{

CORBA::Object_var obj; try {

obj = nc->resolve(name);

}

catch (const CosNaming::NamingContext::NotFound & e) { throw;

}

catch (const CORBA::Exception & e) {

cerr << "Cannot resolve binding: " << e << endl; throw 0;

}

if (CORBA::is_nil(obj)) {

cerr << "Nil binding in Naming Service" << endl; throw 0;

}

typename T::_var_type ref; try {ref = T::_narrow(obj);

}

catch (const CORBA::Exception & e) {cerr << "Cannot narrow reference: " << e << endl;

throw 0;

}

if (CORBA::is_nil(ref)) {

cerr << "Reference has incorrect type" << endl; throw 0;

709

IT-SC book: Advanced CORBA® Programming with C++

}

return ref._retn();

}

18.14.2 Updating the Climate Control System Server

The Naming Service allows us to avoid passing a stringified IOR from server to client. For this example, whenever the climate control server starts, it readvertises the controller reference using the name CCS/Controller. The code uses the resolve_init template function defined in Section 18.14.1 to get a reference to the initial naming context:

#include <CosNaming.hh> // ORB-specific

// ...

int

main(int argc, char * argv[])

{

try {

//...

//Create controller servant and get its refe

rence.

CCS::Controller_var ctrl = ...;

// Get reference to initial naming context. CosNaming::NamingContext_var inc

= resolve_init<CosNaming::NamingContext>( orb, "NameService"

);

// Attempt to create CCS context. CosNaming::Name n;

n.length(1);

n[0].id = CORBA::string_dup("CCS"); try {

CosNaming::NamingContext_var nc

=inc->bind_new_context(n);

}catch (const CosNaming::NamingContext::Alre adyBound &) {

//Fine, CCS context already exists.

}

//Force binding of controller reference to make

//sure it is always up-to-date.

n.length(2);

n[1].id = CORBA::string_dup("Controller"); inc->rebind(n, ctrl);

// ...

}

catch (const CORBA::Exception & e) {

cerr < "Uncaught CORBA exception: " < e < endl; return 1;

710

IT-SC book: Advanced CORBA® Programming with C++

}

 

catch (...) {

// Unexpected exception, dump core

abort();

}

 

return 0;

 

}

The server code includes the generated stub header file for the Naming Service. Note that the include directive for CosNaming.hh is ORB-specific because CORBA does not standardize the names or locations of header files. However, most ORBs ship with precompiled headers and stub libraries, so you do not have to separately compile the IDL for the Naming Service.

The remainder of the code is trivial. After obtaining the initial naming context, the code attempts to create the CCS context by calling bind_new_context. If the context already exists, the operation raises AlreadyBound, which is ignored. The second step is to call rebind, which unconditionally creates a new binding for the controller or replaces whatever reference was previously bound with the name Controller. For a persistent server, strictly speaking it is not necessary to replace the reference if it is already bound. However, it does no harm and ensures that the reference is always up-to- date even if the server was moved into a different location domain (see Chapter 14).

18.14.3 Updating the Climate Control System Client

The resolve_init and resolve_name template functions defined in Section 18.14.1 make it trivial to modify the client to retrieve the controller reference from the Naming Service instead of the command line:

#include <CosNaming.hh> // ORB-specific

// ...

int

main(int argc, char * argv[])

{

try {

// Initialize the ORB

CORBA::ORB_var orb = CORBA::ORB_init(argc, ar gv);

//Check arguments if (argc != 1) {

cerr << "Usage: client" << endl; throw 0;

}

//Get reference to initial naming context. CosNaming::NamingContext_var inc

=resolve_init<<CosNaming::NamingContext>( orb, "NameService"

);

711