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

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

The CORBA specification makes no other guarantees. In particular, the specification does not guarantee non-blocking behavior, does not guarantee asynchronous call dispatch, and does not even guarantee that oneway calls will be received in the same order as they were sent. Do not create designs that assume either non-blocking or asynchronous behavior just because operations are declared oneway. The actual behavior at run time of such calls depends on the ORB and typically also depends on whether client and server are threaded and whether or not they are collocated.

IDL defines interfaces, but oneway has nothing to do with the interface of an operation. Instead, it influences the implementation of the operation's call dispatch. As you will see in Section 7.13.1, the C++ interfaces for oneway operations are identical to those of normal operations, and it is possible to invoke a normal operation as if it had been declared as oneway by using the Dynamic Invocation Interface. This indicates that oneway is really an implementation concern and should not have been made a part of IDL, because it operates at a different level of abstraction.

The semantics established by oneway are too weak to be really useful, and we recommend that you avoid the feature. If you need to guarantee non-blocking behavior or want to build some form of signaling mechanism, the CORBA Event Service (see Chapter 20) is likely to be a much better choice. It has defined semantics and avoids the uncertainty associated with oneway. (The CORBA Messaging specification [20], adopted in 1998, has added features that permit you to control the semantics of oneway invocations in more detail. However, ORB vendors are unlikely to offer implementations before mid-1999.)

4.13 Contexts

Operation definitions can optionally use a context clause. For example:

ValType read_value() context("USER", "GROUP", "X*");

The context clause must contain one or more string literals, starting with an alphabetic character and consisting of alphabetics, digits, period (.), underscore (_), and asterisk (*). An asterisk can occur only as the final character.

A context clause permits one or more values to be made available to the server implicitly with a call. The idea is similar to UNIX environment variables, in which a child process automatically inherits the environment of its parent. The preceding declaration states that when a client calls the read_value operation, the values of the client's context variables USER and GROUP, and the value of all context variables starting with X, will be made available to the server. CORBA defines a Context interface that allows you to connect context objects into defaulting hierarchies, something that creates a more powerful mechanism than just a single vector of variables.

Contexts create a number of problems with respect to type safety.

97

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

If a particular context variable is not set by the client, its value is (silently) not transmitted to the server.

This means that the server cannot rely on the value of a particular context variable being available even though it appears in the context clause.

Context variables are untyped.

For the preceding example, the server may expect to find a numerical user ID in the USER variable. However, the client may have placed the user name into the variable.

This illustrates that context clauses provide no guarantees to the server implementation. A context variable may not be set at all, and, even if it is set, it may contain a string that does not correctly decode to the expected type. This is a recipe for disaster because it shoots a big hole through the IDL type system. CORBA implements strict type checking for operations, and that makes it impossible for a client to forget to supply a parameter or to supply a parameter of the wrong type.[3] In contrast, context variables provide no such guarantees.

[3] It is possible to violate the type system by using "sledgehammer" C++ casts. However, if you insist on using casts, you deserve what you get. It is also possible to violate the type system by using the DII incorrectly, but that is the price of its flexibility.

Because IDL contexts are unsafe, we recommend that you avoid using them. It is also possible that contexts may be removed from CORBA, so the future of this (mis)feature is uncertain anyway.

4.14 Attributes

An attribute definition can be used to create something akin to a C++ public member variable:

interface Thermostat {

temperature;

// Probably bad

readonly attribute short

attribute short

nominal_temp;

// Probably bad

};

 

 

The attribute keyword may be used only inside an interface definition. Attributes can be of any type (including user-defined complex types). An attribute defines a pair of operations the client can call to send and receive a value. A readonly attribute defines a single operation the client can call to receive a value.

Attributes look like C++ public member variables, but in fact they do not define storage or state. For example, the following interface is semantically equivalent to the preceding one:

interface Thermostat {

98

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

short

get_temperature();

short

get_nominal_temp();

void

set_nominal_temp(in short t);

};

Even though attribute definitions look like variables, in reality they are just a shorthand for defining a pair of operations (or a single operation for readonly attributes). There simply is no semantic difference between the preceding two interfaces. In both cases, attribute access is implemented by remote procedure calls.

There is a problem relating to attributes, though: an attribute definition cannot contain a raises expression. The following is illegal:

interface Thermostat {

 

 

exception TooHot {};

 

 

exception TooCold {};

temperature;

 

readonly attribute short

 

attribute short

nominal_temp

// Illegal

 

raises(

 

TooHot, TooCold

 

};

);

 

 

 

Attributes cannot raise user exceptions (system exceptions are possible). This makes attributes second-class citizens, because error reporting is quite limited. For example, setting the temperature of a thermostat should raise an out-of-range exception if an attempt is made to set the nominal temperature too high or too low. However, attributes limit you to error reporting via system exceptions. This means that you must resort to a system exception (for example, CORBA::BAD_PARAM) when an illegal temperature is requested. This exception is less informative than TooHot and TooCold user exceptions.

You cannot safely use the minor member in a system exception to encode the "too hot" and "too cold" conditions. This is because the specification gives no guarantee that an ORB will preserve the minor value of a system exception. Most ORBs will preserve it, but, if you rely on this behavior, you are, strictly speaking, outside the CORBA specification. (And, as we point out in Section 4.11, you should not use system exceptions for application-level error conditions anyway.)

The implementation of attributes by the ORB run time is identical to using operations (attributes are implemented as a pair of operations). This means that there is no difference in performance between attribute accesses and operation invocations. Because attributes offer no performance advantage but suffer from limited error reporting, some organizations have banned attributes in their style guides. You may want to consider doing the same.

99

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

If you choose to use attributes, you should limit yourself to readonly attributes. Typically, not all values in the range of a modifiable attribute are legal, so modifiable attributes can lead to the ambiguities caused by raising system exceptions, as with the nominal_temp attribute in the preceding example.

4.15 Modules

IDL uses the module construct to create namespaces. Modules combine related definitions into a logical group and prevent pollution of the global namespace:

module CCS {

LocType;

typedef string

typedef short

TempType;

interface Thermostat {

LocType

get_location();

TempType

get_temperature();

TempType

get_nominal_temp();

void

set_nominal_temp(in TempType t);

};

 

};

 

Identifiers in a module need be unique only within that module. IDL's module scope resolution rules are the same as those for C++: the IDL compiler searches for the definition of an identifier from the innermost scope outward toward the outermost scope. This means that inside the module CCS, a temperature type can be referred to as

TempType, CCS::TempType, and :: CCS::TempType.

Modules do not hide their contents, so you can use a type defined in one module inside another module:

module Weather {

 

 

enum WType { sunny, cloudy, rainy, foggy };

 

interface Forecast {

tomorrows_minimum();

// From module CCS

CCS::TempType

CCS::TempType

tomorrows_maximum();

// From module CCS

WType

outlook();

 

};

 

 

};

 

 

Modules can contain any definition that can appear at global scope (type, constant, exception, and interface definitions). In addition, modules can contain other modules, so you can create nested hierarchies.

The main purpose of modules is to avoid polluting the global namespace. If you place all the definitions for an application into a module that reflects the application's name, you are less likely to clash with definitions created by other developers.

Modules are similar to C++ namespaces in that they can be reopened:

100