- •Advanced CORBA® Programming with C++
- •Review
- •Dedication
- •Preface
- •Prerequisites
- •Scope of this Book
- •Acknowledgments
- •Chapter 1. Introduction
- •1.1 Introduction
- •1.2 Organization of the Book
- •1.3 CORBA Version
- •1.4 Typographical Conventions
- •1.5 Source Code Examples
- •1.6 Vendor Dependencies
- •1.7 Contacting the Authors
- •Part I: Introduction to CORBA
- •Chapter 2. An Overview of CORBA
- •2.1 Introduction
- •2.2 The Object Management Group
- •2.3 Concepts and Terminology
- •2.4 CORBA Features
- •2.5 Request Invocation
- •2.6 General CORBA Application Development
- •2.7 Summary
- •Chapter 3. A Minimal CORBA Application
- •3.1 Chapter Overview
- •3.2 Writing and Compiling an IDL Definition
- •3.3 Writing and Compiling a Server
- •3.4 Writing and Compiling a Client
- •3.5 Running Client and Server
- •3.6 Summary
- •Part II: Core CORBA
- •Chapter 4. The OMG Interface Definition Language
- •4.1 Chapter Overview
- •4.2 Introduction
- •4.3 Compilation
- •4.4 Source Files
- •4.5 Lexical Rules
- •4.6 Basic IDL Types
- •4.7 User-Defined Types
- •4.8 Interfaces and Operations
- •4.9 User Exceptions
- •4.10 System Exceptions
- •4.11 System Exceptions or User Exceptions?
- •4.12 Oneway Operations
- •4.13 Contexts
- •4.14 Attributes
- •4.15 Modules
- •4.16 Forward Declarations
- •4.17 Inheritance
- •4.18 Names and Scoping
- •4.19 Repository Identifiers and pragma Directives
- •4.20 Standard Include Files
- •4.21 Recent IDL Extensions
- •4.22 Summary
- •Chapter 5. IDL for a Climate Control System
- •5.1 Chapter Overview
- •5.2 The Climate Control System
- •5.3 IDL for the Climate Control System
- •5.4 The Complete Specification
- •Chapter 6. Basic IDL-to-C++ Mapping
- •6.1 Chapter Overview
- •6.2 Introduction
- •6.3 Mapping for Identifiers
- •6.4 Mapping for Modules
- •6.5 The CORBA Module
- •6.6 Mapping for Basic Types
- •6.7 Mapping for Constants
- •6.8 Mapping for Enumerated Types
- •6.9 Variable-Length Types and _var Types
- •6.10 The String_var Wrapper Class
- •6.11 Mapping for Wide Strings
- •6.12 Mapping for Fixed-Point Types
- •6.13 Mapping for Structures
- •6.14 Mapping for Sequences
- •6.15 Mapping for Arrays
- •6.16 Mapping for Unions
- •6.17 Mapping for Recursive Structures and Unions
- •6.18 Mapping for Type Definitions
- •6.19 User-Defined Types and _var Classes
- •6.20 Summary
- •Chapter 7. Client-Side C++ Mapping
- •7.1 Chapter Overview
- •7.2 Introduction
- •7.3 Mapping for Interfaces
- •7.4 Object Reference Types
- •7.5 Life Cycle of Object References
- •7.6 Semantics of _ptr References
- •7.7 Pseudo-Objects
- •7.8 ORB Initialization
- •7.9 Initial References
- •7.10 Stringified References
- •7.11 The Object Pseudo-Interface
- •7.12 _var References
- •7.13 Mapping for Operations and Attributes
- •7.14 Parameter Passing Rules
- •7.15 Mapping for Exceptions
- •7.16 Mapping for Contexts
- •7.17 Summary
- •Chapter 8. Developing a Client for the Climate Control System
- •8.1 Chapter Overview
- •8.2 Introduction
- •8.3 Overall Client Structure
- •8.4 Included Files
- •8.5 Helper Functions
- •8.6 The main Program
- •8.7 The Complete Client Code
- •8.8 Summary
- •Chapter 9. Server-Side C++ Mapping
- •9.1 Chapter Overview
- •9.2 Introduction
- •9.3 Mapping for Interfaces
- •9.4 Servant Classes
- •9.5 Object Incarnation
- •9.6 Server main
- •9.7 Parameter Passing Rules
- •9.8 Raising Exceptions
- •9.9 Tie Classes
- •9.10 Summary
- •Chapter 10. Developing a Server for the Climate Control System
- •10.1 Chapter Overview
- •10.2 Introduction
- •10.3 The Instrument Control Protocol API
- •10.4 Designing the Thermometer Servant Class
- •10.5 Implementing the Thermometer Servant Class
- •10.6 Designing the Thermostat Servant Class
- •10.7 Implementing the Thermostat Servant Class
- •10.8 Designing the Controller Servant Class
- •10.9 Implementing the Controller Servant Class
- •10.10 Implementing the Server main Function
- •10.11 The Complete Server Code
- •10.12 Summary
- •Chapter 11. The Portable Object Adapter
- •11.1 Chapter Overview
- •11.2 Introduction
- •11.3 POA Fundamentals
- •11.4 POA Policies
- •11.5 POA Creation
- •11.6 Servant IDL Type
- •11.7 Object Creation and Activation
- •11.8 Reference, ObjectId, and Servant
- •11.9 Object Deactivation
- •11.10 Request Flow Control
- •11.11 ORB Event Handling
- •11.12 POA Activation
- •11.13 POA Destruction
- •11.14 Applying POA Policies
- •11.15 Summary
- •Chapter 12. Object Life Cycle
- •12.1 Chapter Overview
- •12.2 Introduction
- •12.3 Object Factories
- •12.4 Destroying, Copying, and Moving Objects
- •12.5 A Critique of the Life Cycle Service
- •12.6 The Evictor Pattern
- •12.7 Garbage Collection of Servants
- •12.8 Garbage Collection of CORBA Objects
- •12.9 Summary
- •Part III: CORBA Mechanisms
- •Chapter 13. GIOP, IIOP, and IORs
- •13.1 Chapter Overview
- •13.2 An Overview of GIOP
- •13.3 Common Data Representation
- •13.4 GIOP Message Formats
- •13.5 GIOP Connection Management
- •13.6 Detecting Disorderly Shutdown
- •13.7 An Overview of IIOP
- •13.8 Structure of an IOR
- •13.9 Bidirectional IIOP
- •13.10 Summary
- •14.1 Chapter Overview
- •14.2 Binding Modes
- •14.3 Direct Binding
- •14.4 Indirect Binding via an Implementation Repository
- •14.5 Migration, Reliability, Performance, and Scalability
- •14.6 Activation Modes
- •14.7 Race Conditions
- •14.8 Security Considerations
- •14.9 Summary
- •Part VI: Dynamic CORBA
- •Chapter 15 C++ Mapping for Type any
- •15.1 Chapter Overview
- •15.2 Introduction
- •15.3 Type any C++ Mapping
- •15.4 Pitfalls in Type Definitions
- •15.5 Summary
- •Chapter 16. Type Codes
- •16.1 Chapter Overview
- •16.2 Introduction
- •16.3 The TypeCode Pseudo-Object
- •16.4 C++ Mapping for the TypeCode Pseudo-Object
- •16.5 Type Code Comparisons
- •16.6 Type Code Constants
- •16.7 Type Code Comparison for Type any
- •16.8 Creating Type Codes Dynamically
- •16.9 Summary
- •Chapter 17. Type DynAny
- •17.1 Chapter Overview
- •17.2 Introduction
- •17.3 The DynAny Interface
- •17.4 C++ Mapping for DynAny
- •17.5 Using DynAny for Generic Display
- •17.6 Obtaining Type Information
- •17.7 Summary
- •Part V: CORBAservices
- •Chapter 18. The OMG Naming Service
- •18.1 Chapter Overview
- •18.2 Introduction
- •18.3 Basic Concepts
- •18.4 Structure of the Naming Service IDL
- •18.5 Semantics of Names
- •18.6 Naming Context IDL
- •18.7 Iterators
- •18.8 Pitfalls in the Naming Service
- •18.9 The Names Library
- •18.10 Naming Service Tools
- •18.11 What to Advertise
- •18.12 When to Advertise
- •18.13 Federated Naming
- •18.14 Adding Naming to the Climate Control System
- •18.15 Summary
- •Chapter 19. The OMG Trading Service
- •19.1 Chapter Overview
- •19.2 Introduction
- •19.3 Trading Concepts and Terminology
- •19.4 IDL Overview
- •19.5 The Service Type Repository
- •19.6 The Trader Interfaces
- •19.7 Exporting Service Offers
- •19.8 Withdrawing Service Offers
- •19.9 Modifying Service Offers
- •19.10 The Trader Constraint Language
- •19.11 Importing Service Offers
- •19.12 Bulk Withdrawal
- •19.13 The Admin Interface
- •19.14 Inspecting Service Offers
- •19.15 Exporting Dynamic Properties
- •19.16 Trader Federation
- •19.17 Trader Tools
- •19.18 Architectural Considerations
- •19.19 What to Advertise
- •19.20 Avoiding Duplicate Service Offers
- •19.21 Adding Trading to the Climate Control System
- •19.22 Summary
- •Chapter 20. The OMG Event Service
- •20.1 Chapter Overview
- •20.2 Introduction
- •20.3 Distributed Callbacks
- •20.4 Event Service Basics
- •20.5 Event Service Interfaces
- •20.6 Implementing Consumers and Suppliers
- •20.7 Choosing an Event Model
- •20.8 Event Service Limitations
- •20.9 Summary
- •Part VI: Power CORBA
- •Chapter 21. Multithreaded Applications
- •21.1 Chapter Overview
- •21.2 Introduction
- •21.3 Motivation for Multithreaded Programs
- •21.4 Fundamentals of Multithreaded Servers
- •21.5 Multithreading Strategies
- •21.6 Implementing a Multithreaded Server
- •21.7 Servant Activators and the Evictor Pattern
- •21.8 Summary
- •22.1 Chapter Overview
- •22.2 Introduction
- •22.3 Reducing Messaging Overhead
- •22.4 Optimizing Server Implementations
- •22.5 Federating Services
- •22.6 Improving Physical Design
- •22.7 Summary
- •Appendix A. Source Code for the ICP Simulator
- •Appendix B. CORBA Resources
- •Bibliography
IT-SC book: Advanced CORBA® Programming with C++
18.5.6 Absolute Versus Relative Names
It is important to realize that the OMG Naming Service does not support the concept of an absolute name because a naming graph does not have a distinguished root context. (As you saw in Figure 18.1, a naming graph can have several root contexts.) This means that a name makes sense only when interpreted relative to a starting context. Interpretation of the name begins at this starting context, and each name component identifies a binding within that context, either to the next context down the line or to a binding that points at an application object. This means that all components of a name except the final component must identify bindings to context objects. The final component can identify either a context or an application object. (This is similar to file names, in which each pathname component except for the final one must name a directory.)
18.5.7 Name Resolution
Interpretation of a name relative to a context is called resolving the name. Name resolution begins at a starting context. The Naming Service searches the starting context for a binding that matches the first component of the name. If such a binding exists, the binding identifies an IOR to another context or application object. If the name has further components, the IOR identified by the first component points to another context, which is then searched for the second component, and so on. This resolution process continues until all name components are resolved and yields the object reference identified by the final component of the name. For an arbitrary operation op invoked on context cxt, using a name with components c1, c2, ..., cn, we can recursively define name resolution as follows:
cxt -> op([c1, c2, ..., cn]) = cxt -> resolve([c1]) -> op([c2, ...,
cn])
This looks complicated, but it just describes the process for identifying a file or directory via its pathname: we use each component of the pathname to walk the directory hierarchy until all names have been exhausted. The operation op is applied to the file or directory identified by the final component.
18.6 Naming Context IDL
Most of the functionality of the Naming Service is provided by the NamingContext interface. This interface defines a number of exceptions and operations. Instead of presenting the full interface in a single definition, we show it incrementally. First, we discuss the exceptions defined by the interface, and then we cover the various operations.
18.6.1 Naming Service Exceptions
675
IT-SC book: Advanced CORBA® Programming with C++
The NamingContext interface defines a number of exceptions that can be raised by the various operations:
module CosNaming { // ...
interface NamingContext { enum NotFoundReason {
missing_node, not_context, not_object
};
exception NotFound { NotFoundReason why; Name rest_of_name;
};
exception CannotProceed { NamingContext cxt; Name rest_of_name;
};
exception InvalidName {}; exception AlreadyBound {}; exception NotEmpty {};
// ...
}; // ...
};
NotFound Exception
This exception is raised by operations that require a name for lookup if the name does not resolve to an existing binding. The NotFound exception contains two data members. why
The why member provides more information as to why a lookup failed. missing_node
One of the components of a name specifies a binding that does not exist. not_context
One of the components of a name (other than the final component) specifies a binding to an application object instead of to a context.
not_object
One of the components of a name specifies an object reference that dangles (points to a non-existent object).
rest_of_name
The rest_of_name member contains the trailing part of the name that could not be resolved.
CannotProceed Exception
This exception indicates that the implementation has given up for some reason. Typically, this happens when a name binding denotes a context in a different Naming Service implemented in a remote process, but that context could not be reached during name
676
IT-SC book: Advanced CORBA® Programming with C++
resolution (for example, because the network is down). The CannotProceed exception contains two data members.
cxt
This is the object reference to the context containing the first unresolved binding. rest_of_name
This member contains the unresolved remainder of the name.
InvalidName Exception
This exception is raised if you attempt to resolve an empty name (a Name sequence with length zero, containing no components). If your Naming Service implementation restricts the permissible characters for name components, it raises this exception if you attempt to create a binding that contains an illegal character.
AlreadyBound Exception
This exception is raised if you attempt to create a binding that already exists. (Remember, name bindings must be unique within their parent context.)
NotEmpty Exception
This exception is raised if you attempt to destroy a context that still contains bindings. (As you will see in Section 18.6.7, a context must be empty before you can destroy it.)
18.6.2 Context Life Cycle Operations
The NamingContext interface contains three operations that allow you to create and destroy naming contexts:
interface NamingContext { // ...
NamingContext new_context();
NamingContext bind_new_context(in Name n) raises( NotFound, CannotProceed, InvalidName, AlreadyBound
);
void destroy() raises(NotEmpty); // ...
};
Both new_context and bind_new_context are factory operations that create a new naming context. Note that to create a context, you must have a reference to a naming context because the NamingContext interface also acts as the factory for new contexts. Section 18.6.3 discusses how to obtain a reference to an initial naming context.
new_context
677
IT-SC book: Advanced CORBA® Programming with C++
This operation creates a new, empty naming context. Note that the operation does not accept an in parameter that could be used to give a name to the new context. This means that the new context is not bound into the naming graph by any name and therefore is orphaned. You can bind the new context into the graph later by calling the bind operation (see Section 18.6.4).
The reason for providing a factory operation that creates orphaned contexts is that you may want to create a binding in one Naming Service that denotes a context in a different Naming Service (one that is implemented by a different process, possibly on a remote machine). To do this, you first create an orphaned context in one service and then add a binding to the second service in a separate step.
Because bindings are provided by object references, a single connected naming graph can span servers on different machines. Such distribution of a single logical service over multiple physical servers is known as federation. We discuss federated naming in
Section 18.13.
bind_new_context
This factory operation creates a new context and binds the new context under name n into the context on which bind_new_context was invoked. Typically, you will use this operation instead of new_context because it both creates and names a context in a single step. bind_new_context is analogous to the UNIX mkdir command.
bind_new_context can raise some of the exceptions discussed in Section 18.6.1. For example, an AlreadyBound exception indicates that the binding passed to bind_new_context is already in use, and NotFound indicates that the name n could not be resolved to a target context on which to invoke the bind_new_context operation. For the remainder of this chapter, we do not explicitly discuss the exceptions raised by operations. In all cases, they have the semantics explained in Section 18.6.1.
destroy
The destroy operation destroys a context. You can destroy a context only if it is empty (contains no bindings). The destroy operation, however, is not analogous to the UNIX rmdir command: rmdir both destroys a directory and removes its name from the parent directory. In contrast, destroy only destroys a context and does not remove any bindings to the destroyed context that may still exist in parent contexts. If you destroy a context that is bound into a parent context under some name, you must also invoke an unbind operation (see Section 18.6.7) on the parent context; otherwise, you will leave a dangling binding behind. You will see source code examples of how to correctly destroy contexts in Section 18.6.8.
18.6.3 Obtaining an Initial Naming Context
678
IT-SC book: Advanced CORBA® Programming with C++
Before we further explore the NamingContext interface, let us look at how a client obtains a reference to an initial naming context. In Section 9.6 you saw the resolve_initial_references operation. resolve_initial_references not only returns a reference to the Root POA but also serves as the bootstrap mechanism for a number of other objects and services, including the Naming Service. Here is the relevant PIDL:
module CORBA { // PIDL // ...
interface ORB {
typedef string ObjectId;
typedef sequence<ObjectId> ObjectIdList; exception InvalidName {};
Object resolve_initial_references(in ObjectId id) raises(InvalidName);
ObjectIdList list_initial_services(); // ...
}; // ...
};
resolve_initial_references allows you to portably obtain references that are crucial for bootstrapping your client or server. The id parameter to the call determines which particular reference is returned. The OMG standardizes the set of well-known object identifiers. Currently, they are RootPOA, POACurrent, InterfaceRepository, NameService, TradingService, SecurityCurrent, and TransactionCurrent. This list is extended from time to time as new features are added to CORBA.
resolve_initial_references can return a nil reference. This can happen, for example, if someone has misconfigured the ORB or if the ORB attempts to obtain the initial reference from a remote location and fails. If you pass an unknown object identifier to the operation, it raises InvalidName. If the call fails for some other reason, the operation raises a system exception.
list_initial_services simply returns the list of object identifiers configured for your ORB. Note that the returned list includes only those object identifiers for which your ORB actually provides an implementation. For example, an ORB that does not have a security implementation will not return SecurityCurrent. Also, your ORB may add additional object identifiers, not specified by the OMG, for proprietary extensions.
If you call resolve_initial_references with an object identifier of NameService, the operation returns a reference to an object of type NamingContext. The returned context is the configured initial context of the Naming Service for the local ORB. You must narrow the returned reference before you can use it:
679
IT-SC book: Advanced CORBA® Programming with C++
// Initialize the ORB.
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
//Get reference to initial naming context. CORBA::Object_var obj;
obj = orb->resolve_initial_references("NameService");
//Narrow
CosNaming::NamingContext_var inc; // Initial naming context inc = CosNaming::NamingContext::_narrow(obj); assert(!CORBA::is_nil(inc));
Note that the assertion at the end of this code fragment is justified. You will never receive a nil reference from resolve_initial_references (if the call fails, it raises an exception). If _narrow fails (in the sense that it cannot determine the type of reference), it also raises an exception. This means that the only way the assertion could fail is if the configured reference for the Naming Service were of the wrong type. That would be a serious ORB configuration error.
Exactly which reference (to what exact context) is returned by the call is an ORB configuration issue and is not further specified by CORBA. You should consult your ORB documentation to find out how resolve_initial_references decides which IOR to return. With some ORBs, you can edit a configuration file to change the initial reference, whereas other ORBs hard-wire the initial reference into the run time or rely on the implementation repository to store this information.[4]
[4] The revised Naming Service specification, under review at the time of this writing, will standardize at least some of these configuration issues.
18.6.4 Creating a Binding
The NamingContext interface contains two operations to create bindings: one for ordinary objects and one for contexts.
interface NamingContext { // ...
void bind(in Name n, in Object obj) raises(
NotFound, CannotProceed, InvalidName, AlreadyBound
);
void bind_context(in Name n, in NamingContext nc) raises( NotFound, CannotProceed, InvalidName, AlreadyBound
); // ...
};
bind
680
IT-SC book: Advanced CORBA® Programming with C++
The bind operation adds the name n to the context on which bind is invoked. The new name denotes the passed reference obj. This is the operation you must use if you want to give a name to one of your objects. Note that you can bind a nil reference even though it is rather meaningless. We suggest that you not do this.
bind_context
The bind_context operation works like bind but is used to bind contexts instead of normal application objects. The parameter nc has the type NamingContext, and that makes it impossible to pass something that is not a naming context. Attempts to bind a nil reference as a context raise a BAD_PARAM exception.
If you use bind (instead of bind_context) to bind a context object, the bind operation will work, but the binding will behave like an ordinary binding to an application object. If you incorrectly bind a context with bind instead of bind_context, the bound context will not participate in name resolution because as far as the Naming Service is concerned, the context will be treated like an application object.
18.6.5 Creating a Naming Graph
When you create or navigate a naming graph, you can either navigate the structure from node to node explicitly, or you can use names relative to a root. This is analogous to the following sequences of UNIX commands, each of which creates three directories:
mkdir app2; cd app2; mkdir devices; cd devices; mkdir cd
This command sequence creates each directory and then changes to the new directory before creating the next directory along the path. The alternative is
mkdir app2; mkdir app2/devices; mkdir app2/devices/cd
Here, we use pathnames relative to the starting directory to create all three directories. Whether you use the first or the second style is largely a matter of taste. We show the equivalent of both approaches in this section.
Creating a Naming Graph Relative to Newly Created Contexts
Let us examine the source code for creating a subsection of the naming graph we saw earlier (see Figure 18.1). We assume that the initial naming context is empty when we start and that we want to create the complete structure shown in Figure 18.2. As in a file system, we create the graph from the root toward the leaves, so the first step is to create the app2 context. Note that we omit exception handling in these examples:
681
IT-SC book: Advanced CORBA® Programming with C++
Figure 18.2 Small naming graph.
CosNaming::NamingContext_var inc = ...; // Get initial context
CosNaming::NamingContext_var app2; |
// Create orphaned context |
app2 = inc->new_context(); |
|
CosNaming::Name name; |
// Initialize name |
name.length(1);
name[0].id = CORBA::string_dup("app2"); name[0].kind = CORBA::string_dup("");
inc->bind_context(name, app2); // Bind new context
Executing this code creates the graph shown in Figure 18.3. The preceding code first creates the new context and then adds a binding for it to the root context. Instead, we could have used bind_new_context to achieve the graph in Figure 18.3 in a single step:
Figure 18.3 Graph after creating the app2 context.
CosNaming::NamingContext_var inc = ...; // Get initial context
CosNaming::Name name; // Initialize name name.length(1);
name[0].id = CORBA::string_dup("app2"); // kind is empty string
682
IT-SC book: Advanced CORBA® Programming with C++
CosNaming::NamingContext_var app2;
app2 = inc->bind_new_context(name); // Create and bind context
Note that in this example, not only do we create and name the context in a single step, but we also omit the explicit initialization of the kind member of the name component. This works because nested strings are initialized to the empty string instead of null.[5]
[5] At least with CORBA 2.3. With CORBA 2.2 and earlier, you must initialize the kind field.
The next step is to create the devices and collections contexts within the app2 context. Assuming that we continue the preceding code, this can be written as follows:
name[0].id = CORBA::string_dup("devices"); CosNaming::NamingContext_var devices; devices = app2->bind_new_context(name);
name[0].id = CORBA::string_dup("collections"); CosNaming::NamingContext_var collections; collections = app2->bind_new_context(name);
The code simply uses the app2 context we created before to create and bind the two new contexts by calling bind_new_context, creating the graph in Figure 18.4.
Figure 18.4 Graph after creating the devices and collections contexts.
The next step is to create the cd context and to establish the correct bindings. We create and bind the cd context to the devices context and then add the other two bindings using bind_context:
name[0].id = CORBA::string_dup("cd"); |
// Make cd context |
CosNaming::NamingContext_var cd; |
// devices -> cd |
cd = devices->bind_new_context(name); |
|
collections->bind_context(name, cd); |
// collections -> cd |
name[0].id = CORBA::string_dup("app2"); |
// cd -> app2 |
cd->bind_context(name, app2); |
683
IT-SC book: Advanced CORBA® Programming with C++
This code creates the graph shown in Figure 18.5.
Figure 18.5 Graph after adding the cd context.
All that remains is to add the binding for dev1 to the devices context. We assume here that dev1 is actually an object reference to our CCS::Controller object:
CCS::Controller_var ctrl = ...; |
// Get controller ref |
name[0].id = CORBA::string_dup("dev1"); |
|
devices->bind(name, ctrl); |
// Add controller to graph |
This completes creation of the entire graph shown in Figure 18.2.
Creating a Naming Graph from an Initial Context
The preceding example used names with exactly one name component to create the graph. At each step, we used a context created in the preceding step to create the next binding. Alternatively, we could use names relative to the root context:
CosNaming::NamingContext_var inc = ...; |
// Get initial context |
CosNaming::Name name; |
|
name.length(1); |
// kind is empty |
name[0].id = CORBA::string_dup("app2"); |
|
CosNaming::NamingContext_var app2; |
// inc -> app2 |
app2 = inc->bind_new_context(name); |
name.length(2);
name[1].id = CORBA::string_dup("collections"); CosNaming::NamingContext_var collections;
collections = inc->bind_new_context(name); // app2 -> collections
name[1].id = CORBA::string_dup("devices");
684
IT-SC book: Advanced CORBA® Programming with C++
CosNaming::NamingContext_var devices; |
// app2 -> devices |
devices = inc->bind_new_context(name); |
|
name.length(3); |
|
name[2].id = CORBA::string_dup("cd"); |
|
CosNaming::NamingContext_var cd; |
// devices -> cd |
cd = inc->bind_new_context(name); |
|
name.length(4); |
|
name[3].id = CORBA::string_dup("app2"); |
// cd -> app2 |
inc->bind_context(name, app2); |
|
CCS::Controller_var ctrl = ...; |
|
name.length(3); |
|
name[2].id = CORBA::string_dup("dev1"); |
// devices -> dev1 |
inc->bind(name, ctrl); |
|
name[1].id = CORBA::string_dup("collections"); |
|
name[2].id = CORBA::string_dup("cd"); |
// collections -> cd |
inc->bind_context(name, cd); |
|
This code also creates the graph shown in Figure 18.2 but uses names relative to the initial naming context. Note that at each step, we assign only to those name components that must change for the next step instead of redundantly initializing all name components. (Of course, this makes the order in which we do things significant.)
Also note that all calls to bind_new_context assign the return value to a _var reference even if the return value is not used again. This technique avoids leaking references.
18.6.6 Rebinding
If you attempt to create a binding that already exists, the operation fails with an AlreadyBound exception. For example:
CORBA::Object_var obj = ...; |
// Get some reference |
CosNaming::NamingContext_var cxt = ...; |
// Get some context |
CosNaming::Name name; |
// Initialize name |
name.length(1); |
|
name[0].id = CORBA::string_dup("Fred"); |
|
cxt->bind(name, obj); |
// Advertise as "Fred" |
bool got_AlreadyBound = false; |
|
try { |
// Try same name again |
cxt->bind(name, obj); |
}
catch (const CosNaming::NamingContext::AlreadyBound &) { cout < "Got AlreadyBound, as expected" < endl; got_AlreadyBound = true;
}
assert(got_AlreadyBound); |
// Must pass this |
685
IT-SC book: Advanced CORBA® Programming with C++
This code calls bind twice with the name Fred, but only the first call succeeds; the second call raises AlreadyBound.
The NamingContext interface provides two operations you can use to force creation of a new binding whether or not that binding is already in use:
interface NamingContext { // ...
void rebind(in Name n, in Object obj) raises( NotFound, CannotProceed, InvalidName
);
void rebind_context(in Name n, in NamingContext nc) raises( NotFound, CannotProceed, InvalidName
); // ...
};
The rebind and rebind_context operations behave like bind and bind_context, but they create the requested binding whether or not it is already in use. If a binding with the specified name already exists, it is simply dropped. We can rewrite the code using rebind so that the second attempt to create a binding succeeds:
CORBA::Object_var obj = ...; |
// Get some reference |
CosNaming::NamingContext_var cxt = ...; |
// Get some context |
CosNaming::Name name; |
// Initialize name |
name.length(1);
name[0].id = CORBA::string_dup("Fred");
cxt->rebind(name, |
obj); |
// |
Advertise as "Fred" |
cxt->rebind(name, |
obj); |
// |
Fine, no exception here |
The rebind operation is useful if you want to ensure that a binding is created whether or not the binding already exists. Typically, this happens on server start-up, when the server wants to advertise an initial object and ensure that the latest, current reference to the object appears in the naming graph.
Note that you should exercise some caution, particularly when calling rebind_context, because it can lead to orphaned contexts:
CosNaming::NamingContext_var cxt = ...; |
// Get some context |
CosNaming::Name name; |
// Initialize name |
name.length(1); |
|
name[0].id = CORBA::string_dup("Fred"); |
|
CosNaming::NamingContext_var nc1; |
// Create and bind nc1 |
nc1 = cxt->bind_new_context(name); |
|
// ... |
|
CosNaming::NamingContext_var nc2; |
|
686
IT-SC book: Advanced CORBA® Programming with C++
nc2 = cxt->new_context(); |
// |
Make another |
context |
cxt->rebind_context(name, nc2); |
// |
Oops, nc1 is |
orphaned! |
Here, the call to rebind_context uses the same name Fred to bind nc2 and replaces the existing binding to nc1, so nc1 ends up orphaned. Note that this problem is not limited to rebind_context. If you call rebind to advertise one of your objects but with a name that currently binds a context, you will correctly advertise your object but orphan the context in the process.
Inadvertently orphaning a context in this way means that you cannot easily find the context again because you can no longer navigate to it. Most vendors offer administrative tools that allow you to recover the object references to orphaned contexts and to reconnect them into the graph. (This is similar to the UNIX fsck command, which reconnects lost inodes to the lost+found directory after a crash.) However, such tools are of limited utility because they only allow you to find orphaned contexts but cannot tell you the names the contexts had when they were orphaned.
In general, you should have no reason to call rebind_context (this operation was mainly added for symmetry with bind).[6] We suggest that you therefore restrict yourself to calling rebind and use it only to ensure that the correct reference to an initial application object is always advertised on start-up of a server.
[6] In our opinion, it would have been better not to provide rebind_context at all because the danger of creating orphaned contexts outweighs the usefulness of the operation. The revised Naming Service will most likely raise a NotFound exception if a call to rebind_context would change the type of an existing binding from ncontext to nobject or vice versa. This eliminates some errors, but the operation remains dangerous.
18.6.7 Removing Bindings
The unbind operation removes a binding from the graph:
interface NamingContext { // ...
void unbind(in Name n) raises(
NotFound, CannotProceed, InvalidName
); // ...
};
unbind removes a binding whether the binding denotes a context or an application object. Like rebind and rebind_context, the unbind operation has the potential to create orphaned contexts. Here is a code example that removes the bindings collections and dev1 from the graph shown in Figure 18.2:
CosNaming::NamingContext_var inc = ...; |
// Get initial context |
CosNaming::Name name;
687
IT-SC book: Advanced CORBA® Programming with C++
name.length(2);
name[0].id = CORBA::string_dup("app2"); name[1].id = CORBA::string_dup("collections"); // unbind app2/collections
inc->unbind(name);
name.length(3);
name[1].id = CORBA::string_dup("devices"); name[2].id = CORBA::string_dup("dev1"); // unbind app2/devices/dev1 inc->unbind(name);
This code creates the graph shown in Figure 18.6. Note that removal of the two bindings does not affect the bound objects. The controller object previously bound under the name dev1 still exists (presumably, we still hold a reference to that object elsewhere). Similarly, the context previously named collections still exists but is now orphaned (we can no longer navigate to it via a name).
Figure 18.6 Graph from Figure 18.2 after unbinding collections and dev1.
18.6.8 Destroying Contexts Correctly
The Naming Service provides the bind_new_context operation to both create and bind a naming context in a single step. However, the service does not provide an inverse operation that would both destroy and unbind a context. To correctly destroy a context, you must both destroy it and remove its binding. If you only call unbind, you will leave an orphaned context, and if you only call destroy, you will leave a dangling binding.
Figure 18.7 shows a graph before and after removal of the cd context.
Figure 18.7 Graph from Figure 18.2 after removal of the cd context.
688
IT-SC book: Advanced CORBA® Programming with C++
Following is a code fragment that correctly removes the cd context. It destroys the context itself and also removes all bindings to the context (we assume that the variable cd holds the reference to the cd context).
CosNaming::NamingContext_var inc = ...; |
// Get initial context |
CosNaming::NamingContext_var cd = ...; |
// cd context |
CosNaming::Name name; |
|
// Remove cd -> app2 |
|
name.length(1); |
|
name[0].id = CORBA::string_dup("app2"); |
|
cd->unbind(name);
//cd is now empty, destroy it. cd->destroy();
//Remove devices -> cd name.length(3);
name[1].id = CORBA::string_dup("devices"); name[2].id = CORBA::string_dup("cd"); inc->unbind(name);
//Remove collections -> cd
name[1].id = CORBA::string_dup("collections"); inc->unbind(name);
Note that this code first removes the app2 binding in the cd context before it calls destroy. This is because destroy raises a NotEmpty exception if the context still contains bindings.
To avoid leaving dangling bindings behind, the code correctly removes the two cd bindings in devices and collections. You can first destroy the context and then remove the bindings from its parents, or you can first remove the bindings in the parents and then destroy the context. The order does not matter as long as you call both unbind and destroy.
689
IT-SC book: Advanced CORBA® Programming with C++
18.6.9 Resolving Names
Until now, we have covered operations that allow you to create and destroy a naming graph. These operations are typically used by servers to advertise references in the graph. In contrast, application clients usually are interested only in lookup, to locate references to application objects.
The Naming Service provides the resolve operation, which returns the object reference stored under a name:
interface NamingContext { // ...
Object resolve(in Name n) raises(
NotFound, CannotProceed, InvalidName
);
// ...
};
resolve returns the reference stored under the name n whether the name denotes a context or an application object. The return type is Object because the Naming Service must be able to store references of arbitrary type. This means that you must narrow the returned reference to its correct type before you can use it to invoke operations. The following code retrieves and narrows the controller reference stored under the name dev1 using a name relative to the initial naming context:
CosNaming::NamingContext_var inc = ...; |
// Get initial context |
//Initialize name CosNaming::Name name; name.length(3);
name[0].id = CORBA::string_dup("app2"); name[1].id = CORBA::string_dup("devices"); name[2].id = CORBA::string_dup("dev1");
//Try to resolve
CORBA::Object_var obj; try {
obj = inc->resolve(name);
}
catch (const CosNaming::NamingContext::NotFound &) { cerr << "No name for controller" << endl;
throw 0;
}
catch (const CORBA::Exception & e) {
cerr << "Resolve failed: " << e << endl; throw 0;
}
if (CORBA::is_nil(obj)) {
cerr << "Nil reference for controller!" << endl; throw 0;
}
// Narrow to CCS::Controller CCS::Controller_var ctrl;
690
