
- •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++
consumers would instead have to be directly connected to the supplier event channel. This would mean that the supplier event channel would have five direct consumers rather than two.
Assuming that the supplier event channel and consumer event channel run as separate server processes, this configuration allows the load of obtaining events from suppliers to be handled by the supplier event channel and allows most of the consumer delivery load to be handled by the consumer event channel. The trade-off is the extra hop required to get the events from the supplier channel to the consumer channel.
The steps for connecting event channels are somewhat tricky. If you perform them in the wrong order, your active channel could end up getting disconnection exceptions for each event it tries to deliver because the passive channel is not yet ready to receive events. The proper steps are as follows.
Step 1.
Obtain a ProxyPushSupplier object reference from the supplier event channel (the active end).
Step 2.
Obtain a ProxyPushConsumer object reference from the consumer event channel (the passive end).
Step 3.
Invoke connect_push_supplier on the ProxyPushConsumer, passing it the
ProxyPushSupplier object reference. This lets the passive side of the interchannel connection know about the active side and sets the passive side into the state of being ready to receive events.
Step 4.
Invoke connect_push_consumer on the ProxyPushSupplier, passing it the
ProxyPushConsumer object reference. This lets the active side of the interchannel connection know about the passive side. At this point, the active side can start pushing events to the passive side.
In the following sections we show examples of the actual C++ code you would have to write to set up this kind of connection or any interchannel based on any other of the event delivery models.
20.6 Implementing Consumers and Suppliers
Whether you are implementing a supplier or a consumer that pushes or pulls events, the steps you perform to implement and register them are roughly the same. The general steps required are as follows.
Step 1.
Implement a servant for your push consumer or pull supplier. Both push suppliers and pull consumers are clients, so you do not need to implement servants for those cases.
Step 2.
810

IT-SC book: Advanced CORBA® Programming with C++
Obtain a reference to the event channel. This step depends on your CORBA environment, but it is usually done by using the Naming Service or Trading Service to find an event channel object reference.
Step 3.
Get a ConsumerAdmin reference from the EventChannel if you want to register a consumer, or get a SupplierAdminw reference if you want to register a supplier.
Step 4.
Obtain the appropriate proxy object reference for the event model you want to use from the ConsumerAdmin or SupplierAdmin object.
Step 5.
Invoke the appropriate connection operation on the proxy object.
In the following sections, we show how to implement push and pull flavors of both consumers and suppliers. To keep the example code focused on the Event Service, we create all objects as transient objects of the Root POA. We also assume that a name binding for our event channel already exists directly in the initial NamingContext returned from ORB::resolve_initial_references.
The examples we show in the next few sections are based on having the thermostats in the CCS deliver an event whenever their temperature settings are modified. The following IDL struct is used to convey event data to all interested consumers.
module CCS {
struct TStatEvent { Thermostat ts; AssetType asset_num; LocType location; TempType temp;
}; // ...
};
Whenever any temperature setting of any thermostat in the CCS is modified, the Thermostat_impl servant generates an event. The event data in the TStatEvent structure consists of information concerning the affected Thermostat: its object reference, asset number, location, and new temperature setting.
20.6.1 Obtaining an EventChannel Reference
All examples shown in the following sections are assumed to obtain the event channel object reference as shown here.
int
main(int argc, char * argv[])
{
// Initialize the ORB.
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
811

IT-SC book: Advanced CORBA® Programming with C++
//Obtain a reference to the root NamingContext. CosNaming::NamingContext_var root_nc =
resolve_init<CosNaming::NamingContext>(orb, "NameService") ;
//Create the Name of the event channel binding.
CosNaming::Name ec_name; ec_name.length(1);
ec_name[0].id = CORBA::string_dup("event_channel");
//Resolve the binding to the event channel object reference. CosEventChannelAdmin::EventChannel_var channel =
resolve_name<CosEventChannelAdmin::EventChannel>( root_nc, ec_name
);
//...
}
This program segment (which for brevity contains no error handling) first initializes the ORB and uses it to obtain a NamingContext object reference. To get the initial NamingContext, we use the resolve_init template function introduced in Section 18.14.1. It creates a name, which it then passes to the resolve_name helper template function (also from Section 18.14.1). The resolve_name function looks up the Naming Service binding for the EventChannel object reference and narrows the result to the EventChannel interface. The resulting channel object reference variable is used in the following implementation examples.
20.6.2 Implementing a Push Supplier
To send an event whenever the temperature setting of a Thermostat is changed, we augment our implementation of the Thermostat::set_nominal operation.
CCS::TempType Thermostat_impl::
set_nominal(CCS::TempType new_temp) throw(CORBA::SystemException, CCS::Thermostat::BadTemp)
{
//Check that the new temperature is within range, and if
//so, set the desired temperature to new_temp (not shown).
//Create our event data.
CCS::TStatEvent event_data;
event_data.ts |
= _this(); |
event_data.asset_num |
= m_anum; |
event_data.location |
= location(); |
event_data.temp |
= new_temp; |
//Insert the event data into an any. CORBA::Any any;
any <= event_data;
//Push the event to the event channel. Assume that
//the "consumer" variable is a reference to our
812

IT-SC book: Advanced CORBA® Programming with C++
// ProxyPushConsumer obtained from the event channel. consumer->push(any);
return new_temp;
}
Thermostat_impl::set_nominal first performs whatever actions it normally takes to configure the target thermostat with its new settings (we do not show this code because it does not pertain to event delivery). We then create an instance of the TStatEvent structure and initialize its members. Its thermostat member is intended to refer to the target object of the request being processed, so we initialize it with the result of invoking the _this function to obtain the object reference of the target object. We initialize the asset_num and location data members using the m_anum data member and the device access helper functions (see Section 10.4) and set the temp member to the new temperature setting passed into this method.
After the TStatEvent structure is initialized, we insert it into a CORBA::Any. Finally, we invoke push on the consumer object reference, passing the event any data to it. This pushes the event into the event channel, which then ensures that all consumers receive it.
To register our push supplier with the event channel, we first obtain a SupplierAdmin reference from the event channel.
//Assume the "channel" variable refers to our event channel. CosEventChannelAdmin::SupplierAdmin_var supplier_admin =
channel->for_suppliers();
//Obtain a ProxyPushConsumer from the SupplierAdmin. CosEventChannelAdmin::ProxyPushConsumer_var consumer =
supplier_admin->obtain_push_consumer();
//Invoke the connect_push_supplier operation, passing
//a nil PushSupplier reference to it. CosEventComm::PushSupplier_var nil_supplier =
CosEventComm::PushSupplier::_nil(); consumer->connect_push_supplier(nil_supplier);
We then obtain a reference to a ProxyPushConsumer on our event channel by invoking obtain_push_consumer on the supplier_admin object reference. We have decided that we do not want to be explicitly notified of having our supplier disconnected from the event channel, so we register ourselves as a supplier by invoking connect_push_supplier on the consumer object reference, passing it a nil PushSupplier object reference. If we instead had wanted to be notified of being disconnected, we would have had to create an object supporting the PushSupplier interface and pass its reference to connect_push_supplier.
813

IT-SC book: Advanced CORBA® Programming with C++
Note that in our example, the push supplier is a servant, but it is not a servant for any objects supporting the Event Service interfaces. Indeed, a push supplier need not be a servant nor even a C++ object; any function, or even straight-line code in your program's main function, can push an event. The only time an object reference needs to be provided by a push supplier is when it wants to be called back if a disconnection occurs. In that case, the application must support an instance of a PushSupplier object.
20.6.3 Implementing a Push Consumer
A push consumer must support the CosEventComm::PushConsumer interface. Following is the definition for our push consumer servant.
class PushConsumer_impl :
public virtual POA_CosEventComm::PushConsumer { public:
PushConsumer_impl(CORBA::ORB_ptr orb);
virtual void disconnect_push_consumer() throw(CORBA::SystemException);
virtual void push(const CORBA::Any & any) throw(
CORBA::SystemException,
CosEventComm::Disconnected
);
private:
CORBA::ORB_var m_orb;
// copy and assignment not supported PushConsumer_impl(const PushConsumer_impl &); void operator=(const PushConsumer_impl &);
};
The constructor requires an object reference for the ORB, which it duplicates and stores in a data member. It is used by the disconnect_push_consumer method implementation.
PushConsumer_impl::
PushConsumer_impl( CORBA::ORB_ptr orb
) : m_orb(CORBA::ORB::_duplicate(orb))
{
// Intentionally empty
}
void PushConsumer_impl::
disconnect_push_consumer() throw(CORBA::SystemException)
{
CORBA::Object_var obj =
814

IT-SC book: Advanced CORBA® Programming with C++
m_orb->resolve_initial_references("POACurrent"); PortableServer::Current_var current =
PortableServer::Current::_narrow(obj); PortableServer::POA_var poa = current->get_POA(); PortableServer::ObjectId_var oid = current->get_object_id(); poa->deactivate_object(oid);
}
void PushConsumer_impl::
push(const CORBA::Any & any) throw(CORBA::SystemException, CosEventComm::Disconnected)
{
// Attempt to extract event data as a TStatEventstruct. CCS::TStatEvent * event_data;
if (any >>= event_data) {
// Use values from the event data struct here (not shown).
}
}
Our implementation of disconnect_push_consumer deactivates the target object, effectively destroying the target object. We assume that the POA with which the object is registered has the RETAIN and USE_ACTIVE_OBJECT_MAP_ONLY policies. This means that our object will not be mistakenly reincarnated by a servant manager.
The push method first attempts to extract a TStatEvent struct from the CORBA::Any it receives as an argument. If the extraction succeeds, our consumer can use the event data as it sees fit. For example, depending on the function of the consumer application, it might write it to some kind of log, display it on a screen, or check it to make sure that the new thermostat settings are within an acceptable range.
Note that an attempt to extract the TStatEvent struct from the CORBA::Any argument may fail. Extraction failure can occur if a supplier other than our Thermostat_impl supplier is also connected to the same event channel and is providing event data of a different type to the channel. You should never write a push consumer that assumes that the event data being sent to it is of the type that it expects to receive; always check the Boolean result of every CORBA::Any extraction operation.
To connect our push consumer, we use our servant to create a PushConsumer object and then register it with the event channel.
//Create a new PushConsumer object. Assume the "orb"
//variable is an already-initialized reference to the ORB. PushConsumer_impl servant(orb); CosEventComm::PushConsumer_var my_consumer = servant._this();
//Assume the "channel" variable refers to our event channel. CosEventChannelAdmin::ConsumerAdmin_var consumer_admin =
channel->for_consumers();
//Obtain a ProxyPushSupplier from the ConsumerAdmin. CosEventChannelAdmin::ProxyPushSupplier_var supplier =
815

IT-SC book: Advanced CORBA® Programming with C++
consumer_admin->obtain_push_supplier();
//Invoke the connect_push_consumer operation, passing
//our PushConsumer reference to it.
supplier->connect_push_consumer(my_consumer);
To keep things simple, our example registers the push consumer as a transient object under the Root POA using the _this function for implicit activation. Naturally, your application may need to use a different POA with different policies for your consumer objects.
20.6.4 Implementing a Pull Supplier
A pull supplier must support the CosEventComm::PullSupplier interface. The implementation of a pull supplier is similar to that of a push consumer because both must be implemented as CORBA objects. Following is the definition for our pull supplier.
class PullSupplier_impl :
public virtual POA_CosEventComm::PullSupplier { public:
PullSupplier_impl(CORBA::ORB_ptr orb);
// IDL method functions. virtual void
disconnect_pull_supplier() throw(CORBA::SystemException);
virtual CORBA::Any * pull() throw(
CORBA::SystemException, CosEventComm::Disconnected
);
virtual CORBA::Any *
try_pull(CORBA::Boolean_out has_event) throw( CORBA::SystemException, CosEventComm::Disconnected
|
); |
|
// C++ helper function. |
|
|
void |
thermostat_changed( |
|
|
CCS::Thermostat_ptr ts, |
|
|
CCS::AssetType |
asset_num, |
|
const char * |
location, |
|
CCS::TempType |
temp |
|
); |
|
private:
Queue<CCS::TStatEvent *> m_queue; CORBA::ORB_var m_orb;
// copy and assignment not supported PullSupplier_impl(const PullSupplier_impl &); void operator=(const PullSupplier_impl &);
};
816

IT-SC book: Advanced CORBA® Programming with C++
The PullSupplier_impl constructor takes a reference to the ORB, duplicates it, and stores the duplicated reference in the m_orb data member. The ORB reference is used by the disconnect_pull_consumer method.
Our implementation of disconnect_pull_consumer deactivates the target object, effectively destroying the target object. We assume that the POA with which the object is registered has the RETAIN and USE_ACTIVE_OBJECT_MAP_ONLY policies. This means that our object will not be mistakenly reincarnated by a servant manager.
Implementing pull and try_pull is somewhat tricky because of event buffering considerations. Because there are no standard requirements as to the frequency with which pull consumers will invoke pull or try_pull, a pull supplier must be prepared to store events until they are specifically requested. Available storage resources determine the limits for the number of events that a pull supplier can store before it must start discarding unpulled event data. When discarding events, pull suppliers also must decide which events can be discarded and which ones should be kept. Deciding how to keep the most meaningful events while discarding others depends heavily on both the application and the values of the event data.
Our pull supplier example makes four simplifying assumptions.
Our PullSupplier_impl servant is collocated with our Thermostat_impl servant. This allows the Thermostat_impl servant to signal changes in thermostat settings directly to the PullSupplier_impl servant by invoking its thermostat_changed C++ member function. These invocations are not CORBA operation invocations but instead are ordinary C++ function calls.
Our PullSupplier_impl servant buffers events using a hypothetical thread-safe C++ Queue template class (not shown) that has the same interface as the STL queue type. However, unlike the STL queue, our thread-safe Queue allows us to safely push data into one end and to perform safe, blocking pulls of the data from the other end. The Queue also provides a non-blocking thread-safe pull operation.
We do not implement an algorithm to decide whether and when events should be discarded. In other words, the queue is allowed to grow without bound.
We assume that our POA has the ORB_CTRL_MODEL value for the PortableServer::ThreadingPolicy, thus allowing it to concurrently service requests on multiple objects.
The constructor and disconnection operation are exactly the same as for the PushConsumer_impl servant shown in Section 20.6.3. The constructor duplicates and stores a reference to the ORB hosting the PullSupplier object. The disconnect_pull_supplier operation fetches the POACurrent object from the
817

IT-SC book: Advanced CORBA® Programming with C++
ORB and uses it to get the POA and ObjectId of the target object, which it then deactivates.
PullSupplier_impl::
PullSupplier_impl( CORBA::ORB_ptr orb
) : m_orb(CORBA::ORB::_duplicate(orb))
{
// Intentionally empty
}
void PullSupplier_impl::
disconnect_pull_supplier() throw(CORBA::SystemException)
{
CORBA::Object_var obj = m_orb->resolve_initial_references("POACurrent");
PortableServer::Current_var current = PortableServer::Current::_narrow(obj);
PortableServer::POA_var poa = current->get_POA(); PortableServer::ObjectId_var oid = current->get_object_id(); poa->deactivate_object(oid);
}
CORBA::Any * PullSupplier_impl:: pull()
throw(CORBA::SystemException, CosEventComm::Disconnected)
{
//For our Queue, the front() call blocks until a data item
//exists at the front of the queue.
CCS::TStatEvent * event_data = m_queue.front(); m_queue.pop();
CORBA::Any_var any = new CORBA::Any; any <= *event_data;
delete event_data;
return any._retn();
}
CORBA::Any * PullSupplier_impl::
try_pull(CORBA::Boolean & has_event) throw(CORBA::SystemException, CosEventComm::Disconnected)
{
CORBA::Any_var any = new CORBA::Any; CCS::TStatEvent * event_data;
has_event = m_queue.try_pop(event_data);
if (has_event) {
any <= *event_data; delete event_data;
}
818

IT-SC book: Advanced CORBA® Programming with C++
return any._retn();
}
Both the pull and the try_pull methods access the event queue. Invoking front on the queue returns immediately if there are event data already present; otherwise, it blocks waiting for event data to be pushed into the queue. Because pull blocks if no event data are available, it performs the blocking by simply invoking the blocking front function on the queue. However, try_pull must not block if no event data are available. It therefore uses the non-blocking try_pop function to try to retrieve an event from the queue. If the queue is not empty, the try_pop function sets its argument to point to the popped event data and returns true; otherwise, it returns false. If try_pop returns true, then has_event is true, and the try_pull method inserts the popped event into the CORBA::Any return value and returns. Otherwise, has_event is set to reflect the empty queue, so a CORBA::Any containing no value is returned.
Finally, the set_nominal method of the Thermostat_impl servant class pushes an event onto the PullSupplier_impl servant by invoking its thermostat_changed member function. Following is the modified implementation of set_nominal.
CCS::TempType Thermostat_impl::
set_nominal(CCS::TempType new_temp) throw(CORBA::SystemException, CCS::Thermostat::BadTemp)
{
//Set the desired temperature to new_temp (not shown).
//Push our event data into the PullSupplier_impl servant.
//Assume m_servant points to the PullSupplier_impl instance. CCS::Thermostat_var ts = _this();
CORBA::String_var loc = location(); m_servant->thermostat_changed(ts, m_anum, loc, new_temp); return new_temp;
}
void PullSupplier_impl:: thermostat_changed(
CCS::Thermostat_ptr ts,
CCS::AssetType |
asset_num, |
const char * |
location, |
CCS::TempType |
temp |
) |
|
{ |
|
CCS::TStatEvent * event_data = new CCS::TStatEvent; |
|
event_data->ts |
= CCS::Thermostat::_duplicate(ts); |
event_data->asset_num = asset_num; |
|
event_data->location |
= location; |
event_data->temp |
= temp; |
m_queue.push(event_data);
819

IT-SC book: Advanced CORBA® Programming with C++
}
The thermostat_changed function heap-allocates a TStatEvent data structure, fills its fields with the arguments passed to it from Thermostat_impl::set_nominal, and pushes the event data into the queue.
As this example shows, dealing with the need to buffer events in a pull supplier can be complicated. Even though we simplify things by using a thread-safe Queue class to hold unpulled events, this pull supplier example is more complicated than any of the other supplier and consumer examples.
20.6.5 Implementing a Pull Consumer
Like a push supplier, a pull consumer need not be implemented as a CORBA object. Any ordinary C++ class or function can pull events from an event channel. Therefore, if we want to have our thermostat monitoring application retrieve events by pulling, we can implement the functionality as part of our windowing event loop. The following example shows a simplified event loop that repeatedly checks for thermostat events and for GUI events. We assume that neither the check_for_thermostat_event function nor the check_for_gui_event function enters a busy loop or blocks for any considerable amount of time.
//Assume the "channel" variable refers to our event channel. CosEventChannelAdmin::ConsumerAdmin_var consumer_admin =
channel->for_consumers();
//Obtain a ProxyPullSupplier from the ConsumerAdmin and connect. CosEventChannelAdmin::ProxyPullSupplier_var supplier =
consumer_admin->obtain_pull_supplier(); supplier->connect_pull_consumer(
CosEventComm::PullConsumer::_nil()
);
bool done; // Now enter our GUI event loop. do {
check_for_thermostat_event(supplier); done = check_for_gui_event();
} while (!done);
First, we call for_consumers on our event channel, which returns a
ConsumerAdmin_ptr that we use to invoke the obtain_pull_supplier method. We store the object reference returned from obtain_pull_supplier in the supplier variable, which we pass to our event polling function. To keep the example simple, we use a C-style function to implement the check_for_thermostat_event helper function, which performs event polling.
void check_for_thermostat_event(
CosEventComm::PullSupplier_ptr supplier
820