Interface Type Checking

# Chapter 14 Interface Type Checking

This chapter describes the mechanism used by omniORB to ensure type safety when object references are exchanged across the network. This mechanism is handled completely within the ORB. There is no programming interface visible at the application level. However, for the sake of diagnosing the problem when there is a type violation, it is useful to understand the underlying mechanism in order to interpret the error conditions reported by the ORB.

## 14.1 Introduction

In GIOP/IIOP, an object reference is encoded as an Interoperable Object Reference (IOR) when it is sent across a network connection. The IOR contains a Repository ID (RepoId) and one or more communication profiles. The communication profiles describe where and how the object can be contacted. The RepoId is a string which uniquely identifies the IDL interface of the object.

Unless the ID pragma is specified in the IDL, the ORB generates the RepoId string in the so-called OMG IDL Format1. For instance, the RepoId for the Echo interface used in the examples of chapter 2 is IDL:Echo:1.0.

When interface inheritance is used in the IDL, the ORB always sends the RepoId of the most derived interface. For example:

// IDL interface A { ... }; interface B : A { ... }; interface C { void op(in A arg); };
// C++ C_ptr server; B_ptr objB; A_ptr objA = objB; server->op(objA); // Send B as A

In the example, the operation C::op() accepts an object reference of type A. The real type of the reference passed to C::op() is B, which inherits from A. In this case, the RepoId of B, and not that of A, is sent across the network.

The GIOP/IIOP specification allows an ORB to send a null string in the RepoId field of an IOR. It is up to the receiving end to work out the real type of the object. omniORB never sends out null strings as RepoIds, but it may receive null RepoIds from other ORBs. In that case, it will use the mechanism described below to ensure type safety.

## 14.2 Interface Inheritance

When the ORB receives an IOR of interface type B when it expects the type to be A, it must find out if B inherits from A. When the ORB has no local knowledge of the type B, it must work out the type of B dynamically.

The CORBA specification defines an Interface Repository (IR) from which IDL interfaces can be queried dynamically. In the above situation, the ORB could contact the IR to find out the type of B. However, this approach assumes that an IR is always available and contains the up-to-date information of all the interfaces used in the domain. This assumption may not be valid in many applications.

An alternative is to use the _is_a() operation to work out the actual type of an object. This approach is simpler and more robust than the previous one because no 3rd party is involved, so this is what omniORB does.

class Object{ CORBA::Boolean _is_a(const char* type_id); };

The _is_a() operation is part of the CORBA::Object interface and must be implemented by every object. The input argument is a RepoId. The function returns true if the object is really an instance of that type, including if that type is a base type of the most derived type of that object.

In the situation above, the ORB would invoke the _is_a() operation on the object and ask if the object is of type A before it processes any application invocation on the object.

Notice that the _is_a() call is not performed when the IOR is unmarshalled. It is performed just prior to the first application invocation on the object. This leads to some interesting failure modes if B reports that it is not an A. Consider the following example:

// IDL interface A { ... }; interface B : A { ... }; interface D { ... }; interface C { A op1(); Object op2(); };
1 // C++ 2 C_ptr objC = //... an object reference from somewhere; 3 A_ptr objA; 4 CORBA::Object_ptr objR; 5 6 objA = objC->op1(); 7 objA->_non_existent(); 8 9 objR = objC->op2(); 10 objA = A::_narrow(objR);

If the stubs of A,B,C,D are linked into the executable and:

Case 1
  C::op1() and C::op2() return a B. Lines 6–10 complete successfully. The remote object is only contacted at line 7.
Case 2
  C::op1() and C::op2() return a D. This condition only occurs if the runtime of the remote end is buggy. Even though the IDL definitions show that D is not derived from A, omniORB gives it the benefit of the doubt, in case it actually has a more derived interface that is derived from both A and D. At line 7, the object is contacted to ask if it is an A. The answer is no, so a CORBA::INV_OBJREF exception is raised. At line 10, the narrow operation will fail, and objA will be set to nil.

If only the stubs of A are linked into the executable and:

Case 1
  C::op1() and C::op2() return a B. Lines 6–10 complete successfully. When lines 7 and 10 are executed, the object is contacted to ask if it is an A.
Case 2
  C::op1() and C::op2() return a D. This condition only occurs if the runtime of the remote end is buggy. Line 6 completes and no exception is raised. At line 7, the object is contacted to ask if it is an A. If the answer is no, a CORBA::INV_OBJREF exception is raised. At line 10, the narrow operation will fail, and objA will be set to nil.

1
  For further details of the repository ID formats, see section 10.6 in the CORBA 2.6 specification.