Pguide CPP
Pguide CPP
Actional, Actional (and design), Allegrix, Allegrix (and design), Apama, Apama (and Design), Artix, Business
Empowerment, DataDirect (and design), DataDirect Connect, DataDirect Connect64, DataDirect Technologies,
DataDirect XML Converters, DataDirect XQuery, DataXtend, Dynamic Routing Architecture, EdgeXtend,
Empowerment Center, Fathom, IntelliStream, IONA, IONA (and design), Mindreef, Neon, Neon New Era of
Networks, ObjectStore, OpenEdge, Orbix, PeerDirect, Persistence, POSSENET, Powered by Progress, PowerTier,
Progress, Progress DataXtend, Progress Dynamics, Progress Business Empowerment, Progress Empowerment
Center, Progress Empowerment Program, Progress OpenEdge, Progress Profiles, Progress Results, Progress
Software Developers Network, Progress Sonic, ProVision, PS Select, SequeLink, Shadow, SOAPscope,
SOAPStation, Sonic, Sonic ESB, SonicMQ, Sonic Orchestration Server, Sonic Software (and design),
SonicSynergy, SpeedScript, Stylus Studio, Technical Empowerment, WebSpeed, Xcalia (and design), and Your
Software, Our Technology-Experience the Connection are registered trademarks of Progress Software
Corporation or one of its affiliates or subsidiaries in the U.S. and/or other countries. AccelEvent, Apama
Dashboard Studio, Apama Event Manager, Apama Event Modeler, Apama Event Store, Apama Risk Firewall,
AppsAlive, AppServer, ASPen, ASP-in-a-Box, BusinessEdge, Cache-Forward, DataDirect Spy, DataDirect
SupportLink, FUSE, FUSE Mediation Router, FUSE Message Broker, FUSE Services Framework, Future Proof,
GVAC, High Performance Integration, ObjectStore Inspector, ObjectStore Performance Expert, OpenAccess,
Orbacus, Pantero, POSSE, ProDataSet, Progress ESP Event Manager, Progress ESP Event Modeler, Progress
Event Engine, Progress RFID, PSE Pro, SectorAlliance, SeeThinkAct, Shadow z/Services, Shadow z/Direct,
Shadow z/Events, Shadow z/Presentation, Shadow Studio, SmartBrowser, SmartComponent,
SmartDataBrowser, SmartDataObjects, SmartDataView, SmartDialog, SmartFolder, SmartFrame, SmartObjects,
SmartPanel, SmartQuery, SmartViewer, SmartWindow, Sonic Business Integration Suite, Sonic Process Manager,
Sonic Collaboration Server, Sonic Continuous Availability Architecture, Sonic Database Service, Sonic
Workbench, Sonic XML Server, StormGlass, The Brains Behind BAM, WebClient, Who Makes Progress, and Your
World. Your SOA. are trademarks or service marks of Progress Software Corporation or one of its affiliates or
subsidiaries in the U.S. and other countries. Java and all Java-based marks are trademarks or registered
trademarks of Sun Microsystems, Inc. in the U.S. and other countries. Any other trademarks contained herein
are the property of their respective owners.
Third Party Acknowledgments:
1. The Product incorporates IBM-ICU 2.6 (LIC-255) technology from IBM. Such technology is subject to the
following terms and conditions: Copyright (c) 1995-2009 International Business Machines Corporation and
others. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the
Software, and to permit persons to whom the Software is furnished to do so, provided that the above copyright
notice(s) and this permission notice appear in all copies of the Software and that both the above copyright
notice(s) and this permission notice appear in supporting documentation.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to
promote the sale, use or other dealings in this Software without prior written authorization of the copyright
holder. All trademarks and registered trademarks mentioned herein are the property of their respective owners.
2. The Product incorporates IDL Compiler Front End Technology from Sun Microsystems, Inc. Such technology
is subject to the following terms and conditions: Copyright 1992, 1993, 1994 Sun Microsystems, Inc. Printed in
the United States of America. All Rights Reserved. This product is protected by copyright and distributed under
the following license restricting its use. The Interface Definition Language Compiler Front End (CFE) is made
available for your use provided that you include this license and copyright notice on all media and
documentation and the software program in which this product is incorporated in whole or part. You may copy
and extend functionality (but may not remove functionality) of the Interface Definition Language CFE without
charge, but you are not authorized to license or distribute it to anyone else except as part of a product or
program developed by you or with the express written consent of Sun Microsystems, Inc. ("Sun"). The names of
Sun Microsystems, Inc. and any of its subsidiaries or affiliates may not be used in advertising or publicity
pertaining to distribution of Interface Definition Language CFE as permitted herein. This license is effective until
terminated by Sun for failure to comply with this license. Upon termination, you shall destroy or return all code
and documentation for the Interface Definition Language CFE. The Interface Definition Language CFE may not be
exported outside of the United States without first obtaining the appropriate government approvals.
INTERFACE DEFINITION LANGUAGE CFE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE,
NONINFRINGEMENT, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. INTERFACE
DEFINITION LANGUAGE CFE IS PROVIDED WITH NO SUPPORT AND WITHOUT ANY OBLIGATION ON THE PART OF
SUN OR ANY OF ITS SUBSIDIARIES OR AFFILIATES TO ASSIST IN ITS USE, CORRECTION, MODIFICATION OR
ENHANCEMENT. SUN OR ANY OF ITS SUBSIDIARIES OR AFFILIATES SHALL HAVE NO LIABILITY WITH RESPECT TO
THE INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY INTERFACE DEFINITION LANGUAGE
CFE OR ANY PART THEREOF. IN NO EVENT WILL SUN OR ANY OF ITS SUBSIDIARIES OR AFFILIATES BE LIABLE
FOR ANY LOST REVENUE OR PROFITS OR OTHER SPECIAL, INDIRECT AND CONSEQUENTIAL DAMAGES, EVEN IF
SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
Use, duplication, or disclosure by the government is subject to restrictions as set forth in subparagraph (c)(1)(ii)
of the Rights in Technical Data and Computer Software clause at DFARS 252.227-7013 and FAR 52.227-19.
Sun, Sun Microsystems and the Sun logo are trademarks or registered trademarks of Sun Microsystems, Inc.
SunSoft, Inc. 2550 Garcia Avenue Mountain View, California 94043. NOTE: SunOS, SunSoft, Sun, Solaris, Sun
Microsystems or the Sun logo are trademarks or registered trademarks of Sun Microsystems, Inc.
List of Tables 21
Preface 23
5
CONTENTS
6
CONTENTS
7
CONTENTS
8
CONTENTS
9
CONTENTS
10
CONTENTS
11
CONTENTS
12
CONTENTS
storagetype 626
storagehome 628
Factory Native Types 630
13
CONTENTS
14
CONTENTS
Index 847
15
CONTENTS
16
List of Figures
Figure 1: The nature of abstract CORBA objects 30
Figure 2: The object request broker 32
Figure 3: Invoking on a CORBA object 33
Figure 4: The portable object adapter 34
Figure 5: Client makes a single operation call on a server 49
Figure 6: Simple strategy for passing object references to clients 64
Figure 7: Multiple inheritance of IDL interfaces 121
Figure 8: Reference count for Account proxy is set to one. 206
Figure 9: Reference for Account proxy is incremented to 2. 207
Figure 10: Multiple _ptr references to a proxy object can leave the reference count unchanged.213
Figure 11: The server-side ORB conveys client requests to the POA via its manager, and the POA
dispatches the request to the appropriate servant. 264
Figure 12: A servant class can inherit base class implementations. 290
Figure 13: A servant class can implement operations of all base skeleton classes. 291
Figure 14: Inheritance options among servant and base skeleton classes. 292
Figure 15: A portable object adapter (POA) maps abstract objects to their concrete implementations
(servants) 299
Figure 16: On the first request on an object, the servant activator returns a servant to the POA, which
establishes the mapping in its active object map. 343
Figure 17: The POA directs each object request to the servant locator, which returns a servant to the
POA to process the request. 349
Figure 18: Reply handler implementation 369
Figure 19: The C++ mapping arranges exceptions into a hierarchy 375
Figure 20: Interfaces that derive from the DynAny interface 423
Figure 21: Hierarchy of interface repository objects 474
Figure 22: A naming graph is a hierarchy of naming contexts 501
Figure 23: Checking context bound to initial naming context 512
17
LIST OF FIGURES
Figure 24: Savings and Loans naming contexts bound to initial naming context 512
Figure 25: Binding an object reference to a naming context 515
Figure 26: Destroying a naming context and removing related bindings 527
Figure 27: A naming graph that spans multiple servers 529
Figure 28: Multiple naming graphs are linked by binding initial naming contexts of several servers to a
root server. 531
Figure 29: The root server’s initial naming context is bound to the initial naming contexts of other
servers, allowing clients to locate the root naming context. 532
Figure 30: Associating a name with an object group 538
Figure 31: Architecture of the stock market example 544
Figure 32: A server process uses sessions to establish a logical connection with a datastore and its
contents 572
Figure 33: Transactional session states 587
Figure 34: No Delegation Required for Ordinary Read Operation 598
Figure 35: Delegation Required for Transactional Read Operation 599
Figure 36: Delegation Required for Write Operation 600
Figure 37: Suppliers and consumers communicating through an event channel 632
Figure 38: Event propagation in a CORBA system 633
Figure 39: Push model of event transfer 634
Figure 40: Pull Model suppliers and consumers communicating through an event channel 635
Figure 41: Push suppliers and pull consumers communicating through an event channel 636
Figure 42: Push consumers pushing typed events to typed push consumers 637
Figure 43: Client interceptors allow services to access outgoing requests and incoming replies.673
Figure 44: PICurrent facilitates transfer of thread context data to a request or reply. 675
Figure 45: Client interceptors process a normal reply. 691
Figure 46: Client interceptors process a LOCATION_FORWARD reply. 692
Figure 47: send_request throws an exception in a client-side interceptor 693
Figure 48: Client interceptors can change the nature of the reply. 694
Figure 49: Server interceptors receive request and send exception thrown by target object. 705
18
LIST OF FIGURES
19
LIST OF FIGURES
20
List of Tables
Table 1: CORBA::LocalObject pseudo-operation returns 126
Table 2: Built-in IDL types 131
Table 3: Extended built-in IDL types 133
Table 4: Component specifier arguments to cpp_poa_genie.tcl 152
Table 5: Optional switches to cpp_poa_genie.tcl 152
Table 6: Wildcard pattern matching to interface names 156
Table 7: Arguments that control servant generation 158
Table 8: Options affecting the server 162
Table 9: Parameter passing for low-level mapping 244
Table 10: Parameter passing with _var types 245
Table 11: Timeout Policies 250
Table 12: POA policy factories and argument options 304
Table 13: POA manager states and interface operations 325
Table 14: Reply Handler Operation Types for Normal Replies 367
Table 15: Reply Handler Operation Types for Exceptional Replies 367
Table 16: Base minor code values for Orbix subsystems 384
Table 17: Type Codes and Parameters 395
Table 18: Type-Specific Operations 400
Table 19: Information Obtained by Type-Specific Operations 402
Table 20: Interface Repository OIbject Types 471
Table 21: Container and Contained Objects in the Interface Repository 479
Table 22: SessionManager parameters 578
Table 23: ParameterList settings for a TransactionalSession 586
Table 24: Isolation levels 590
Table 25: PSDL Reference Mappings 621
Table 26: Mapping for PSDL parameters 625
21
LIST OF TABLES
22
Preface
Orbix is a full implementation of the Common Object Request Broker
Architecture (CORBA), as specified by the Object Management Group. Orbix
complies with the following specifications:
• CORBA 2.3
• GIOP 1.2 (default), 1.1, and 1.0
Audience The CORBA Programmer’s Guide is intended to help you become familiar
with Orbix, and show how to develop distributed applications using Orbix
components. This guide assumes that you are familiar with programming in
C++.
This guide does not discuss every API in great detail, but gives a general
overview of the capabilities of the Orbix development kit and how various
components fit together.
Organization of this guide Read Chapter 1 for an overview of Orbix. Chapter 2 shows how you can use
code-generation genies to build a distributed application quickly and easily.
Chapter 3 describes in detail the basic steps in building client and server
programs. Subsequent chapters expand on those steps by focusing on topics
that are related to application development.
23
PREFACE
Additional resources The Knowledge Base contains helpful articles, written by experts, about
Orbix Mainframe, and other products:
http://www.iona.com/support/kb/
If you need help with Orbix Mainframe or any other products, contact
technical support:
http://www.progress.com/support
24
PREFACE
Keying conventions This guide may use the following keying conventions:
25
PREFACE
26
CHAPTER 1
Introduction to
Orbix
With Orbix, you can develop and deploy large-scale
enterprise-wide CORBA systems in C++ and Java. Orbix has
an advanced modular architecture that lets you configure and
change functionality without modifying your application code,
and a rich deployment architecture that lets you configure and
manage a complex distributed system.
27
CHAPTER 1 | Introduction to Orbix
Why CORBA?
Overview Today’s enterprises need flexible, open information systems. Most
enterprises must cope with a wide range of technologies, operating systems,
hardware platforms, and programming languages. Each of these is good at
some important business task; all of them must work together for the
business to function.
The common object request broker architecture—CORBA—provides the
foundation for flexible and open systems. It underlies some of the Internet’s
most successful e-business sites, and some of the world’s most complex and
demanding enterprise information systems.
What is CORBA? CORBA is an open, standard solution for distributed object systems. You can
use CORBA to describe your enterprise system in object-oriented terms,
regardless of the platforms and technologies used to implement its different
parts. CORBA objects communicate directly across a network using
standard protocols, regardless of the programming languages used to create
objects or the operating systems and platforms on which the objects run.
CORBA solutions are available for every common environment and are used
to integrate applications written in C, C++, Java, Ada, Smalltalk, and
COBOL, running on embedded systems, PCs, UNIX hosts, and mainframes.
CORBA objects running in these environments can cooperate seamlessly.
Through COMet, IONA’s dynamic bridge between CORBA and COM, they
can also interoperate with COM objects.
CORBA is widely available and offers an extensive infrastructure that
supports all the features required by distributed business objects. This
infrastructure includes important distributed services, such as transactions,
security, and messaging.
28
Why CORBA?
29
CHAPTER 1 | Introduction to Orbix
CORBA Objects
CORBA objects are abstract objects in a CORBA system that provide
distributed object capability between applications in a network. Figure 1
shows that any part of a CORBA system can refer to the abstract CORBA
object, but the object is only implemented in one place and time on some
server of the system.
A server implements
a CORBA object
Clients access
CORBA objects
via object
references
30
Why CORBA?
31
CHAPTER 1 | Introduction to Orbix
Server
O bject
Client
32
CORBA Application Basics
Server
O bject
Client
Client O bject
Stub Skeleton
Code Code
33
CHAPTER 1 | Introduction to Orbix
Servant
Client
Server
skeleton
34
Servers and the Portable Object Adapter
A server can have one or more nested POAs. Because each POA has its own
set of policies, you can group objects logically or functionally among
multiple POAs, where each POA is defined in a way that best
accommodates the needs of the objects that it processes.
35
CHAPTER 1 | Introduction to Orbix
Plug-ins A plug-in is a code library that can be loaded into an Orbix application at
runtime. A plug-in can contain any type of code; typically, it contains
objects that register themselves with the ORB runtimes to add functionality.
Plug-ins can be linked directly with an application, loaded when an
application starts up, or loaded on-demand while the application is running.
This gives you the flexibility to choose precisely those ORB features that you
actually need. Moreover, you can develop new features such as protocol
support for direct ATM or HTTPNG. Because ORB features are configured
into the application rather than compiled in, you can change your choices
as your needs change without rewriting or recompiling applications.
For example, an application that uses the standard IIOP protocol can be
reconfigured to use the secure SSL protocol simply by configuring a different
transport plug-in. No one transport is inherent to the ORB core; you simply
load the transport set that suits your application best. This architecture
makes it easy for Orbix to support additional transports in the future such as
multicast or special purpose network protocols.
ORB core The ORB core presents a uniform programming interface to the developer:
everything is a CORBA object. This means that everything appears to be a
local C++ or Java object within the process. In fact it might be a local
object, or a remote object reached by some network protocol. It is the ORB’s
job to get application requests to the right objects no matter where they live.
To do its job, the ORB loads a collection of plug-ins as specified by ORB
configuration settings—either on startup or on demand—as they are needed
by the application. For remote objects, the ORB intercepts local function
calls and turns them into CORBA requests that can be dispatched to a
remote object.
36
Orbix Plug-In Design
In order to send a request on its way, the ORB core sets up a chain of
interceptors to handle requests for each object. The ORB core neither
knows nor cares what these interceptors do, it simply passes the request
along the interceptor chain. The chain might be a single interceptor which
sends the request with the standard IIOP protocol, or a collection of
interceptors that add transaction information, encrypt the message and send
it on a secure protocol such as SSL. All of this is transparent to the
application, so you can change the protocol or services used by your
application simply by configuring a different set of interceptors.
37
CHAPTER 1 | Introduction to Orbix
Development Tools
The CORBA developer’s environment contains a number of facilities and
features that help you and your development team be more productive.
Code generation toolkit Orbix provides a code generation toolkit that simplifies and streamlines the
development effort. You only need to define your IDL interfaces; out-of-the
box scripts generate a complete client/server application automatically from
an IDL file.
The toolkit also can be useful for debugging: you can use an auto-generated
server to debug your client, and vice versa. Advanced users can write code
generation scripts to automate repetitive coding in a large application.
For more information about the code generation toolkit, refer to the CORBA
Code Generation Toolkit Guide.
Multi-threading support Orbix provides excellent support for multi-threaded applications. Orbix
libraries are multi-threaded and thread-safe. Orbix servers use standard POA
policies to enable multi-threading. The ORB creates a thread pool that
automatically grows or shrinks depending on demand load. Thread pool
size, growth and request queuing can be controlled by configuration settings
without any coding.
Of course, multi-threaded applications must themselves be thread-safe. This
usually means they need to use thread-synchronization objects such as
mutexes or semaphores. Although most platforms provide similar thread
synchronization facilities, the interfaces vary widely. Orbix includes an
object-oriented thread synchronization portability library which allows you to
write portable multi-threaded code.
38
Development Tools
Configuration and logging Applications can store their own configuration information in Orbix
interfaces configuration domains, taking advantage of the infrastructure for ORB
configuration. CORBA interfaces provide access to configuration information
in application code.
Applications can also take advantage of the Orbix logging subsystem, again
using CORBA interfaces to log diagnostic messages. These messages are
logged to log-stream objects that are registered with the ORB. Log streams
for local output, file logging and system logging (Unix syslogd or Windows
Event Service) are provided with Orbix. You can also implement your own
log streams, which capture ORB and application diagnostics and send them
to any destination you desire.
39
CHAPTER 1 | Introduction to Orbix
Location domains A location domain is a collection of servers under the control of a single
locator daemon. The locator daemon can manage servers on any number of
hosts across a network. The locator daemon automatically activates remote
servers through a stateless activator daemon that runs on the remote host.
The locator daemon also maintains the implementation repository, which is
a database of available servers. The implementation repository keeps track
of the servers available in a system and the hosts they run on. It also
provides a central forwarding point for client requests. By combining these
two functions, the locator lets you relocate servers from one host to another
without disrupting client request processing. The locator redirects requests
to the new location and transparently reconnects clients to the new server
instance. Moving a server does not require updates to the naming service,
trading service, or any other repository of object references.
The locator can monitor the state of health of servers and redirect clients in
the event of a failure, or spread client load by redirecting clients to one of a
group of servers.
40
Orbix Application Deployment
41
CHAPTER 1 | Introduction to Orbix
Full CORBA 2.3 support and All CORBA 2.3 IDL data types are fully supported, including:
interoperability • Extended precision numeric types for 64 bit integer and extended
floating point calculations.
• Fixed point decimals for financial calculations.
• International character sets, including support for code-set negotiation
where multiple character sets are available.
• Objects by value: you can define objects that are passed by value as
well as the traditional pass-by-reference semantics of normal CORBA
objects. This is particularly relevant in Java based systems, but also
supported for C++ using object factories.
Orbix supports the most recent 1.2 revision of the CORBA standard General
Inter-ORB Protocol (GIOP) and Internet Inter-ORB Protocol (IIOP), and also
supports previous 1.1 and 1.0 revisions for backwards compatibility with
applications based on other ORBs. Orbix is interoperable with any
CORBA-compliant application that uses the standard IIOP protocol.
Asynchronous messaging and Orbix implements two key parts of the CORBA messaging specification that
quality of service are included in CORBA 3.0.
• Asynchronous messaging interfaces allow easy, type-safe
asynchronous calls to normal CORBA operations. This means that
clients can make a request on a remote service, and then carry on with
other work until the reply is ready.
• ORB quality-of-service policies provide finer standardized control over
how the ORB processes requests. For example, you can specify how
quickly a client resumes processing after sending one-way requests.
42
CORBA Features and Services
Interoperable naming service and Orbix supports the interoperable naming service specification. This is a
load balancing extensions superset of the original CORBA naming service which adds some
ease-of-use features and provides a standard URL format for CORBA object
references to simplify configuration and administration of CORBA services.
The Orbix naming service also supports IONA-specific load-balancing
extensions of OrbixNames 3. A group of objects can be registered against a
single name; the naming service hands out references to clients so that the
client load is spread across the group.
Object transaction service Orbix includes the object transaction service (OTS) which is optimized for
the common case where only a single resource (database) is involved in a
transaction. Applications built against the single resource OTS can easily be
reconfigured to use a full-blown OTS when it is available, since the
interfaces are identical. With Orbix plug-in architecture, applications will not
even need to be recompiled. For the many applications where transactions
do not span multiple databases, the single-resource OTS will continue to be
a highly efficient solution, compared to a full OTS that performs extensive
logging to guarantee transaction integrity.
Event service Orbix supports the CORBA event service specification, which defines a
model for indirect communications between ORB applications. A client does
not directly invoke an operation on an object in a server. Instead, the client
sends an event that can be received by any number of objects. The sender of
an event is called a supplier; the receivers are called consumers. An
intermediary event channel takes care of forwarding events from suppliers
to consumers.
Orbix supports both the push and pull model of event transfer, as defined in
the CORBA event specification. Orbix performs event transfer using the
untyped format, whereby events are based on a standard operation call that
takes a generic parameter of type any.
SSL/TLS Orbix SSL/TLS provides data security for applications that communicate
across networks by ensuring authentication, privacy, and integrity features
for communications across TCP/IP connections.
43
CHAPTER 1 | Introduction to Orbix
Persistent state service Orbix includes the first implementation of the persistent state service (PSS).
PSS interposes a CORBA-based abstraction layer between a server and its
persistent storage. Orbix’s implementation of PSS is based on Berkeley DB,
an efficient embedded database that is included with Orbix. By adding new
PSS driver plug-ins, applications that use PSS can be reconfigured to store
their data in any database without code changes or recompilation.
Dynamic type support: interface Orbix has full support for handling data values that are not known at
repository and dynany compile time. The interface repository stores information about all CORBA
types known to the system and can be queried at runtime. Clients can
construct requests based on runtime type information using the dynamic
invocation interface (DII), and servers can implement “universal” objects
that can implement any interface at run time with the dynamic skeleton
interface (DSI).
Although all of these features have been available since early releases of the
CORBA specification, they are incomplete without the addition of the
DynAny interface. This interface allows clients and servers to interpret or
construct values based purely on runtime information, without any
compiled-in data types.
These features are ideal for building generic object browsers, type
repositories, or protocol gateways that map CORBA requests into another
object protocol.
44
Orbix C++ Development on z/OS
Note: Not all features mentioned in this guide are available on z/OS.
Native z/OS The material in this guide that relates to writing C++ applications is
relevant to C++ application development on native z/OS. However, there is
material in this guide that is not relevant to building and running on native
z/OS.
For details about building and running Orbix C++ applications in a native
z/OS environment, see the readme files and JCL that are supplied in your
Orbix Mainframe product installation as follows (where orbixhlq represents
your high-level qualifier):
• orbixhlq.DEMO.CPP.README
• orbixhlq.DEMO.CPP.BLD.JCLLIB
• orbixhlq.DEMO.CPP.RUN.JCLLIB
UNIX System Services The material in this guide that relates to writing C++ applications, and to
building and running them in a UNIX environment, is relevant to C++
application development in a UNIX System Services environment. However,
there is material in this guide that is not relevant to building and running on
Unix System Services.
For details about building and running Orbix C++ applications in a Unix
System Services environment, see the readme files supplied in your Orbix
Mainframe Unix System Services product installation.
45
CHAPTER 1 | Introduction to Orbix
46
CHAPTER 2
Getting Started
with Orbix
You can use the CORBA Code Generation Toolkit to develop
an Orbix application quickly.
Given a user-defined IDL interface, the toolkit generates the bulk of the
client and server application code, including makefiles. You then complete
the distributed application by filling in the missing business logic.
47
CHAPTER 2 | Getting Started with Orbix
Note: Orbix Mainframe does not support the code generation toolkit and
distributed genies. For information about building applications in a native
z/OS environment, see the readme files and JCL that are supplied in the
DEMO data sets of your Orbix Mainframe installation.
Setting the Domain The scripts that set the Orbix environment are associated with a particular
domain, which is the basic unit of Orbix configuration. Consult the
Installation Guide, and the Administrator’s Guide for further details on
configuring your environment.
To set the Orbix environment associated with the domain-name domain,
enter:
Windows
> config-dir\etc\bin\domain-name_env.bat
UNIX
% . config-dir/etc/bin/domain-name_env
config-dir is the root directory where the Appliation Server Platform stores
its configuration information. You specify this directory while configuring
your domain. domain-name is the name of a configuration domain.
48
Hello World Example
IDL Interface
The client and server applications communicate with each other using the
Internet Inter-ORB Protocol (IIOP), which sits on top of TCP/IP. When a
client invokes a remote operation, a request message is sent from the client
to the server. When the operation returns, a reply message containing its
return values is sent back to the client. This completes a single remote
CORBA invocation.
All interaction between the client and server is mediated via a set of IDL
declarations. The IDL for the Hello World! application is:
//IDL
interface Hello {
string getGreeting();
};
The IDL declares a single Hello interface, which exposes a single operation
getGreeting(). This declaration provides a language neutral interface to
CORBA objects of type Hello.
49
CHAPTER 2 | Getting Started with Orbix
50
Development from the Command Line
Define the IDL interface Create the IDL file for the Hello World! application. First of all, make a
directory to hold the example code:
Windows
UNIX
% mkdir -p OCGT/HelloExample
//IDL
interface Hello {
string getGreeting();
};
This interface mediates the interaction between the client and the server
halves of the distributed application.
51
CHAPTER 2 | Getting Started with Orbix
Generate starting point code Generate files for the server and client application using the CORBA Code
Generation Toolkit.
In the directory C:\OCGT\HelloExample (Windows) or OCGT/HelloExample
(UNIX) enter the following command:
This command logs the following output to the screen while it is generating
the files:
hello.idl:
cpp_poa_genie.tcl: creating it_servant_base_overrides.h
cpp_poa_genie.tcl: creating it_servant_base_overrides.cxx
cpp_poa_genie.tcl: creating HelloImpl.h
cpp_poa_genie.tcl: creating HelloImpl.cxx
cpp_poa_genie.tcl: creating server.cxx
cpp_poa_genie.tcl: creating client.cxx
cpp_poa_genie.tcl: creating call_funcs.h
cpp_poa_genie.tcl: creating call_funcs.cxx
cpp_poa_genie.tcl: creating it_print_funcs.h
cpp_poa_genie.tcl: creating it_print_funcs.cxx
cpp_poa_genie.tcl: creating it_random_funcs.h
cpp_poa_genie.tcl: creating it_random_funcs.cxx
cpp_poa_genie.tcl: creating Makefile
You can edit the following files to customize client and server applications:
Client:
client.cxx
Server:
server.cxx
HelloImpl.h
HelloImpl.cxx
52
Development from the Command Line
Complete the server program Complete the implementation class, HelloImpl, by providing the definition
of the HelloImpl::getGreeting() function . This C++ function provides
the concrete realization of the Hello::getGreeting() IDL operation.
Edit the HelloImpl.cxx file, and delete most of the generated boilerplate
code occupying the body of the HelloImpl::getGreeting() function.
Replace it with the line of code highlighted in bold font below:
//C++
//File ’HelloImpl.cxx’
...
char *
HelloImpl::getGreeting() throw(
CORBA::SystemException
)
{
char * _result;
return _result;
}
...
53
CHAPTER 2 | Getting Started with Orbix
Complete the client program Complete the implementation of the client main() function in the
client.cxx file. You must add a couple of lines of code to make a remote
invocation of the getGreeting() operation on the Hello object.
Edit the client.cxx file and search for the line where the
call_Hello_getGreeting() function is called. Delete this line and replace it
with the two lines of code highlighted in bold font below:
//C++
//File: ‘client.cxx’
...
if (CORBA::is_nil(Hello1))
{
cerr << "Could not narrow reference to interface "
<< "Hello" << endl;
}
else
{
CORBA::String_var strV = Hello1->getGreeting();
cout << "Greeting is: " << strV << endl;
}
...
Build the demonstration The Makefile generated by the code generation toolkit has a complete set of
rules for building both the client and server applications.
To build the client and server complete the following steps:
1. Open a command line window.
2. Go to the ../OCGT/HelloExample directory.
54
Development from the Command Line
3. Enter:
Windows
> nmake
UNIX
% make -e
start_domain-name_services
> domain-name_env
55
CHAPTER 2 | Getting Started with Orbix
stop_domain-name_services
The passing of the object reference from the server to the client in this way
is suitable only for simple demonstrations. Realistic server applications use
the CORBA naming service to export their object references instead (see
Chapter 18).
56
CHAPTER 3
First Application
This chapter uses a simple application to describe the basic
programming steps required to define CORBA objects, write
server programs that implement those objects, and write client
programs that access them. The programming steps are the
same whether the client and server run on a single host or are
distributed across a network.
57
CHAPTER 3 | First Application
58
Development Using Code Generation
First, you define a set of interfaces written in the OMG interface definition
language (IDL). The IDL forms the basis of development for both the client
and the server. The toolkit takes the IDL file as input and, based on the
declarations in the IDL file, generates a complete, working Orbix application.
You can then modify the generated code to add business logic to the
application.
WARNING: Orbix Mainframe does not support the code generation toolkit
and distributed genies. You must develop Orbix applications in a
mainframe environment without the code generation toolkit (see page 61).
59
CHAPTER 3 | First Application
60
Development Without Using Code Generation
First, you define a set of interfaces written in the OMG interface definition
language (IDL). The IDL file forms the basis of development for both the
client and the server.
61
CHAPTER 3 | First Application
62
Locating CORBA Objects
1 The server converts the object reference into a string (stringified object
reference) and writes this stringified object reference to a file.
2 The client reads the stringified object reference from the file and converts it
to a real object reference.
63
CHAPTER 3 | First Application
3 The client can now make remote invocations by invoking on the object
reference.
64
Development Steps
Development Steps
Overview You typically develop an Orbix application in the following steps:
1. Define IDL interfaces: Identify the objects required by the application
and define their public interfaces in IDL.
2. Generate starting point code: Use the code generation toolkit to
generate starting point code for the application. You can then edit the
generated files to add business logic.
3. Compile the IDL definitions: The compiler generates the C++ header
and source files that you need to implement client and server
programs.
4. Develop the server program: The server acts as a container for a variety
of CORBA objects, each of which supports one IDL interface. You must
add code to provide the business logic for each type of CORBA object.
The server makes its CORBA objects available to clients by exporting
object references to a well-known location.
5. Develop the client program: The client uses the IDL compiler-generated
mappings to invoke operations on the object references that it obtains
from the server.
6. Build the application.
7. Run the application.
65
CHAPTER 3 | First Application
//IDL
//File: ’building.idl’
interface Building {
1 readonly attribute string address;
66
Development Steps
67
CHAPTER 3 | First Application
The idlgen interpreter is an executable file that processes IDL files based
on the instructions contained in predefined code generation scripts.
A set of genies (code generation scripts) are supplied with the toolkit. Most
important of these is the cpp_poa_genie.tcl genie that is used to generate
starting point code for a C++ application.
Taking the building.idl IDL file as input, the cpp_poa_genie.tcl genie
can produce complete source code for a distributed application that includes
a client and a server program.
To generate starting point code, execute the following command:
idlgen cpp_poa_genie.tcl -all building.idl
This command generates all of the files you need for this application. The
-all flag selects a default set of genie options that are appropriate for
simple demonstration applications.
The main client file generated by the cpp_poa_genie.tcl genie is:
68
Development Steps
The following files are also generated and support a dummy implementation
of the client and server programs:
call_funcs.h
call_funcs.cxx
it_print_funcs.h
it_print_funcs.cxx
it_random_funcs.h
it_random_funcs.cxx
Dummy implementation of client The generated starting point code provides a complete dummy
and server programs implementation of the client and the server programs. The dummy
implementation provides:
• A server program that implements every IDL interface.
Every IDL operation is implemented with default code that prints the
in and inout parameters to the screen when it is invoked. Return
values, inout and out parameters are populated with randomly
generated values. At random intervals a CORBA user exception might
be thrown instead.
• A client program that calls every operation on every IDL interface once.
The dummy client and server programs can be built and run as they are.
Modifying dummy client and Later steps describe in detail how to modify the generated code to
server programs implement the business logic of the Building application.
In the code listings that follow, modifications are indicated as follows:
• Additions to the generated code are highlighted in bold font. You can
manually add this code to the generated files using a text editor.
• In some cases the highlighted additions replace existing generated
code, requiring you to manually delete the old code.
69
CHAPTER 3 | First Application
Output from IDL compilation The IDL compiler produces several C++ files when it compiles the
building.idl file. These files contain C++ definitions that correspond to
your IDL definitions. You should never modify this code.
The generated files can be divided into two categories:
• Client stub code is compiled and linked with client programs, so they
can make remote invocations on Building CORBA objects.
• Server skeleton code is compiled and linked with server programs, so
they can service invocations on Building CORBA objects.
70
Development Steps
Any clients that want to invoke on CORBA objects that support the Building
interface must include the header file building.hh and link with the stub
code buildingC.cxx.
The skeleton code is a superset of the stub code. The additional files contain
code that allows you to implement servants for the Building interface.
Server files include the buildingS.hh header file, which recursively includes
the file building.hh. The server must be linked with both buildingC.cxx
and buildingS.cxx.
IDL to C++ mapping The IDL compiler translates IDL into stub and skeleton code for a given
language—in this case, C++. As long as the client and server programs
comply with the definitions in the generated header files, building.hh and
buildingS.hh, the runtime ORB enables type-safe interaction between the
client and the server.
71
CHAPTER 3 | First Application
Both the client and the server source files include the generated header file
building.hh, which contains the C++ mappings for the Building interface
(see “Define IDL interfaces” on page 66):
72
Development Steps
parameters. For basic types, such as short and long, the _out type is
a typedef of a reference to the corresponding C++ type. For example,
the CORBA::Long_out type is defined in the CORBA namespace as:
typedef CORBA::Long& CORBA::Long_out;
Other helper data types and methods generated in this file are described
when they are used in this chapter.
73
CHAPTER 3 | First Application
Declare the servant class The code generation toolkit generates a header file, BuildingImpl.h, that
declares the BuildingImpl servant class. You can use this starting point
code to implement the Building interface.
Note: The name of the BuildingImpl servant class is not significant but
simply conforms to a naming convention that helps distinguish servant code
from other application code.
You can modify the generated code in BuildingImpl.h to add member
variables needed for the implementation. The code shown here provides a
simple implementation of BuildingImpl.
Manual additions to the generated code are shown in bold font.
// File: ’BuildingImpl.h’
...
1 #include "buildingS.hh"
#include "it_servant_base_overrides.h"
74
Development Steps
2 class BuildingImpl :
public virtual IT_ServantBaseOverrides,
public virtual POA_Building
{
public:
BuildingImpl(PortableServer::POA_ptr);
virtual ~BuildingImpl();
// IDL operations
//
3 virtual CORBA::Boolean available(
CORBA::Long date
) IT_THROW_DECL((CORBA::SystemException));
// IDL attributes
//
4 virtual char* address()
IT_THROW_DECL((CORBA::SystemException));
private:
5 //-----------------------
// Private Member Variables
//-----------------------
CORBA::Long m_confirmation_counter;
CORBA::Long m_reservation[366];
75
CHAPTER 3 | First Application
76
Development Steps
Define the servant class The code generation toolkit also generates the BuildingImpl.cxx file, which
contains an outline of the method definitions for the BuildingImpl servant
class. You should edit this file to fill in the bodies of methods that
correspond to the operations and attributes of the Building interface. It is
usually necessary to edit the constructor and destructor of the servant class
as well.
Manual additions made to the generated code are shown in bold font. In
some cases, the additions replace existing generated code requiring you to
manually delete the old code.
// File: ’BuildingImpl.cxx’
...
#include "BuildingImpl.h"
// _create() -- create a new servant.
POA_Building*
77
CHAPTER 3 | First Application
1 BuildingImpl::_create(PortableServer::POA_ptr the_poa)
{
return new BuildingImpl(the_poa);
}
// BuildingImpl constructor
//
// Note: since we use virtual inheritance, we must include an
// initialiser for all the virtual base class constructors that
// require arguments, even those that we inherit indirectly.
//
BuildingImpl::BuildingImpl(
PortableServer::POA_ptr the_poa
) :
IT_ServantBaseOverrides(the_poa),
2 m_address( "200 West Street, Waltham, MA." ),
m_confirmation_counter(1)
{
for (int i=0; i<366; i++) { m_reservation[i] = 0; }
}
// ~BuildingImpl destructor.
//
3 BuildingImpl::~BuildingImpl()
{
// Intentionally empty.
}
78
Development Steps
return 0;
}
79
CHAPTER 3 | First Application
80
Development Steps
Client main() The code in the client main() initializes the ORB, reads a Building object
reference from the file Building.ref and hands over control to
run_warehouse_menu(), which is described in the next section. When
run_warehouse_menu() returns, the generated code shuts down the ORB.
Changes or additions to the code are shown in bold font.
//File: ’client.cxx’
...
#include "building.hh"
...
// global_orb -- make ORB global so all code can find it.
//
CORBA::ORB_var
1 global_orb = CORBA::ORB::_nil();
81
CHAPTER 3 | First Application
2 read_reference(
const char* file
)
{
cout << "Reading stringified object reference from "
<< file << endl;
ifstream ifs(file);
CORBA::String_var str;
ifs >> str;
if (!ifs) {
cerr << "Error reading object reference from "
<< file << endl;
return CORBA::Object::_nil();
}
return global_orb->string_to_object(str);
}
...
82
Development Steps
6 run_warehouse_menu(Building1);
}
}
catch(CORBA::Exception &ex)
{
cerr << "Unexpected CORBA exception: " << ex << endl;
exit_status = 1;
}
7 global_orb->shutdown(1);
global_orb->destroy();
}
catch (...)
{
// Do nothing.
}
return exit_status;
}
83
CHAPTER 3 | First Application
Client business logic You access an object’s attributes and operations by calling the appropriate
Building class methods on the proxy object. The proxy object redirects the
C++ calls across the network to the appropriate servant method.
84
Development Steps
The following code uses the C++ arrow operator (->) on the Building_ptr
object warehouse to access Building class methods.
Additions to the code are shown in bold font.
//File: ’client.cxx’
void
run_warehouse_menu(Building_ptr warehouseP)
{
CORBA::String_var addressV = warehouseP->address();
cout << "The warehouse address is:" << endl
<< addressV.in() << endl;
CORBA::Long date;
CORBA::Long confirmation;
char quit = 'n';
do {
cout << "Enter day to reserve warehouse (1,2,...): ";
cin >> date;
if(warehouseP->available(date)) {
if (warehouseP->reserveDate(date, confirmation) )
cout << "Confirmation number: "
<< confirmation << endl;
else
cout << "Reservation attempt failed!" << endl;
}
else {
cout << "That date is unavailable." << endl;
}
cout << "Quit? (y,n)";
cin >> quit;
}
while (quit == 'n');
}
85
CHAPTER 3 | First Application
Windows
> nmake
UNIX
% make -e
86
Development Steps
87
CHAPTER 3 | First Application
88
Enhancing Server Functionality
89
CHAPTER 3 | First Application
int
main(int argc, char** argv)
{
IT_TerminationHandler
termination_handler(termination_handler_callback);
// ...
}
You can create only one termination handler object in a program. The server
shutdown mechanism and termination_handler_callback() are discussed
in detail in “Shut down the ORB” on page 100.
90
Enhancing Server Functionality
...
// global_orb -- make ORB global so all code can find it.
CORBA::ORB_var
1 global_orb = CORBA::ORB::_nil();
...
int
main(int argc, char **argv)
{
...
cout << "Initializing the ORB" << endl;
2 global_orb = CORBA::ORB_init(argc, argv);
...
91
CHAPTER 3 | First Application
Example 7:
try {
// For temporary object references.
CORBA::Object_var tmp_ref;
...
1 tmp_ref = global_orb->resolve_initial_references("RootPOA");
2 PortableServer::POA_var root_poa =
PortableServer::POA::_narrow(tmp_ref);
assert(!CORBA::is_nil(root_poa));
3 PortableServer::POAManager_var root_poa_manager
= root_poa->the_POAManager();
assert(!CORBA::is_nil(root_poa_manager));
92
Enhancing Server Functionality
PortableServer::POA_ptr
create_simple_poa(
const char* poa_name,
PortableServer::POA_ptr parent_poa,
PortableServer::POAManager_ptr poa_manager
)
{
// Create a policy list.
// Policies not set in the list get default values.
//
CORBA::PolicyList policies;
policies.length(1);
int i = 0;
// Make the POA single threaded.
//
policies[i++] = parent_poa->create_thread_policy(
PortableServer::SINGLE_THREAD_MODEL
);
assert(i==1);
return parent_poa->create_POA(
poa_name,
poa_manager,
policies);
}
93
CHAPTER 3 | First Application
94
Enhancing Server Functionality
#include <BuildingImpl.h>
...
// Note: PortableServer::Servant is a pointer type - it's
// actually a typedef for PortableServer::ServantBase*.
//
PortableServer::Servant the_Building = 0;
...
the_Building = BuildingImpl::_create(my_poa);
95
CHAPTER 3 | First Application
Example 8:
#include <BuildingImpl.h>
...
CORBA::Object_var tmp_ref;
...
PortableServer::ObjectId_var oid;
...
1 oid = my_poa->activate_object(the_Building);
2 tmp_ref = my_poa->id_to_reference(oid);
96
Enhancing Server Functionality
97
CHAPTER 3 | First Application
void
write_reference(
CORBA::Object_ptr ref, const char* objref_file
)
{
CORBA::String_var stringified_ref =
global_orb->object_to_string(ref);
cout << "Writing stringified object reference to "
<< objref_file << endl;
ofstream os(objref_file);
os << stringified_ref;
if (!os.good())
{
cerr << "Failed to write to " << objref_file << endl;
}
}
98
Enhancing Server Functionality
Example 9:
1 // Activate the POA Manager and let the ORB process requests.
//
root_poa_manager->activate();
2 global_orb->run();
99
CHAPTER 3 | First Application
Example 10:
static void
termination_handler_callback(
long signal
)
{
1 if (!CORBA::is_nil(orb))
{
2 global_orb->shutdown(IT_FALSE);
}
}
100
Enhancing Server Functionality
Example 11:
1 global_orb->run();
// Delete the servants.
2 delete the_Building;
101
CHAPTER 3 | First Application
#include "it_random_funcs.h"
#include <iostream.h>
#include <fstream.h>
#include <string.h>
#include <stdlib.h>
#include <it_ts/termination_handler.h>
#include <omg/PortableServer.hh>
#include "BuildingImpl.h"
102
Complete Source Code for server.cxx
static void
termination_handler_callback(
long signal
)
{
cout << "Processing shutdown signal " << signal << endl;
if (!CORBA::is_nil(orb))
{
cout << "ORB shutdown ... " << flush;
orb->shutdown(IT_FALSE);
cout << "done." << endl;
}
}
ofstream os(objref_file);
os << stringified_ref;
if (!os.good())
{
cerr << "Failed to write to " << objref_file << endl;
}
}
103
CHAPTER 3 | First Application
return parent_poa->create_POA(poa_name,
poa_manager,
policies);
}
104
Complete Source Code for server.cxx
try
{
// For temporary object references.
CORBA::Object_var tmp_ref;
105
CHAPTER 3 | First Application
106
CHAPTER 4
Defining Interfaces
The CORBA Interface Definition Language (IDL) is used to
describe interfaces of objects in an enterprise application. An
object’s interface describes that object to potential clients—
its attributes and operations, and their signatures.
An IDL-defined object can be implemented in any language that IDL maps
to, such as C++, Java, and COBOL. By encapsulating object interfaces
within a common language, IDL facilitates interaction between objects
regardless of their actual implementation. Writing object interfaces in IDL is
therefore central to achieving the CORBA goal of interoperability between
different languages and platforms.
CORBA defines standard mappings from IDL to several programming
languages, including C++, Java, and Smalltalk. Each IDL mapping
specifies how an IDL interface corresponds to a language-specific
implementation. Orbix’s IDL compiler uses these mappings to convert IDL
definitions to language-specific definitions that conform to the semantics of
that language.
This chapter describes IDL semantics and uses. For mapping information,
refer to language-specific mappings in the Object Management Group’s
latest CORBA specification.
107
CHAPTER 4 | Defining Interfaces
108
Modules and Name Scoping
module BankDemo
{
interface Bank {
//...
};
interface Account {
//...
};
};
Within the same module, interfaces can reference each other by name
alone. If an interface is referenced from outside its module, its name must
be fully scoped with the following syntax:
module-name::interface-name
For example, the fully scoped names of interfaces Bank and Account are
BankDemo::Bank and BankDemo::Account, respectively.
Nesting restrictions A module cannot be nested inside a module of the same name. Likewise,
you cannot directly nest an interface inside a module of the same name. To
avoid name ambiguity, you can provide an intervening name scope as
follows:
109
CHAPTER 4 | Defining Interfaces
module A
{
module B
{
interface A {
//...
};
};
};
110
Interfaces
Interfaces
Interfaces are the fundamental abstraction mechanism of CORBA. An
interface defines a type of object, including the operations that the object
supports in a distributed enterprise application.
An IDL interface generally describes an object’s behavior through operations
and attributes:
• Operations of an interface give clients access to an object’s behavior.
When a client invokes an operation on an object, it sends a message to
that object. The ORB transparently dispatches the call to the object,
whether it is in the same address space as the client, in another
address space on the same machine, or in an address space on a
remote machine.
• An IDL attribute is short-hand for a pair of operations that get and,
optionally, set values in an object.
For example, the Account interface in module BankDemo describes the
objects that implement bank accounts:
module BankDemo
{
typedef float CashAmount; // Type for representing cash
typedef string AccountId; // Type for representing account
ids
//...
interface Account {
readonly attribute AccountId account_id;
readonly attribute CashAmount balance;
void
withdraw(in CashAmount amount)
raises (InsufficientFunds);
void
deposit(in CashAmount amount);
};
};
111
CHAPTER 4 | Defining Interfaces
112
Interfaces
Interface Contents
An IDL interface can define the following components:
• Operations
• Attributes
• Exceptions
• Types
• Constants
Of these, operations and attributes must be defined within the scope of an
interface; all other components can be defined at a higher scope.
113
CHAPTER 4 | Defining Interfaces
Operations
IDL operations define the signatures of an object’s function, which client
invocations on that object must use. The signature of an IDL operation is
generally composed of three components:
• Return value data type
• Parameters and their direction
• Exception clause
A operation’s return value and parameters can use any data types that IDL
supports (see “Abstract Interfaces” on page 128).
For example, the Account interface defines two operations, withdraw() and
deposit(); it also defines the exception InsufficientFunds:
module BankDemo
{
typedef float CashAmount; // Type for representing cash
//...
interface Account {
exception InsufficientFunds {};
void
withdraw(in CashAmount amount)
raises (InsufficientFunds);
void
deposit(in CashAmount amount);
};
};
Parameter direction Each parameter specifies the direction in which its arguments are passed
between client and object. Parameter passing modes clarify operation
definitions and allow the IDL compiler to map operations accurately to a
target programming language. At runtime, Orbix uses parameter passing
modes to determine in which direction or directions it must marshal a
parameter.
114
Interfaces
in: The parameter is initialized only by the client and is passed to the object.
out: The parameter is initialized only by the object and returned to the
client.
inout: The parameter is initialized by the client and passed to the server; the
server can modify the value before returning it to the client.
In general, you should avoid using inout parameters. Because an inout
parameter automatically overwrites its initial value with a new value, its
usage assumes that the caller has no use for the parameter’s original value.
Thus, the caller must make a copy of the parameter in order to retain that
value. By using two parameters, in and out, the caller can decide for itself
when to discard the parameter.
One-way operations By default, IDL operations calls are synchronous—that is, a client invokes
an operation on an object and blocks until the invoked operation returns. If
an operation definition begins with the keyword oneway, a client that calls
the operation remains unblocked while the object processes the call.
Three constraints apply to a one-way operation:
• The return value must be set to void.
• Directions of all parameters must be set to in.
• No raises clause is allowed.
For example, interface Account might contain a one-way operation that
sends a notice to an Account object:
module BankDemo {
//...
interface Account {
oneway void notice(in string text);
//...
};
};
115
CHAPTER 4 | Defining Interfaces
116
Interfaces
Attributes
An interface’s attributes correspond to the variables that an object
implements. Attributes indicate which variables in an object are accessible
to clients.
Unqualified attributes map to a pair of get and set functions in the
implementation language, which let client applications read and write
attribute values. An attribute that is qualified with the keyword readonly
maps only to a get function.
For example, the Account interface defines two readonly attributes,
AccountId and balance. These attributes represent information about the
account that only the object implementation can set; clients are limited to
read-only access.
117
CHAPTER 4 | Defining Interfaces
Exceptions
IDL operations can raise one or more CORBA-defined system exceptions.
You can also define your own exceptions and explicitly specify these in an
IDL operation. An IDL exception is a data structure that can contain one or
more member fields, formatted as follows:
exception exception-name {
[member;]...
};
After you define an exception, you can specify it through a raises clause in
any operation that is defined within the same scope. A raises clause can
contain multiple comma-delimited exceptions:
Exceptions that are defined at module scope are accessible to all operations
within that module; exceptions that are defined at interface scope are
accessible only to operations within that interface.
For example, interface Account defines the exception InsufficientFunds
with a single member of data type string. This exception is available to any
operation within the interface. The following IDL defines the withdraw()
operation to raise this exception when the withdrawal fails:
module BankDemo
{
typedef float CashAmount; // Type for representing cash
//...
interface Account {
exception InsufficientFunds {};
void
withdraw(in CashAmount amount)
raises (InsufficientFunds);
//...
};
};
118
Interfaces
Empty Interfaces
IDL allows you to define empty interfaces. This can be useful when you wish
to model an abstract base interface that ties together a number of concrete
derived interfaces. For example, the CORBA PortableServer module
defines the abstract ServantManager interface, which serves to join the
interfaces for two servant manager types, servant activator and servant
locator:
module PortableServer
{
interface ServantManager {};
119
CHAPTER 4 | Defining Interfaces
module BankDemo{
typedef float CashAmount; // Type for representing cash
interface Account {
//...
};
120
Interfaces
Multiple inheritance The following IDL definition expands module BankDemo to include interface
PremiumAccount, which inherits from two interfaces, CheckingAccount and
SavingsAccount:
module BankDemo {
interface Account {
//...
};
interface PremiumAccount :
CheckingAccount, SavingsAccount {
//...
};
};
Account
CheckingAccount SavingsAccount
PremiumAccount
121
CHAPTER 4 | Defining Interfaces
Inheritance of the object interface All user-defined interfaces implicitly inherit the predefined interface Object.
Thus, all Object operations can be invoked on any user-defined interface.
You can also use Object as an attribute or parameter type to indicate that
any interface type is valid for the attribute or parameter. For example, the
following operation getAnyObject() serves as an all-purpose object locator:
interface ObjectLocator {
void getAnyObject (out Object obj);
};
Inheritance redefinition A derived interface can modify the definitions of constants, types, and
exceptions that it inherits from a base interface. All other components that
are inherited from a base interface cannot be changed. In the following
example, interface CheckingAccount modifies the definition of exception
InsufficientFunds, which it inherits from Account:
module BankDemo
{
typedef float CashAmount; // Type for representing cash
//...
interface Account {
exception InsufficientFunds {};
//...
};
interface CheckingAccount : Account {
exception InsufficientFunds {
CashAmount overdraftLimit;
};
};
//...
};
122
Interfaces
123
CHAPTER 4 | Defining Interfaces
module BankDemo
{
typedef float CashAmount; // Type for representing cash
typedef string AccountId; // Type for representing account
ids
Account
find_account(in AccountId account_id)
raises(AccountNotFound);
Account
create_account(
in AccountId account_id,
in CashAmount initial_balance
) raises (AccountAlreadyExists);
};
124
Interfaces
Local Interfaces
An interface declaration that contains the keyword local defines a local
interface. An interface declaration that omits this keyword can be referred to
as an unconstrained interface, to distinguish it from local interfaces. An
object that implements a local interface is a local object.
Local interfaces differ from unconstrained interfaces in the following ways:
• A local interface can inherit from any interface, whether local or
unconstrained. However, an unconstrained interface cannot inherit
from a local interface.
• Any non-interface type that uses a local interface is regarded as a local
type. For example, a struct that contains a local interface member is
regarded as a local struct, and is subject to the same localization
constraints as a local interface.
• Local types can be declared as parameters, attributes, return types, or
exceptions only in a local interface, or as state members of a valuetype.
• Local types cannot be marshaled, and references to local objects
cannot be converted to strings through ORB::object_to_string().
Attempts to do so throw CORBA::MARSHAL.
• Any operation that expects a reference to a remote object cannot be
invoked on a local object. For example, you cannot invoke any DII
operations or asynchronous methods on a local object; similarly, you
cannot invoke pseudo-object operations such as is_a() or
validate_connection(). Attempts to do so throw
CORBA::NO_IMPLEMENT.
• The ORB does not mediate any invocation on a local object. Thus,
local interface implementations are responsible for providing the
parameter copy semantics that a client expects.
• Instances of local objects that the OMG defines as supplied by ORB
products are exposed either directly or indirectly through
ORB::resolve_initial_references().
125
CHAPTER 4 | Defining Interfaces
is_a()
get_interface()
get_domain_managers()
get_policy()
get_client_policy()
set_policy_overrides()
get_policy_overrides()
validate_connection()
non_existent() False
126
Valuetypes
Valuetypes
Valuetypes enable programs to pass objects by value across a distributed
system. This type is especially useful for encapsulating lightweight data
such as linked lists, graphs, and dates.
Valuetypes can be seen as a cross between data types such as long and
string that can be passed by value over the wire as arguments to remote
invocations, and objects, which can only be passed by reference. When a
program supplies an object reference, the object remains in its original
location; subsequent invocations on that object from other address spaces
move across the network, rather than the object moving to the site of each
request.
Like an interface, a valuetype supports both operations and inheritance from
other valuetypes; it also can have data members. When a valuetype is
passed as an argument to a remote operation, the receiving address space
creates a copy it of it. The copied valuetype exists independently of the
original; operations that are invoked on one have no effect on the other.
Because a valuetype is always passed by value, its operations can only be
invoked locally. Unlike invocations on objects, valuetype invocations are
never passed over the wire to a remote valuetype.
Valuetype implementations necessarily vary, depending on the languages
used on sending and receiving ends of the transmission, and their respective
abilities to marshal and demarshal the valuetype’s operations. A receiving
process that is written in C++ must provide a class that implements
valuetype operations and a factory to create instances of that class. These
classes must be either compiled into the application, or made available
through a shared library. Conversely, Java applications can marshal enough
information on the sender, so the receiver can download the bytecodes for
the valuetype operation implementations.
127
CHAPTER 4 | Defining Interfaces
Abstract Interfaces
An application can use abstract interfaces to determine at runtime whether
an object is passed by reference or by value. For example, the following IDL
definitions specify that operation Example::display() accepts any
derivation of abstract interface Describable:
interface Example {
void display(in Describable someObject);
};
Given these definitions, you can define two derivations of abstract interface
Describable, valuetype Currency and interface Account:
128
Abstract Interfaces
129
CHAPTER 4 | Defining Interfaces
130
IDL Data Types
Built-in Types
Table 2 lists built-in IDL types.
Integer types IDL supports short and long integer types, both signed and unsigned. IDL
guarantees the range of these types. For example, an unsigned short can
hold values between 0-65535. Thus, an unsigned short value always maps
to a native type that has at least 16 bits. If the platform does not provide a
native 16-bit type, the next larger integer type is used.
Floating point types Types float and double follow IEEE specifications for single- and
double-precision floating point values, and on most platforms map to native
IEEE floating point types.
131
CHAPTER 4 | Defining Interfaces
char Type char can hold any value from the ISO Latin-1 character set. Code
positions 0-127 are identical to ASCII. Code positions 128-255 are
reserved for special characters in various European languages, such as
accented vowels.
String types Type string can hold any character from the ISO Latin-1 character set
except NUL. IDL prohibits embedded NUL characters in strings. Unbounded
string lengths are generally constrained only by memory limitations. A
bounded string, such as string<10>, can hold only the number of
characters specified by the bounds, excluding the terminating NUL character.
Thus, a string<6> can contain the six-character string cheese.
The declaration statement can optionally specify the string’s maximum
length, thereby determining whether the string is bounded or unbounded:
string[<length>] name
For example, the following code declares data type ShortString, which is a
bounded string whose maximum length is 10 characters:
octet Octet types are guaranteed not to undergo any conversions in transit. This
lets you safely transmit binary data between different address spaces. Avoid
using type char for binary data, inasmuch as characters might be subject to
translation during transmission. For example, if client that uses ASCII sends
a string to a server that uses EBCDIC, the sender and receiver are liable to
have different binary values for the string’s characters.
any Type any allows specification of values that express any IDL type, which is
determined at runtime. An any logically contains a TypeCode and a value
that is described by the TypeCode. For more information about the any data
type, see Chapter 15 on page 407.
132
IDL Data Types
long long The 64-bit integer types long long and unsigned long long support
numbers that are too large for 32-bit integers. Platform support varies. If
you compile IDL that contains one of these types on a platform that does not
support it, the compiler issues an error.
long double Like 64-bit integer types, platform support varies for long double, so usage
can yield IDL compiler errors.
wchar Type wchar encodes wide characters from any character set. The size of a
wchar is platform-dependent.
133
CHAPTER 4 | Defining Interfaces
Scaling options
If scale is a positive integer, it specifies where to place the decimal point
relative to the rightmost digit. For example the following code declares fixed
data type CashAmount to have a digit size of 8 and a scale of 2:
Given this typedef, any variable of type CashAmount can contain values of up
to (+/-)99999999.99.
If scale is negative, the decimal point moves to the right scale digits,
thereby adding trailing zeros to the fixed data type’s value. For example, the
following code declares fixed data type bigNum to have a digit size of 3 and a
scale of -4:
module Circle {
const fixed pi = 3.142857;
};
134
IDL Data Types
For example, the decimal value 0.1 cannot be represented exactly in IEEE
format. Over a series of computations with floating-point values, the
cumulative effect of this imprecision can eventually yield inaccurate results.
Type fixed is especially useful in calculations that cannot tolerate any
imprecision, such as computations of monetary values.
135
CHAPTER 4 | Defining Interfaces
enum An enum (enumerated) type lets you assign identifiers to the members of a
set of values. For example, you can modify the BankDemo IDL with enum
type balanceCurrency:
module BankDemo {
enum Currency {pound, dollar, yen, franc};
interface Account {
readonly attribute CashAmount balance;
readonly attribute Currency balanceCurrency;
//...
};
};
136
IDL Data Types
struct A struct data type lets you package a set of named members of various
types. In the following example, struct CustomerDetails has several
members. Operation getCustomerDetails() returns a struct of type
CustomerDetails that contains customer data:
module BankDemo{
struct CustomerDetails {
string custID;
string lname;
string fname;
short age;
//...
};
interface Bank {
CustomerDetails getCustomerDetails
(in string custID);
//...
};
};
union A union data type lets you define a structure that can contain only one of
several alternative members at any given time. A union saves space in
memory, as the amount of storage required for a union is the amount
necessary to store its largest member.
You declare a union type with the following syntax:
137
CHAPTER 4 | Defining Interfaces
enum dateStorage
{ numeric, strMMDDYY, strDDMMYY };
struct DateStructure {
short Day;
short Month;
short Year;
};
138
IDL Data Types
arrays IDL supports multi-dimensional fixed-size arrays of any IDL data type, with
the following syntax:
sequence IDL supports sequences of any IDL data type with the following syntax:
139
CHAPTER 4 | Defining Interfaces
struct LimitedAccounts {
string bankSortCode<10>;
sequence<Account, 50> accounts; // max sequence length is 50
};
struct UnlimitedAccounts {
string bankSortCode<10>;
sequence<Account> accounts; // no max sequence length
};
140
IDL Data Types
CORBA::NamedValue
CORBA::TypeCode
To use these types in an IDL specification, include the file orb.idl in the
IDL file as follows:
#include <orb.idl>
//...
This statement tells the IDL compiler to allow types NamedValue and
TypeCode.
141
CHAPTER 4 | Defining Interfaces
module BankDemo {
interface Account {
//...
};
142
Constants
Constants
IDL lets you define constants of all built-in types except type any. To define
a constant’s value, you can either use another constant (or constant
expression) or a literal. You can use a constant wherever a literal is
permitted.
The following constant types are supported:
• Integer
• Floating-point
• Character and string
• Wide character and string
• Boolean
• Octet
• Fixed-point
• Enumeration
143
CHAPTER 4 | Defining Interfaces
Character and string Character constants use the same escape sequences as C++:
Wide character and string Wide character and string constants use C++ syntax. Use Universal
character codes to represent arbitrary characters. For example:
Note: IDL files themselves always use the ISO Latin-1 code set, they
cannot use Unicode or other extended character sets.
Boolean Boolean constants use the keywords FALSE and TRUE. Their use is
unnecessary, inasmuch as they create needless aliases:
144
Constants
Note: Octet constants were added with CORBA 2.3, so ORBs that are not
compliant with this specification might not support them.
Fixed-point For fixed-point constants, you do not explicitly specify the digits and scale.
Instead, they are inferred from the initializer. The initializer must end in d or
D. For example:
Note: Enumeration constants were added with CORBA 2.3, so ORBs that
are not compliant with this specification might not support them.
145
CHAPTER 4 | Defining Interfaces
Constant Expressions
IDL provides a number of arithmetic and bitwise operators.
Operator precedence The precedence for operators follows the rules for C++. You can override
the default precedence by adding parentheses.
Arithmetic operators The arithmetic operators have the usual meaning and apply to integral,
floating-point, and fixed-point types (except for %, which requires integral
operands). However, these operators do not support mixed-mode
arithmetic; you cannot, for example, add an integral value to a floating-point
value. The following code contains several examples:
// 5% discount
const fixed DISCOUNT = 0.05D;
const fixed PRICE = 99.99D;
Expressions are evaluated using the type promotion rules of C++. The
result is coerced back into the target type. The behavior for overflow is
undefined, so do not rely on it. Fixed-point expressions are evaluated
internally with 62 bits of precision, and results are truncated to 31 digits.
146
Constant Expressions
Bitwise operators The bitwise operators only apply to integral types. The right-hand operand
must be in the range 0–63. Note that the right-shift operator >> is
guaranteed to inject zeros on the left, whether the left-hand operand is
signed or unsigned:
147
CHAPTER 4 | Defining Interfaces
148
CHAPTER 5
Developing
Applications with
Genies
The code generation toolkit is packaged with several genies
that can help your development effort get off to a fast start.
Two genies generate code that you can use immediately for application
development:
• cpp_poa_genie.tcl reads IDL code and generates C++ source files
that you can compile into a working application.
• cpp_poa_op.tcl generates the C++ signatures of specified operations
and attributes and writes them to a file. You can use this genie on new
or changed interfaces, then update existing source code with the
generated signatures.
Note: Orbix Mainframe does not support the code generation toolkit and
distributed genies.
149
CHAPTER 5 | Developing Applications with Genies
150
Starting Development Projects
151
CHAPTER 5 | Developing Applications with Genies
Genie Syntax
cpp_poa_genie.tcl uses the following syntax:
idlgen cpp_poa_genie.tcl component-spec [options] idl-file
You must specify an IDL file. You must also specify the application
components to generate, either all components at once, or individual
components, with one of the arguments in Table 4:
Component Output
specifier
Each component specifier can take its own arguments. For more information
on these, refer to the discussion on each component later in this chapter.
You can also supply one or more of the optional switches shown in Table 5:
Option Description
152
Starting Development Projects
Option Description
153
CHAPTER 5 | Developing Applications with Genies
bankdemo.idl:
idlgen: creating BankDemo_BankImpl.h
idlgen: creating BankDemo_BankImpl.cxx
idlgen: creating BankDemo_AccountImpl.h
idlgen: creating BankDemo_AccountImpl.cxx
idlgen: creating server.cxx
idlgen: creating client.cxx
idlgen: creating call_funcs.h
idlgen: creating call_funcs.cxx
idlgen: creating it_print_funcs.h
idlgen: creating it_print_funcs.cxx
idlgen: creating it_random_funcs.h
idlgen: creating it_random_funcs.cxx
idlgen: creating Makefile
bankdemo.idl:
idlgen: creating BankDemo_BankImpl.h
idlgen: creating BankDemo_BankImpl.cxx
idlgen: creating BankDemo_AccountImpl.h
idlgen: creating BankDemo_AccountImpl.cxx
idlgen: creating it_print_funcs.h
idlgen: creating it_print_funcs.cxx
idlgen: creating it_random_funcs.h
idlgen: creating it_random_funcs.cxx
154
Starting Development Projects
155
CHAPTER 5 | Developing Applications with Genies
Selecting Interfaces
By default, cpp_poa_genie.tcl generates code for all interfaces in the
specified IDL file. You can specify to generate code for specific interfaces
within the file by supplying their fully scoped names. For example, the
following command specifies to generate code for the Bank interface in
bankdemo.idl:
> idlgen cpp_poa_genie.tcl -all BankDemo::Bank bankdemo.idl
You can also use wildcard patterns to specify the interfaces to process. For
example, the following command generates code for all interfaces in module
BankDemo:
> idlgen cpp_poa_genie.tcl BankDemo::* bankdemo.idl
The following command generates code for all interfaces in foo.idl with
names that begin with Foo or end with Bar.
> idlgen cpp_poa_genie.tcl foo.idl "Foo*" "*Bar"
Note: For interfaces defined inside modules, the wildcard is matched
against the fully scoped interface name, so Foo* matches FooModule::Y but
not BarModule::Foo.
Pattern matching is performed according to the rules of the TCL string
match command, which is similar to Unix or Windows filename matching.
Table 6 contains some common wildcard patterns:
* Any string
[xyz] x, y, or z.
156
Starting Development Projects
Including Files
By default, cpp_poa_genie.tcl generates code only for the specified IDL
files. You can specify also to generate code for all #include files by
supplying the -include option. For example, the following command
specifies to generate code from bankdemo.idl and any IDL files that are
included in it:
> idlgen cpp_poa_genie.tcl -all -include bankdemo.idl
The default for this option is set in the configuration file through
default.cpp_poa_genie.want_include.
157
CHAPTER 5 | Developing Applications with Genies
Implementing Servants
The -servant option generates POA servant classes that implement IDL
interfaces. For example, this command generates a class header and
implementation code for each interface that appears in IDL file
bankdemo.idl:
idlgen cpp_poa_genie.tcl -servant bankdemo.idl
The genie constructs the implementation class name from the scoped name
of the interface, replacing double colons (::) with an underscore (_) and
adding a suffix—by default, Impl.. The default suffix is set in the
configuration file through default.cpp.impl_class_suffix.
For example, BankDemo::Account is implemented by class
BankDemo_AccountImpl. The generated implementation class contains these
components:
• A static _create() member method to create a servant.
• A member method to implement each IDL operation for the interface.
The -servant option can take one or more arguments, shown in Table 7,
that let you control how servant classes are generated:
Argument Purpose
158
Starting Development Projects
Argument Purpose
-tie/-notie A POA servant is either an instance of a class that inherits from a POA
skeleton, or an instance of a tie template class that delegates to a separate
implementation class. You can choose the desired approach by supplying
-tie or -notie options. The default for this option is set in the configuration
file through default.cpp_poa_genie.want_tie.
With -notie, the genie generates servants that inherit directly from POA
skeletons. For example:
class BankDemo_AccountImpl : public virtual POA_BankDemo::Account
The _create() method constructs a servant as follows:
POA_BankDemo::Account*
BankDemo_AccountImpl::_create(PortableServer::POA_ptr the_poa)
{
return new BankDemo_AccountImpl(the_poa);
}
159
CHAPTER 5 | Developing Applications with Genies
With -tie, the genie generates implementation classes that do not inherit
from POA skeletons. The following example uses a _create method to
create an implementation object (1), and a tie (2) that delegates to it:
POA_BankDemo::Account*
BankDemo_AccountImpl::_create(PortableServer::POA_ptr the_poa)
{
1 BankDemo_AccountImpl* tied_object =
new BankDemo_AccountImpl();
2 POA_BankDemo::Account* the_tie =
new POA_BankDemo_Account_tie<BankDemo_AccountImpl>(
tied_object,
the_poa
);
return the_tie;
}
-inherit/-noinherit IDL servant implementation classes typically have the same inheritance
hierarchy as the interfaces that they implement, but this is not required.
• -inherit generates implementation classes with the same inheritance
as the corresponding interfaces.
• -noinherit generates implementation classes that do not inherit from
each other. Instead, each implementation class independently
implements all operations for its IDL interface, including operations
that are inherited from other IDL interfaces.
The default for this option is set in the configuration file through
default.cpp_poa_genie.want_inherit.
-default_poa In the standard CORBA C++ mapping, each servant class provides a
_this() method, which generates an object reference and implicitly
activates that object with the servant. Implicit activation calls
_default_POA() on the same servant to determine the POA in which this
object is activated. Unless you specify otherwise, _default_POA() returns
the root POA, which is typically not the POA where you want to activate
objects.
160
Starting Development Projects
class BankDemo_AccountImpl
: public virtual POA_BankDemo::Account,
public virtual PortableServer::RefCountServantBase
Note: -refcount is invalid with -tie. The genie issues a warning if you
combine these options. Tie templates as defined in the POA standard do not
support reference counting, and the genie cannot change their inheritance.
It is recommended that you do not use the tie approach for multi-threaded
servers.
161
CHAPTER 5 | Developing Applications with Genies
162
Starting Development Projects
-threads/-nothreads You can specify the threads policy for all POAs in the server with one of
these options:
-threads sets the ORB_CTRL_MODEL policy on all POAs in the server, allowing
the ORB to dispatch incoming calls in multiple threads concurrently.
The default for this option is set in the configuration file through
default.cpp_poa_genie.want_threads.
-strategy Options The POA is a flexible tool that lets servers manage objects with different
strategies. Some servers can use a combination of strategies for different
objects. You can use the genie to generate examples of each strategy, then
cut-and-paste the appropriate generated code into your own server.
You set a server’s object management strategy through one of the following
arguments to the -strategy option:
163
CHAPTER 5 | Developing Applications with Genies
-strategy activator: The server creates a POA and a servant activator (see
“Servant Activators” on page 343). For each interface, the server exports an
object reference. The object remains inactive until a client first calls on its
reference; then, the servant activator is invoked and creates the appropriate
servant, which remains in memory to handle future calls on that reference.
The servant activator deletes the servants when the POA is destroyed.
This strategy lets the server start receiving requests immediately and defer
creation of servants until they are needed. It is useful for servers that
normally activate just a few objects out of a large collection on each run, or
for servants that take a long time to initialize.
-strategy locator: The server creates a POA and a servant locator (see
“Servant Locators” on page 349). The server exports references, but all
objects are initially inactive. For every incoming operation, the POA asks the
servant locator to select an appropriate servant. The generated servant
locator creates a servant for each incoming operation, and deletes it when
the operation is complete.
A servant locator is ideal for managing a cache of servants from a very large
collection of objects in a database. You can replace the preinvoke and
postinvoke methods in the generated locator with code that looks for
servants in a database cache, loads them into the cache if required, and
deletes old servants when the cache is full.
-strategy default_servant: The server creates a POA for each interface, and
defines a default servant for each POA to handle incoming requests. A server
that manages requests for many objects that all use the same interface
should probably have a POA that maps all these requests to the same
default servant. For more information about using default servants, see
“Setting a Default Servant” on page 357.
164
Starting Development Projects
-ns/-nons Determines how the server exports object references to the application:
-ns: Use the naming service to publish object references. For each interface,
the server binds a reference that uses the interface name, in naming context
IT_GenieDemo. For example, for interface Demo_Bank, the genie binds the
reference IT_GenieDemo/BankDemo_Bank. If you use this option, the naming
service and locator daemon must be running when you start the server.
For more information about the naming service, see Chapter 18 on
page 499.
-nons: Write stringified object references to a file. For each interface, the
server exports a reference to a file named after the interface with the suffix
ref—for example BankDemo_Bank.ref
The default for this option is set in the configuration file through
default.cpp_poa_genie.
165
CHAPTER 5 | Developing Applications with Genies
Implementing a Client
The -client option generates client source code in client.cxx. For
example:
> idlgen cpp_poa_genie.tcl -client bank.idl
When you run this client, it performs the following actions for each
interface:
1. Reads an object reference from the file generated by the server—for
example, BankDemo_Bank.ref.
2. If generated with the -complete option, for each operation:
♦ Calls the operation and passes random values.
♦ Prints out the results.
3. Catches raised exceptions and prints an appropriate message.
166
Starting Development Projects
Generating a Makefile
The -makefile option generates a makefile that can build the server and
client applications. The makefile provides the following targets
• all: Compile and link the client and server.
• clean: Delete files created during compile and link.
• clean_all: Like clean, it also deletes all the source files generated by
idlgen, including the makefile itself.
To build the client and server, enter nmake (Windows) or make (UNIX).
167
CHAPTER 5 | Developing Applications with Genies
168
Starting Development Projects
The sections that follow describe, for each application component, the
differences between complete and incomplete code generation. All examples
assume the following IDL for interface Account:
// IDL:
module BankDemo
{
// Other interfaces and type definitions omitted...
interface Account
{
exception InsufficientFunds {};
readonly attribute AccountId account_id;
readonly attribute CashAmount balance;
void withdraw(
in CashAmount amount
) raises (InsufficientFunds);
void
deposit(
in CashAmount amount
);
};
}
Servant code Qualifying the -servant option with -incomplete or -complete yields the
required source files for each IDL interface. Either option generate the
following files for interface Account:
BankDemo_AccountImpl.h
BankDemo_AccountImpl.cxx
Incomplete servant
The -incomplete option specifies to generate servant class
BankDemo_AccountImpl, which implements the BankDemo::Account
interface. The implementation of each operation and attribute throws a
CORBA::NO_IMPLEMENT exception.
For example, the following code is generated for the deposit() operation:
void
BankDemo_AccountImpl::deposit(
BankDemo::CashAmount amount
) throw(
CORBA::SystemException
)
{
169
CHAPTER 5 | Developing Applications with Genies
throw CORBA::NO_IMPLEMENT();
}
All essential elements of IDL code are automatically generated, so you can
focus on writing the application logic for each IDL operation.
Complete servant
The -complete option specifies to generate several files that provide the
functionality required to generate random values for parameter passing, and
to print those values:
it_print_funcs.h
it_print_funcs.cxx
it_random_funcs.h
it_random_funcs.cxx
Member methods are fully implemented to print parameter values and, if
required, return a value to the client. For example, the following code is
generated for the deposit() operation:
void
BankDemo_AccountImpl::deposit(
BankDemo::CashAmount amount
) throw(
CORBA::SystemException
)
{
// Diagnostics: print the values of "in" and "inout"
parameters
cout << "BankDemo_AccountImpl::deposit(): "
<< "called with..."
<< endl;
cout << "\tamount = ";
IT_print_BankDemo_CashAmount(cout, amount, 3);
cout << endl;
// Diagnostics.
cout << "BankDemo_AccountImpl::deposit(): returning"
<< endl;
170
Starting Development Projects
they send, and those that are received back as out parameters. Utility
methods to assign random values to IDL types are generated in the file
it_random_funcs.cxx, and utility methods to print the values of IDL types
are generated in the file it_print_funcs.cxx.
An incomplete client contains no invocations.
Both complete and incomplete clients catch raised exceptions and print
appropriate messages.
171
CHAPTER 5 | Developing Applications with Genies
General Options
You can supply switches that control cpp_poa_genie.tcl genie output:
172
Starting Development Projects
173
CHAPTER 5 | Developing Applications with Genies
bankdemo.idl:
idlgen: creating ops.txt
Generating signatures for BankDemo::Account::close
174
Configuration Settings
Configuration Settings
The configuration file idlgen.cfg contains default settings for the C++
genie cpp_poa_genie.tcl at the scope default.cpp_poa_genie.
Some other settings are not specific to cpp_poa_genie.tcl but are used by
the std/cpp_poa_boa_lib.tcl library, which maps IDL constructs to their
C++ equivalents. cpp_poa_genie.tcl uses this library extensively, so these
settings affect the output that it generates. They are held in the scope
default.cpp.
For a full listing of these settings, refer to the CORBA Code Generation
Toolkit Guide.
175
CHAPTER 5 | Developing Applications with Genies
176
CHAPTER 6
ORB Initialization
and Shutdown
The mechanisms for initializing and shutting down the ORB
on a client and a server are the same.
Overview The main() of both sever and client must perform these steps:
• Initialize the ORB by calling CORBA::ORB_init().
• Shut down and destroy the ORB, by calling shutdown() and destroy()
on the ORB.
Orbix also provides its own IT_TerminationHandler class, which enables
applications to handle delivery of Ctrl-C and similar events in a portable
manner. For more information, see “Termination Handler” on page 294.
177
CHAPTER 6 | ORB Initialization and Shutdown
Calling within main() It is common practice to set a global variable with the ORB reference, so the
ORB object is accessible to most parts of the code. However, you should
call ORB_init() only after you call main() to ensure access to command line
arguments. ORB_init() scans its arguments parameter for command-line
options that start with -ORB and removes them. The arguments that remain
can be assumed to be application-specific.
Supplying an ORB name You can supply an ORB name as an argument; this name determines the
configuration information that the ORB uses. If you supply null, Orbix uses
the ORB identifier as the default ORB name. ORB names and configuration
are discussed in the Application Server Platform Administrator’s Guide.
namespace CORBA {
// ...
ORB_ptr ORB_init(
int & argc,
char ** aaccv,
const char * orb_identifier = ""
);
// ...
}
178
Initializing the ORB Runtime
Registering portable interceptors During ORB initialization, portable interceptors are instantiated and
registered through an ORB initializer. The client and server applications
must register the ORB initializer before calling ORB_init(). For more
information, see “Registering Portable Interceptors” on page 715.
179
CHAPTER 6 | ORB Initialization and Shutdown
180
Shutting Down the ORB
// C++
int main(int argc, char* argv[])
{
CORBA::ORB_var orb;
try
{
// ORB initialization not shown
...
...
1 // SHUTDOWN
orb->shutdown(1);
2 orb->destroy();
return 0;
}
catch (const CORBA::Exception& e)
{
cout << "Exception occurred: " << e << endl;
return 1;
}
}
181
CHAPTER 6 | ORB Initialization and Shutdown
182
Shutting Down the ORB
Using a signal handler Example 14 illustrates shutting down a CORBA server using a signal
handler:
// C++
CORBA::ORB_var global_orb;
Note: Pay attention to the value of the flag passed to shutdown(). You
can easily cause deadlock in a server by calling shutdown(1) which forces
shutdown() to block until the ORB shutdown is complete. In a server,
shutdown(0), which returns immediately, is the appropriate form.
183
CHAPTER 6 | ORB Initialization and Shutdown
184
CHAPTER 7
Using Policies
Orbix supports a number of CORBA and proprietary policies
that control the behavior of application components.
Most policies are locality-constrained; that is, they apply only to the server
or client on which they are set. Therefore, policies can generally be divided
into server-side and client-side policies:
• Server-side policies generally apply to the processing of requests on
object implementations. Server-side policies can be set
programmatically and in the configuration, and applied to the server’s
ORB and its POAs.
• client-side policies apply to invocations that are made from the client
process on an object reference. Client-side policies can be set
programmatically and in the configuration, and applied to the client’s
ORB, to a thread, and to an object reference.
The procedure for setting policies programmatically is the same for both
client and server:
1. Create the CORBA::Policy object for the desired policy.
2. Add the Policy object to a PolicyList.
3. Apply the PolicyList to the appropriate target—ORB, POA, thread, or
object reference.
In this chapter This chapter discusses issues that are common to all client and server
policies.
185
CHAPTER 7 | Using Policies
For detailed information about specific policies, refer to the chapters that
cover client and POA development: “Developing a Client” on page 201, and
“Managing Server Objects” on page 297.
186
Creating Policy and PolicyList Objects
Using POA policy factories The PortableServer::POA interface provides factories for creating
CORBA::Policy objects that apply only to a POA (see Table 12 on
page 304). For example, the following code uses POA factories to create
policy objects that specify PERSISTENT and USER_ID policies for a POA, and
adds these policies to a PolicyList.
CORBA::PolicyList policies;
policies.length (2);
Orbix also provides several proprietary policies to control POA behavior (see
page 187). These policies require you to call create_policy() on the ORB
to create Policy objects, as described in the next section.
Calling create_policy() You call create_policy() on the ORB to create Policy objects. For
example, the following code creates a PolicyList that sets a SyncScope
policy of SYNC_WITH_SERVER; you can then use this PolicyList to set client
policy overrides at the ORB, thread, or object scope:
187
CHAPTER 7 | Using Policies
#include <omg/messaging.hh>;
// ...
CORBA::PolicyList policies(1);
policies.length(1);
CORBA::Any policy_value;
policy_any <<= Messaging::SYNC_WITH_SERVER;
policies[0] = orb->create_policy(
Messaging::SYNC_SCOPE_POLICY_TYPE, policy_value );
188
Setting Orb and Thread Policies
module CORBA {
// ...
enum SetOverrideType
{
SET_OVERRIDE,
ADD_OVERRIDE
};
exception InvalidPolicies
{
sequence<unsigned short> indices;
};
189
CHAPTER 7 | Using Policies
interface PolicyManager {
PolicyList
get_policy_overrides( in PolicyTypeSeq ts );
void
set_policy_overrides(
in PolicyList policies,
in SetOverrideType set_add
) raises (InvalidPolicies);
};
190
Setting Orb and Thread Policies
191
CHAPTER 7 | Using Policies
192
Setting Server-Side Policies
193
CHAPTER 7 | Using Policies
194
Setting Client Policies
195
CHAPTER 7 | Using Policies
interface Object {
// ...
Policy
get_client_policy(in PolicyType type);
Policy
get_policy(in PolicyType type);
PolicyList
get_policy_overrides( in PolicyTypeSeq ts );
Object
set_policy_overrides(
in PolicyList policies,
in SetOverrideType set_add
) raises (InvalidPolicies);
boolean
validate_connection(out PolicyList inconsistent_policies);
};
196
Setting Client Policies
197
CHAPTER 7 | Using Policies
198
Getting Policies
Getting Policies
As shown earlier, CORBA::PolicyManager, CORBA::PolicyCurrent, and
CORBA::Object each provide operations that allow programmatic access to
the effective policies for an ORB, thread, and object. Accessor operations
obtain a PolicyList for the given scope. After you get a PolicyList, you
can iterate over its Policy objects. Each Policy object has an accessor
method that identifies its PolicyType. You can then use the Policy object’s
PolicyType to narrow to the appropriate type-specific Policy derivation—
for example, a SyncScopePolicy object. Each derived object provides its
own accessor method that obtains the policy in effect for that scope.
The Messaging module provides these PolicyType definitions:
module Messaging
{
// Messaging Quality of Service
// PolicyType constants
199
CHAPTER 7 | Using Policies
For example, the following code gets the ORB’s SyncScope policy:
#include <omg/messaging.hh>
...
// get reference to PolicyManager
CORBA::Object_var object;
object = orb->resolve_initial_references("ORBPolicyManager");
// narrow
CORBA::PolicyManager_var policy_mgr =
CORBA::PolicyManager::_narrow(object);
200
CHAPTER 8
Developing a
Client
A CORBA client initializes the ORB runtime, handles object
references, invokes operations on objects, and handles
exceptions that these operations throw.
Client Policies
201
CHAPTER 8 | Developing a Client
module BankDemo
{
typedef float CashAmount;
exception InsufficientFunds {};
// ...
interface Account{
void withdraw( in CashAmount amount )
raises (InsufficientFunds);
202
Mapping IDL Interfaces to Proxies
Given this IDL, the IDL compiler generates the following proxy class
definition for the client implementation:
namespace BankDemo
{
typedef CORBA::Float CashAmount;
// ...
This proxy class demonstrates several characteristics that are true of all
proxy classes:
• Member methods derive their names from the corresponding interface
operations—in this case, withdrawal().
• The proxy class inherits from CORBA::Object, so the client can access
all the inherited functionality of a CORBA object.
• Account::withdrawal and all other member methods are defined as
pure virtual, so the client code cannot instantiate the Account proxy
class or any other proxy class. Instead, clients can access the Account
object only indirectly through object references.
203
CHAPTER 8 | Developing a Client
// ...
// withdraw_amt is already initialized
204
Using Object References
Note: Because _ptr types are not always implemented as actual C++
pointers, you should always use the _ptr definition. Regardless of the
underlying mapping, a _ptr type is always guaranteed to behave like a
pointer, so it is portable across all platforms and language mappings.
205
CHAPTER 8 | Developing a Client
Counting References
When you initialize a _var or _ptr reference with an object reference for the
first time, the client instantiates a proxy and sets that proxy’s reference
count to one. Each proxy class has a _duplicate() method, which allows a
client to create a copy of the target proxy. In practice, this method simply
increments the reference count on that proxy and returns a new _ptr
reference to it. Actual methods for copying _ptr and _var references differ
and are addressed separately in this chapter; conceptually, however, the
result is the same.
For example, given an object reference to the Account interface, the
following client code initializes a _ptr reference as follows:
Account_ptr accp1 = ...; // get reference somehow
This instantiates an Account object proxy and automatically sets its
reference count to one:
Account
accp1
1
The following code copies accp1 into reference accp2, thus incrementing the
Account proxy’s reference count to 2
Account_ptr accp2 = Account::_duplicate(accp1);
206
Using Object References
The client now has two initialized _ptr references, accp1 and accp2. Both
refer to the same proxy, so invocations on either are treated as invocations
on the same object.
Account
accp1
2
accp2
207
CHAPTER 8 | Developing a Client
Nil References
Nil references are analogous to C++ null pointers and contain a special
value to indicate that the reference points nowhere. Nil references are useful
mainly to indicate “not there” or optional semantics. For example, if you
have a lookup operation that searches for objects via a key, it can return a
nil reference to indicate the “not found” condition instead of raising an
exception. Similarly, clients can pass a nil reference to an operation to
indicate that no reference was passed for this operation—that is, you can
use a nil reference to simulate an optional reference parameter.
You should only use the CORBA::is_nil() method to test whether a
reference is nil. All other attempts to test for nil have undefined behavior.
For example, the following code is not CORBA-compliant and can yield
unpredictable results:
You cannot invoke operations on a nil reference. For example, the following
code has undefined behavior:
208
Using Object References
// In namespace CORBA:
class Object {
public:
static Object_ptr _duplicate(Object_ptr obj);
void release(Type_ptr);
Boolean is_nil(Type_ptr p);
Boolean _is_a(const char * repository_id);
Boolean _non_existent();
Boolean _is_equivalent(Object_ptr other_obj);
ULong _hash(ULong max);
// ...
};
209
CHAPTER 8 | Developing a Client
if (!CORBA::is_nil(obj) &&
obj->_is_a("IDL:BankDemo/Account:1.0"))
// It's an Account object...
else
// Some other type of object...
The test for nil in this code example prevents the client program from
making a call via a nil object reference.
_is_a() lets applications manipulate IDL interfaces without static
knowledge of the IDL—that is, without having linked the IDL-generated
stubs. Most applications have static knowledge of IDL definitions, so they
never need to call _is_a(). In this case, you can rely on _narrow() to
ascertain whether an object supports the desired interface.
210
Using Object References
_hash() returns a hash value in the range 0..max-1. The hash value remains
constant for the lifetime of the reference. Because the CORBA specifications
offer no hashing algorithm, the same reference on different ORBs can have
different hash values.
_hash() is guaranteed to be implemented as a local operation—that is, it
will not send a message on the wire.
_hash() is mainly useful for services such as the transaction service, which
must be able to determine efficiently whether a given reference is already a
member of a set of references. _hash() permits partitioning of a set of
references into an arbitrary number of equivalence classes, so set
membership testing can be performed in (amortized) constant time.
Applications rarely need to call this method.
211
CHAPTER 8 | Developing a Client
Duplicating and releasing To make a copy of a _ptr reference, invoke the static _duplicate() member
references method on an existing object reference. For example:
_duplicate() makes an exact copy of a reference. The copy and the original
are indistinguishable from each other. As shown earlier (see “Counting
References” on page 206), _duplicate() also makes a deep copy of the
target reference, so the reference count on the proxy object is incremented.
Consequently, you must call release() on all duplicated references to
destroy them and prevent memory leaks.
To destroy a reference, use the release method. For example:
Widening and narrowing _ptr Proxy classes emulate the inheritance hierarchy of the IDL interfaces from
references which they are generated. Thus, you can widen and narrow _ptr references
to the corresponding proxies.
Widening assignments Object references to proxy instances conform to C++ rules for type
compatibility. Thus, you can assign a derived reference to a base reference,
or pass a derived reference where a base reference is expected.
212
Using Object References
Note: Because all proxies inherit from CORBA::Object, you can assign
any type of object reference to Object_ptr, such as _ptr references obj1
and obj2.
ck
accp
Account
obj1 1
obj2
Figure 10: Multiple _ptr references to a proxy object can leave the
reference count unchanged.
213
CHAPTER 8 | Developing a Client
Type-safe narrowing of _ptr For each interface, the IDL compiler generates a static _narrow() method
references that lets you down-cast a _ptr reference at runtime. For example, the
following code narrows an Account reference to a CheckingAccount
reference:
214
Using Object References
_var class member methods Given the Account interface shown earlier, the IDL compiler generates an
Account_var class with the following definition:
class Account_var{
public:
Account_var();
Account_var(Account_ptr &);
Account_var(const Account_var &);
~Account_var();
Account_var & operator=(Account_ptr &);
Account_var & operator=(const Account_var &);
operator Account_ptr & ();
Account_ptr in() const;
Account_ptr & in inout();
Account_ptr & in out();
Account_ptr _retn();
private:
Account_ptr p; //actual reference stored here
};
215
CHAPTER 8 | Developing a Client
operator Account_ptr &(): This conversion operator lets you pass a _var
reference where a _ptr reference is expected, so use of _var references is
transparent for assignment and parameter passing.
Widening and narrowing _var You can copy-construct and assign from _var references, but only if both
References references are of the same type. For example, the following code is valid:
216
Using Object References
To widen a _var reference, you must first call _duplicate() on the original
_var. Although _duplicate() expects a _ptr reference, a _var can be
supplied in its place, as with any method that expects a _ptr reference.
_duplicate() returns a _ptr reference that can then be implicitly widened.
For example, in the following statement, _duplicate() receives a
CheckingAccount_var:
Account_var accv1(CheckingAccount::_duplicate(ckv));
_duplicate() returns a CheckingAccount_ptr that is implicitly widened to
an Account_ptr as the argument to the Account_var constructor. The
constructor in turn takes ownership, so the copy made by _duplicate() is
not leaked.
In the next statement, _duplicate() expects an Account_ptr:
Account_var accv2(Account::_duplicate(ckv));
In fact, a CheckingAccount_var argument is supplied, which has a
conversion operator to CheckingAccount_ptr. A CheckingAccount_ptr can
be passed where an Account_ptr is expected, so the compiler finds an
argument match. _duplicate() makes a copy of the passed reference and
returns it as an Account_ptr, which is adopted by the Account_var, and no
leak occurs.
You can also use _duplicate() for implicit _var widening through
assignment, as in these examples:
accv1 = CheckingAccount::_duplicate(ckv);
accv2 = Account::_duplicate(ckv);
You can freely mix _ptr and _var references; you only need to remember
that when you give a _ptr reference to a _var reference, the _var takes
ownership:
217
CHAPTER 8 | Developing a Client
218
Using Object References
String Conversions
Object references can be converted to and from strings, which facilitates
persistent storage. When a client obtains a stringified reference, it can
convert the string back into an active reference and contact the referenced
object. The reference remains valid as long as the object remains viable.
When the object is destroyed, the reference becomes permanently invalid.
// In <corba/orb.hh>:
namespace CORBA {
// ...
class ORB {
public:
char * object_to_string(Object_ptr op);
Object_ptr string_to_object(const char *);
// ...
};
// ...
}
object_to_string() For example, the following code stringifies an Account object reference:
219
CHAPTER 8 | Developing a Client
c65723a312e300001000000000000004a000000010102000e0000003139322e3
36382e312e3231300049051b0000003a3e0231310c01000000c7010000234800
008000000000000000000010000000600000006000000010000001100
The stringified references returned by object_to_string() always contain
the prefix IOR:, followed by an even number of hexadecimal digits.
Stringified references do not contain any unusual characters, such as control
characters or embedded newlines, so they are suitable for text I/O.
try {
CORBA::Object_ptr obj;
obj = orb->string_to_object(accv[1]);
if (CORBA::is_nil(obj))
throw 0; // accv[1] is nil
} catch (...) {
// Deal with error...
}
220
Using Object References
Constraints IOR string references should be used only for these tasks:
• Store and retrieve an IOR string to and from a storage medium such as
disk or tape.
• Conversion to an active reference.
It is inadvisable to rely on IOR string references as database keys for the
following reasons:
• Actual implementations of IOR strings can vary across different
ORBs—for example, vendors can add proprietary information to the
string, such as a time stamp. Given these differences, you cannot rely
on consistent string representations of any object reference.
• The actual size of IOR strings—between 200 and 600 bytes— makes
them prohibitively expensive to use as database keys.
In general, you should not compare one IOR string to another. To compare
object references, use is_equivalent() (see page 210).
Note: Stringified IOR references are one way to make references to initial
objects known to clients. However, distributing strings as e-mail messages
or writing them into shared file systems is neither a distributed nor a
scalable solution. More typically, applications obtain object references
through the naming service (see Chapter 18 on page 499).
Using corbaloc URL strings string_to_object() can also take as an argument a corbaloc-formatted
URL, and convert it into an object reference. A corbaloc URL denotes
objects that can be contacted by IIOP or resolve_initial_references().
A corbaloc URL uses one of the following formats:
corbaloc:rir:/rir-argument
corbaloc:iiop-address[, iiop-address].../key-string
221
CHAPTER 8 | Developing a Client
222
Initializing and Shutting Down the ORB
223
CHAPTER 8 | Developing a Client
module Test {
interface Example {
attribute string name;
oneway void set_address(in string addr);
string get_address();
};
};
The IDL compiler maps this definition’s members to the following methods
in the C++ proxy class Example. A client invokes on these methods as if
their implementations existed within its own address space:
namespace Test {
// ...
class Example : public virtual CORBA::Object
{
public:
// ...
virtual char* name() = 0;
virtual void name(const char* _itvar_name) = 0;
virtual void set_address(const char* addr) = 0;
virtual char* get_address() = 0;
// ...
};
};
224
Passing Parameters in Client Invocations
225
CHAPTER 8 | Developing a Client
Simple Parameters
For simple fixed-length types, parameters are passed by value if they are in
parameters or return values, and are passed by reference if they are inout or
out parameters.
For example, the following IDL defines an operation with simple parameters:
interface Example {
long op(
in long in_p, inout long inout_p, out long out_p
);
};
The proxy member method signature is the same as the signature of any
other C++ method that passes simple types in these directions:
virtual CORBA::Long
op(
CORBA::Long in_p,
CORBA::Long & inout_p,
CORBA::Long & out_p
) = 0;
The client passes the constant 500 as the in parameter. For the inout
parameter, the client passes the initial value 99, which the server can
change. No initialization is necessary for the out parameter and the return
value. No dynamic allocation is required; the client can pass variables on
the stack, on the heap, or in the data segment (global or static variables).
226
Passing Parameters in Client Invocations
interface Example {
FLS op(in FLS in_p, inout FLS inout_p, out FLS out_p);
};
Using the generated proxy method in the client is easy, and no dynamic
memory allocations are required:
227
CHAPTER 8 | Developing a Client
228
Passing Parameters in Client Invocations
interface Example {
Larr op(in Larr in_p, inout Larr inout_p, out Larr out_p);
};
The IDL compiler maps this IDL to the following C++ definitions:
For in, inout, and out parameters, memory is caller-allocated and need not
be on the heap; the method receives and, for inout and out parameters,
modifies the array via the passed pointer. For the return value, a pointer
must be returned to dynamically allocated memory, simply because there is
no other way to return an array in C++. Therefore, the client must
deallocate the return value when it is no longer wanted:
// Use results...
Larr_free(ret_val); // Must deallocate here!
229
CHAPTER 8 | Developing a Client
// Use results...
230
Passing Parameters in Client Invocations
String Parameters
The C++ mapping does not encapsulate strings in a class, so string
parameters are passed as char *. Because strings are variable-length types,
the following memory management issues apply:
• in strings are passed as const char *, so the callee cannot modify the
string’s value. The passed string need not be allocated on the heap.
• inout strings must be allocated on the heap by the caller. The callee
receives a C++ reference to the string pointer. This is necessary
because the callee might need to reallocate the string if the new value
is longer than the initial value. Passing a reference to the callee lets the
callee modify the bytes of the string and the string pointer itself.
Responsibility for deallocating the string remains with the caller.
• out strings are dynamically allocated by the callee. Responsibility for
deallocating the string passes to the caller.
• Strings returned as the return value behave like out strings: they are
allocated by the callee and responsibility for deallocation passes to the
caller.
For example, the following IDL defines an operation with string parameters:
interface Example {
string op(
in string in_p,
inout string inout_p,
out string out_p
);
};
The IDL compiler maps this interface to the following class, in which string
parameters are passed as char *:
231
CHAPTER 8 | Developing a Client
Example_var ev = ...;
232
Passing Parameters in Client Invocations
Example_var ev = ...;
In this example, in and inout are initialized to the null pointer by the
default constructor. However, it is illegal to pass a null pointer across an
interface; code that does so is liable to crash or raise an exception.
Note: This restriction applies to all types that are passed by pointer, such
as arrays and variable-length types. Never pass a null pointer or an
uninitialized pointer. Only one exception applies: you can pass a nil
reference, even if nil references are implemented as null pointers.
233
CHAPTER 8 | Developing a Client
_out Types
IDL out parameters result in proxy signatures that use C++ _out types.
_out types ensure correct deallocation of previous results for _var types.
For example, the following IDL defines a single out parameter:
interface Person {
void get_name(out string name);
// ...
};
class Person {
public:
void get_name(CORBA::String_out name);
// ...
};
The following code fragment uses the Person interface, but leaks memory:
char * name;
Person_var person_1 = ...;
Person_var person_2 = ...;
person_1->get_name(name);
cout << "Name of person 1: " << name << endl;
CORBA::string_free(name); // Deallocate
234
Passing Parameters in Client Invocations
char * name;
Person_var person_1 = ...;
Person_var person_2 = ...;
person_1->get_name(name);
cout << "Name of person 1: " << name << endl;
CORBA::String_free(name); // Much better!
person_2->get_name(name); // No problem
cout << "Name of person 2: " << name << endl;
CORBA::String_free(name); // Deallocate
person_1->get_name(name);
cout << "Name of person 1: " << name << endl;
When the name variable is passed to get_name a second time, the mapping
implementation transparently deallocates the previous string. However, how
does the mapping manage to avoid deallocation for pointer types but
deallocates the previous value for _var types?
235
CHAPTER 8 | Developing a Client
Note: You can ignore most of the implementation details for _out types. It
is only important to know that they serve to prevent memory leaks when
you pass a _var as an out parameter.
236
Passing Parameters in Client Invocations
interface Example {
VLS op(in VLS in_p, inout VLS inout_p, out VLS out_p);
};
The IDL compiler maps this IDL to the following C++ definitions:
class VLS_out;
// ...
virtual VLS *
op(const VLS & in_p, VLS & inout_p, VLS_out out_p) = 0;
237
CHAPTER 8 | Developing a Client
// Use values...
// Use values...
Note: Type Any is passed using the same rules—that is, out parameters
and return values are dynamically allocated by the stub and must be
deallocated by the caller. Of course, you can use CORBA::Any_var to
achieve automatic deallocation.
238
Passing Parameters in Client Invocations
interface Example {
Sarr op(in Sarr in_p, inout Sarr inout_p, out Sarr out_p);
};
The IDL compiler maps this IDL to the following C++ definitions:
Sarr in;
in[0] = CORBA::string_dup("Bjarne");
in[1] = CORBA::string_dup("Stan");
in[2] = CORBA::string_dup("Andrew");
Sarr inout;
inout[0] = CORBA::string_dup("Dennis");
inout[1] = CORBA::string_dup("Ken");
inout[2] = CORBA::string_dup("Brian");
239
CHAPTER 8 | Developing a Client
// Use values...
As always, you can rewrite the code to use _var types, and so prevent
memory leaks:
Sarr in;
in[0] = CORBA::string_dup("Bjarne");
in[1] = CORBA::string_dup("Stan");
in[2] = CORBA::string_dup("Andrew");
Sarr inout;
inout[0] = CORBA::string_dup("Dennis");
inout[1] = CORBA::string_dup("Ken");
inout[2] = CORBA::string_dup("Brian");
// Use values...
240
Passing Parameters in Client Invocations
interface Example {
string greeting();
Example op(
in Example in_p,
inout Example inout_p,
out Example out_p
);
};
The IDL compiler maps this IDL to the following C++ definitions:
class Example_out;
// ...
virtual Example_ptr op(
Example_ptr in_p, Example_ptr & inout_p, Example_out out_p
) = 0;
Example_var ev = ...;
Example_var in = ...; // Initialize in param
Example_var inout = ...; // Initialize inout param
Example_ptr out; // Note _ptr reference
Example_ptr ret_val; // Note _ptr reference
// Use references...
CORBA::release(out); // Deallocate
CORBA::release(ret_val); // Ditto...
Note that the code explicitly releases the references returned as the out
parameter and the return value.
241
CHAPTER 8 | Developing a Client
You can also rewrite this code to use _var references in order to avoid
memory leaks:
Example_var ev = ...;
Example_var in = ...; // Initialize in param
Example_var inout = ...; // Initialize inout param
Example_var out; // Note _var reference
Example_var ret_val; // Note _var reference
// Use references...
242
Passing Parameters in Client Invocations
Do not ignore variable-length return values. Ignoring return values can leak
memory. For example, the following interface defines operation
do_something() to return a string value:
// interface Example {
// string do_something();
// };
Be careful never to ignore the return, because the memory that the stub
allocates to the return value can never be reclaimed.
Allocate string and reference inout parameters on the heap and deallocate
them after the call. String and reference inout parameters must be
allocated on the heap; ownership of the memory remains with the caller.
243
CHAPTER 8 | Developing a Client
Use _var types for complex inout and out parameters and return values.
Always use a _var type when a value must be heap-allocated. This includes
any complex or variable-length inout or out parameter or return value. After
you have assigned a parameter to a _var type, you don’t have to worry
about deallocating memory.
For example, the following interface defines three operations:
// Some sample IDL to show how _var types make life easier.
interface Example {
string get_string();
void modify_string(inout string s);
void put_string(in string s);
};
Because _var types convert correctly to pass in any direction, the following
code does exactly the right things:
// _var automates memory management.
{
Example_var ev = ...; // Get reference
CORBA::String_var s; // Parameter
244
Passing Parameters in Client Invocations
struct, fixed const struct & struct & struct & struct
union, fixed const union & union & union & union
struct, variable const struct & struct & struct * & struct *
union, variable const union & union & union * & union *
As Table 9 shows, the parameter type varies for both out parameters and
return values, depending on whether a complex structure, union, or array is
variable length or fixed length. Table 10 shows the considerably simpler
parameter-passing rules for _var types:
245
CHAPTER 8 | Developing a Client
246
Client Policies
Client Policies
Orbix supports a number of quality of service policies, which can give a
client programmatic control over request processing:
• RebindPolicy specifies whether the ORB transparently reopens closed
connections and rebinds forwarded objects.
• SyncScopePolicy determines how quickly a client resumes processing
after sending one-way requests.
• Timeout policies offer different degrees of control over the length of
time that an outstanding request remains viable.
You can set quality of service policies at three scopes, in descending order of
precedence:
1. On individual objects, so they apply only to invocations on those
objects.
2. On a given thread, so they apply only to invocations on that thread
3. On the client ORB, so they apply to all invocations.
You can set policies in any combination at all three scopes; the effective
policy is determined on each invocation. If settings are found for the same
policy type at more than one scope, the policy at the lowest scope prevails.
For detailed information about setting these and other policies on a client,
see “Setting Client Policies” on page 194.
Note: Because all policy types and their settings are defined in the
Messaging module, client code that sets quality of service policies must
include omg/messaging.hh.
247
CHAPTER 8 | Developing a Client
RebindPolicy
A client’s RebindPolicy determines whether the ORB can transparently
reconnect and rebind. A client’s rebind policy is set by a RebindMode
constant, which describes the level of transparent binding that can occur
when the ORB tries to carry out a remote request:
248
Client Policies
SyncScopePolicy
A client’s SyncScopePolicy determines how quickly it resumes processing
after sending one-way requests. You specify this behavior with one of these
SyncScope constants:
Note: This policy only applies to GIOP 1.2 (and higher) requests.
249
CHAPTER 8 | Developing a Client
Timeout Policies
A responsive client must be able to specify timeouts in order to abort
invocations. Orbix supports several standard OMG timeout policies, as
specified in the Messaging module; it also provides proprietary policies in
the IT_CORBA module that offer more fine-grained control. Table 11 shows
which policies are supported in each category:
Proprietary BindingEstablishmentPolicy
Timeout Policies RelativeBindingExclusiveRoundtripTimeoutPolicy
RelativeBindingExclusiveRequestTimeoutPolicy
RelativeConnectionCreationTimeoutPolicy
InvocationRetryPolicy
If a request’s timeout expires before the request can complete, the client
receives the system exception CORBA::TIMEOUT.
Note: When using these policies, be careful that their settings are
consistent with each other. For example, the
RelativeRoundtripTimeoutPolicy specifies the maximum amount of time
allowed for round-trip execution of a request.
Orbix also provides its own policies, which let you control specific
segments of request execution—for example,
BindingEstablishmentPolicy lets you set the maximum time to establish
bindings.
It is possible to set the maximum binding time to be greater than the
maximum allowed for roundtrip request execution. Although these settings
are inconsistent, no warning is issued; and Orbix silently adheres to the
more restrictive policy.
Setting absolute and relative times Two policies, RequestEndTimePolicy and ReplyEndTimePolicy, set absolute
deadlines for request and reply delivery, respectively, through the
TimeBase::UtcT type. Other policies set times that are relative to a specified
250
Client Policies
251
CHAPTER 8 | Developing a Client
252
Client Policies
No default is set for this policy; if it is not set, request delivery has unlimited
time to complete.
For example, the following code sets a RelativeRequestTimeoutPolicy
override on the ORB PolicyManager, setting a three-second limit on the time
allowed to deliver a request:
253
CHAPTER 8 | Developing a Client
struct BindingEstablishmentPolicyValue
{
TimeBase::TimeT relative_expiry;
unsigned short max_binding_iterations;
unsigned short max_forwards;
TimeBase::TimeT initial_iteration_delay;
float backoff_ratio;
};
254
Client Policies
0,
initial_iteration_delay x backoff_ratio0,
initial_iteration_delay x backoff_ratio1,
initial_iteration_delay x backoff_ratio2,
...,
initial_iteration_delay x backoff_ratio(max_binding_iterations -
2)
try{
CORBA::Any bind_est_value;
IT_CORBA::BindingEstablishmentPolicyValue val;
val.rel_expiry = (TimeBase::TimeT)30 * 10000000; // 30s
val.max_rebinds = (CORBA::UShort)5; // 5 binding tries
val.max_forwards = (CORBA::UShort)20; // 20 forwards
val.initial_iteration_delay
= (TimeBase::TimeT)1000000; // 0.1s delay
val.backoff_ratio = (CORBA::Float)2.0; // back-off
ratio
CORBA::PolicyList policies(1);
policies.length(1);
policies[0] = orb->create_policy(
IT_CORBA::BINDING_ESTABLISHMENT_POLICY_ID,
bind_est_value
);
lots_of_retries_slave = ClientPolicy::Slave::_narrow(obj);
}
255
CHAPTER 8 | Developing a Client
256
Client Policies
struct InvocationRetryPolicyValue
{
unsigned short max_retries;
unsigned short max_rebinds;
unsigned short max_forwards;
TimeBase::TimeT initial_retry_delay;
float backoff_ratio;
};
• max_forwards limits the number of forward tries that are allowed for a
given invocation. Set to -1 to specify unlimited forward tries. The
default value is 20.
• initial_retry_delay sets the amount of time, in 100-nanosecond units,
between the first and second retries. The default value is 0.1 seconds.
Note: The delay between the initial invocation and first retry is
always 0.
257
CHAPTER 8 | Developing a Client
0,
initial_iteration_delay x backoff_ratio0,
initial_iteration_delay x backoff_ratio1,
initial_iteration_delay x backoff_ratio2,
...,
initial_iteration_delay x backoff_ratio(max_retries - 2)
try{
CORBA::Any lots_of_retries_value;
IT_CORBA::InvocationRetryPolicyValue val;
val.max_retries = (CORBA::UShort)10000; // 10000 retries
val.max_rebinds = (CORBA::UShort)5; // 5 rebinds
val.max_forwards = (CORBA::UShort)20; // 20 forwards
val.initial_retry_delay
= (TimeBase::TimeT)1000000; // 0.1s delay
val.backoff_ratio = (CORBA::Float)2.0; // back-off
ratio
CORBA::PolicyList policies(1);
policies.length(1);
policies[0] = orb->create_policy(
IT_CORBA::INVOCATION_RETRY_POLICY_ID,
lots_of_retries_value
);
lots_of_retries_slave = ClientPolicy::Slave::_narrow(obj);
}
258
Client Policies
259
CHAPTER 8 | Developing a Client
260
CHAPTER 9
Developing a
Server
This chapter explains how to develop a server that implements
servants for CORBA objects.
261
CHAPTER 9 | Developing a Server
Implementing Operations
Implementation Inheritance
Interface Inheritance
Multiple Inheritance
Termination Handler
262
POAs, Skeletons, and Servants
POA skeleton class For each IDL interface, the IDL compiler generates a POA_ skeleton class
that you compile into the server application. Skeleton classes are abstract
base classes. You implement skeleton classes in the server application code
with servant classes, which define the behavior of the pure virtual methods
that they inherit. Through a servant’s inherited connection to a skeleton
class, ORB runtime connects that servant back to the CORBA object that it
incarnates.
TIE class The IDL compiler can also generate a TIE class, which lets you implement
CORBA objects with classes that are unrelated (by inheritance) to skeleton
classes. For more information, see “Delegating Servant Implementations” on
page 288.
Note: The POA_ prefix only applies to the outermost naming scope of an
IDL construct. So, if an interface is nested in a module, only the outermost
module gets the POA_ prefix; constructs nested inside the module do not
have the prefix.
263
CHAPTER 9 | Developing a Server
Server request handling Figure 11 shows how a CORBA server handles an incoming client request,
and the stages by which it dispatches that request to the appropriate
servant. The server’s ORB runtime directs an incoming request to the POA
where the object was created. Depending on the POA’s state, the request is
either processed or blocked. A POA manager can block requests by rejecting
them outright and raising an exception in the client, or by queueing them for
later processing.
Server
Request Servants
manager
ORB POA POA
Figure 11: The server-side ORB conveys client requests to the POA via its
manager, and the POA dispatches the request to the appropriate servant.
264
Mapping Interfaces to Skeleton Classes
module BankDemo
{
typedef float CashAmount; // type represents cash
typedef string AccountId; // Type represents account IDs
// ...
interface Account
{
exception InsufficientFunds {};
void
withdraw(in CashAmount amount)
raises (InsufficientFunds);
void
deposit( in CashAmount amount);
};
265
CHAPTER 9 | Developing a Server
namespace POA_BankDemo
{
class Account :
virtual public PortableServer::ServantBase
{
virtual ::BankDemo::AccountId
account_id() IT_THROW_DECL((CORBA::SystemException)) = 0;
virtual ::BankDemo::CashAmount
balance() IT_THROW_DECL((CORBA::SystemException)) = 0;
virtual void
withdraw(
::BankDemo::CashAmount amount
) IT_THROW_DECL((CORBA::SystemException,
BankDemo::Account::InsufficientFunds)) = 0;
virtual void
deposit(
::BankDemo::CashAmount amount
) IT_THROW_DECL((CORBA::SystemException)) = 0;
};
The following points are worth noting about the skeleton class:
• POA_BankDemo::Account inherits from PortableServer::ServantBase.
All skeleton classes inherit from the ServantBase class for two reasons:
♦ ServantBase provides functionality that is common to all
servants.
♦ Servants can be passed generically—you can pass a servant for
any type of object as a pointer or reference to ServantBase.
• The names of the skeleton class and the corresponding client-side
proxy class are different. In this case, the fully scoped name of the
skeleton class is POA_BankDemo::Account, while the proxy class name
is BankDemo::Account.
This differentiation is important if client and server are linked into the
same program, because it avoids name clashes for multiply defined
symbols. It also preserves location transparency because it guarantees
266
Mapping Interfaces to Skeleton Classes
267
CHAPTER 9 | Developing a Server
virtual BankDemo::AccountId
account_id() IT_THROW_DECL((CORBA::SystemException));
virtual BankDemo::CashAmount
balance() IT_THROW_DECL((CORBA::SystemException));
virtual void
withdraw(
BankDemo::CashAmount amount
) IT_THROW_DECL((CORBA::SystemException,
BankDemo::Account::InsufficientFunds));
virtual void
deposit(
BankDemo::CashAmount amount
) IT_THROW_DECL((CORBA::SystemException));
private:
// Prevent copying and assigment of servants
AccountImpl(const AccountImpl &);
void operator=(const AccountImpl &);
};
Servant class requirements The following requirements and recommendations apply to servant class
definitions:
268
Creating a Servant Class
• The code must include the generated server header file—in this case,
BankDemoS.hh.
• AccountImpl inherits from POA_BankDemo::Account through virtual
inheritance. If, as in this case, the servant class inherits from only one
source, it is unimporant to specify virtual inheritance. However, a
servant class that inherits from multiple skeleton classes should always
use virtual inheritance to prevent errors.
• The choice of name for servant classes is purely a matter of
convention. The examples here and elsewhere apply the Impl suffix to
the original interface name, as in AccountImpl. It is always good
practice to have a naming convention and use it consistently in your
code.
• The copy constructor and assignment operator for the servant class are
private to prevent copying and assignment of servant instances.
Servants should not be copied or assigned; only one servant should
incarnate any given CORBA object; otherwise, it is unclear which
servant should handle requests for that object. It is always good
practice to hide a servant’s copy constructor and assignment operator.
The preceding AccountImpl class is a complete and functional servant class.
It only remains to implement the pure virtual methods that are inherited
from the skeleton. You can also can add other member variables and
methods, public and private, that can help implement a servant. For
example, it is typical to add a constructor and destructor, and private
member variables to hold the state of the object while the servant is in
memory.
269
CHAPTER 9 | Developing a Server
Implementing Operations
Most work in developing a servant consists of implementing each inherited
pure virtual method. Because the application code controls the body of each
operation, it largely determines the application’s overall behavior. The
following code outlines an implementation of the withdraw() method:
void
AccountImpl::withdraw(
BankDemo::CashAmount amount
) IT_THROW_DECL((
CORBA::SystemException,
BankDemo::Account::InsufficientFunds
))
{
// ... database connection (via PSS) code omitted here
ref->balance(new_balance);
// ...
270
Activating CORBA Objects
this() The simplest way to activate a CORBA object is by calling _this() on the
servant. The IDL compiler generates a _this() method for each servant
skeleton class. _this() performs two separate tasks:
• Checks the POA to determine whether the servant is registered with an
existing object. If not, _this() creates an object from the servant’s
interface, registers a unique ID for this object in the POA’s active object
map, and maps this object ID to the servant’s address.
• Generates and returns an object reference that includes the object’s ID
and POA identifier.
In other words, the object is implicitly activated in order to return an object
reference.
271
CHAPTER 9 | Developing a Server
Explicit activation methods Alternatively, you can explicitly activate a CORBA object: call
activate_object() or activate_object_with_id() on the POA. You can
then obtain an object reference by calling _this() on the servant. Because
the servant is already registered in the POA with an object ID, the method
simply returns an object reference.
The ability to activate an object implicitly or explicitly depends on a POA’s
activation policy. For more information on this topic, see “Using POA
Policies” on page 308.
Note: The object reference returned by _this() is independent of the
servant itself; you must eventually call release() on the object or hold it in
a _var reference in order to avoid resource leaks. Releasing the object
reference has no effect on the corresponding servant.
272
Handling Output Parameters
273
CHAPTER 9 | Developing a Server
Simple Parameters
Simple IDL types such as short or long are passed by value. For example,
the following IDL defines operation Example::op(), which passes three long
parameters:
interface Example {
long
op( in long in_p, inout long inout_p, out long out_p);
};
virtual CORBA::Long
op(
CORBA::Long in_p,
CORBA::Long & inout_p,
CORBA::Long_out out_p
) throw(CORBA::SystemException);
Implementation example This example has the same mapping as the client, where CORBA::Long_out
type is simply an alias for CORBA::Long &. You might implement this
operation as follows:
CORBA::Long
ExampleImpl::op(
CORBA::Long in_p, CORBA::Long & inout_p, CORBA::Long_out out_p
) throw(CORBA::SystemException)
{
inout_p = 2 * inout_p; // Change inout_p.
out_p = in_p * in_p; // Set out_p
return in_p / 2; // Return in_p
}
The method simply sets output parameters and return values; the changes
are automatically propagated back to the client.
274
Handling Output Parameters
interface Example {
FLS op(in FLS in_p, inout FLS inout_p, out FLS out_p);
};
Implementation example The following code implements the servant operation. No memory
management issues arise; the method simply assigns the values of output
parameters and the return value:
FLS
ExampleImpl::op(const FLS & in_p, FLS & inout_p, FLS_out out_p)
throw(CORBA::SystemException)
{
cout << in_p.long_val << endl; // Use in_p
cout << in_p.double_val << endl; // Use in_p
cout << inout_p.double_val << endl; // Use inout_p
275
CHAPTER 9 | Developing a Server
// Change inout_p
inout_p.double_val = inout_p.long_val * in_p.double_val;
276
Handling Output Parameters
interface Example {
Larr op(in Larr in_p, inout Larr inout_p, out Larr out_p);
};
Larr_slice *
ExampleImpl::
op(const Larr in_p, Larr_slice * inout_p, Larr_out out_p)
throw(CORBA::SystemException)
{
int len = sizeof(in_p) / sizeof(*in_p);
// Modify inout_p
inout_p[1] = 12345;
277
CHAPTER 9 | Developing a Server
// Initialize out_p
for (int i = 0; i < len; i++)
out_p[i] = i * i;
return ret_val;
}
278
Handling Output Parameters
String Parameters
String-type output parameters and return values must be dynamically
allocated. For example, the following IDL defines a fixed-length array that
operation Example::op() uses in its return value and parameters:
interface Example {
string op(
in string in_p,
inout string inout_p,
out string out_p
);
};
Memory requirements The server is constrained by the same memory requirements as the client:
• Strings are initialized as usual.
• inout strings are dynamically allocated and initialized by the client.
The servant can change an inout string by modifying the bytes of the
inout string in place, or shorten the inout string in place by writing a
terminating NUL byte into the string. To return an inout string that is
longer than the initial value, the servant must deallocate the original
copy and allocate a longer string.
• out strings must be dynamically allocated.
• Return value strings must be dynamically allocated.
279
CHAPTER 9 | Developing a Server
const char *
ExampleImpl::
op(
const char * in_p,
char * & inout_p,
CORBA::String_out out_p
) throw(CORBA::SystemException)
{
CORBA::string_free(inout_p);
inout_p = CORBA::string_dup("New string value");
return ret_val;
}
280
Handling Output Parameters
interface Example {
VLS op(in VLS in_p, inout VLS inout_p, out VLS out_p);
};
VLS *
ExampleImpl::
op(const VLS & in_p, VLS & inout_p, VLS_out out_p)
throw(CORBA::SystemException)
{
cout << in_p.string_val << endl; // Use in_p
cout << inout_p.long_val << endl; // Use inout_p
inout_p.long_val = 99; // Modify inout_p
out_p = new VLS; // Allocate out param
out_p->long_val = 1; // Initialize...
out_p->string_val = CORBA::string_dup("One");
281
CHAPTER 9 | Developing a Server
return ret_val;
}
282
Handling Output Parameters
interface Example {
Sarr op(in Sarr in_p, inout Sarr inout_p, out Sarr out_p);
};
Implementation example The following code implements the servant operation. As with all nested
strings, string elements behave like a String_var, so assignments make
deep copies or, if a pointer is assigned, take ownership:
Sarr_slice *
ExampleImpl::
op(
const Sarr in_p, Sarr_slice * inout_p, Sarr_out out_p
) throw(CORBA::SystemException)
283
CHAPTER 9 | Developing a Server
{
cout << in_p[1] << endl; // Use in_p
cout << inout_p[0] << endl; // Use inout_p
inout_p[1] = in_p[0]; // Modify inout_p
284
Handling Output Parameters
interface Example {
string greeting();
Example op(
in Example in_p,
inout Example inout_p,
out Example out_p
);
};
Implementation example The following implementation dynamically allocates the new value of
inout_p after releasing the previous value. The return value is dynamically
allocated because _this() calls _duplicate() implicitly.
As shown in this example, you should always test for nil before making a
call on a passed in or inout reference. Otherwise, your servant is liable to
make a call on a nil reference and cause a core dump.
285
CHAPTER 9 | Developing a Server
Example_ptr
ExampleImpl::
op(
Example_ptr in_p, Example_ptr & inout_p, Example_out out_p
) throw(CORBA::SystemException)
{
// Use in_p.
//
if (!CORBA::is_nil(in_p)) {
CORBA::String_var s = in_p->greeting();
cout << s << endl;
}
// Use inout_p.
//
if (!CORBA::is_nil(inout_p)) {
CORBA::String_var s = inout_p->greeting();
cout << s << endl;
}
286
Counting Servant References
Enabling reference counting The POA specification provides the standard methods _add_ref() and
_remove_ref() to support reference counting, but by default they do
nothing. You can enable reference counting by inheriting the standard class
PortableServer::RefCountServantBase in servant implementations. For
example:
class BankDemo_AccountImpl
: public virtual POA_BankDemo::Account,
public virtual PortableServer::RefCountServantBase
Implicit reference counting With reference counting enabled, the POA calls _add_ref() when it holds a
pointer to a servant in any thread, and calls _remove_ref() when it is
finished with that servant. POA methods that return servants to user code
call _add_ref() before they deliver the servant, so the same code should
call _remove_ref() on the result when it is finished.
Explicit reference counting In your own code, you should call _add_ref() for each additional pointer to
a servant, and _remove_ref() when you are done with that pointer (rather
than delete it). Doing so ensures that the servant is deleted when no
pointers are held to that servant either in your own code or in the POA.
Reference counting is ignored by tie-based servants. Tie templates, as
defined in the POA standard, do not support reference counting, Therefore,
it is not recommended that you use the tie approach for multi-threaded
servers.
287
CHAPTER 9 | Developing a Server
2 Pass the tie object’s address to the tie object constructor with this syntax:
tie-template-class<impl-class> tie-servant(tied-object);
288
Delegating Servant Implementations
Given this tie servant, you can use it to create an object reference:
When the POA receives client invocations on the bankref object, it relays
them to tie servant bank_srv_tie, which delegates them to the bank tie
object for processing.
Removing tie objects and servants You remove a tie servant from memory like any other servant—for example,
with PortableServer::POA::deactivate_object(). If the tie servant’s tie
object implements only a single object, the tie object is also removed.
Tie versus inheritance The tie approach can be useful where implementations must inherit from an
existing class framework, as often occurs with OODB systems. In this case,
you can create object implementations only with the tie approach.
Otherwise, the tie approach has several drawbacks:
• Because the tie approach requires two C++ instances for each
CORBA object, it uses up more resources.
• Tie-based servants ignore reference counting; therefore, you should not
use the tie approach for multi-threaded servers.
• The tie approach adds an unnecessary layer of complexity to
application code.
In general, unless you have a compelling reason to use the tie approach, you
should favor the inheritance approach in your code.
289
CHAPTER 9 | Developing a Server
Implementation Inheritance
IDL inheritance does not constrain your options for implementing servant
classes. In Figure 12, shaded classes represent the skeleton abstract base
classes generated by the IDL compiler; non-shaded classes represent the
servant classes that you provide
POA_BankDemo::Account
AccountImpl POA_BankDemo::CheckingAccount
CheckingAccountImpl
290
Interface Inheritance
Interface Inheritance
You can choose not to derive CheckingAccountImpl() from AccountImpl().
If all methods in POA_BankDemo::CheckingAccount are defined as pure
virtual, then CheckingAccountImpl must implement the methods that it
inherits from POA_BankDemo::Account, as well as those inherited from
POA_BankDemo::CheckingAccount, as shown in Figure 13
POA_BankDemo::Account
AccountImpl POA_BankDemo::CheckingAccount
CheckingAccountImpl
Figure 13: A servant class can implement operations of all base skeleton
classes.
291
CHAPTER 9 | Developing a Server
Multiple Inheritance
Implementation and interface inheritance extend to multiple inheritance. In
Figure 14, solid arrows indicate inheritance that is mandated by the C++
mapping. The dotted arrows indicate that the servants allow either
implementation or interface inheritance.
Given this hierarchy, it is also possible to leave POA_BankDemo::Account
without an implementation, inasmuch as it is an IDL abstract base class. In
this case, CheckingAccountImpl and SavingsAccountImpl must provide the
required virtual method implementations.
POA_BankDemo::Account
AccountImpl
POA_BankDemo::SavingsAccount POA_BankDemo::CheckingAccount
SavingsAccountImpl CheckingAccountImpl
POA_BankDemo::NOWAccount
NOWAccountImpl
Figure 14: Inheritance options among servant and base skeleton classes.
292
Explicit Event Handling
293
CHAPTER 9 | Developing a Server
Termination Handler
Orbix provides its own IT_TerminationHandler class, which enables server
applications to handle delivery of Ctrl-C and similar events in a portable
manner. On UNIX, the termination handler handles the following signals:
SIGINT
SIGTERM
SIGQUIT
On Windows, the termination handler is just a wrapper around
SetConsoleCtrlHandler, which handles delivery of the following control
events:
CTRL_C_EVENT
CTRL_BREAK_EVENT
CTRL_SHUTDOWN_EVENT
CTRL_LOGOFF_EVENT
CTRL_CLOSE_EVENT
You can create only one termination handler object in a program.
Example In the following example, the main routine creates a termination handler
object on the stack. On POSIX platforms, it is critical to create this object in
the main thread before creation of any other thread, especially before calling
ORBinit(). The IT_TerminationHandler destructor deregisters the callback,
in order to avoid calling it during static destruction.
static void
termination_handler_callback(
long signal
)
{
int
main(int argc, char** argv)
{
IT_TerminationHandler
termination_handler(termination_handler_callback);
}
294
Termination Handler
cout << "Processing shutdown signal " << signal << endl;
if (!CORBA::is_nil(orb))
{
cout >> "ORB shutdown ... " << flush;
orb->shutdown(IT_FALSE);
cout << "done." << endl;
}
}
295
CHAPTER 9 | Developing a Server
296
CHAPTER 10
Managing Server
Objects
A portable object adapter, or POA, maps CORBA objects to
language-specific implementations, or servants, in a server
process. All interaction with server objects takes place via the
POA.
A POA identifies objects through their object IDs, which are encapsulated
within the object requests that it receives. Orbix views an object as active
when its object ID is mapped to a servant; the servant is viewed as
incarnating that object. By abstracting an object’s identity from its
implementation, a POA enables a server to be portable among different
implementations.
In this chapter This chapter shows how to create and manage a POA within a server
process, covering the following topics:
297
CHAPTER 10 | Managing Server Objects
298
Mapping Objects to Servants
Skeleton
POA
A A
Object ID Servant
Client request
Server
Figure 15: A portable object adapter (POA) maps abstract objects to their
concrete implementations (servants)
299
CHAPTER 10 | Managing Server Objects
Mapping options A POA can map between objects and servants in several ways:
• An active object map retains object-servant mappings throughout the
lifetime of its POA, or until an object is explicitly deactivated. Before a
POA is activated, it can anticipate incoming requests by mapping
known objects to servants, and thus facilitate request processing.
• A servant manager maps objects to servants on demand, either on the
initial object request, or on every request. Servant managers can
enhance control over servant instantiation, and help avoid or reduce
the overhead incurred by a static object-servant mapping.
• A single default servant can be used to handle all object requests. A
POA that uses a default servant incurs the same overhead no matter
how many objects it processes.
Depending on its policies, a POA can use just one object-mapping method,
or several methods in combination. For more information, see “Enabling the
Active Object Map” on page 309.
300
Creating a POA
Creating a POA
All server processes in a location domain use the same root POA, which you
obtain by calling resolve_initial_references("POA"). The root POA has
predefined policies which cannot be changed (see page 307). Within each
server process, the root POA can spawn one or more child POAs. Each child
POA provides a unique namespace; and each can have its own set of
policies, which determine how the POA implements and manages
object-servant mapping. Further, each POA can have its own POA manager
and servant manager.
Using multiple POAs A number of objectives can justify the use of multiple POAs within the same
server. These include:
• Partition the server into logical or functional groups of servants. You
can associate each group with a POA whose policies conform with the
group’s requirements. For example, a server that manages Customer
and Account servants can provide a different POA for each set of
servants.
You can also group servants according to common processing
requirements. For example, a POA can be configured to generate
object references that are valid only during the lifespan of that POA, or
across all instantiations of that POA and its server. POAs thus offer
built-in support for differentiating between persistent and transient
objects.
• Independently control request processing for sets of objects. A POA
manager’s state determines whether a POA is active or inactive; it also
determines whether an active POA accepts incoming requests for
processing, or defers them to a queue (see “Processing Object
Requests” on page 310). By associating POAs with different
managers, you can gain finer control over object request flow.
• Choose the method of object-servant binding that best serves a given
POA. For example, a POA that processes many objects can map all of
them to the same default servant, incurring the same overhead no
matter how many objects it processes.
301
CHAPTER 10 | Managing Server Objects
302
Creating a POA
Creating Policy objects The PortableServer::POA interface provides factories to create CORBA
Policy object types (see Table 12 on page 304). If a Policy object type is
proprietary to Orbix, you must create the Policy object by calling
create_policy() on the ORB (see “Setting proprietary policies for a POA”
on page 305). In all cases, you attach the PolicyList object to the new POA.
All policies that are not explicitly set in the PolicyList are set to their
defaults.
For example, the following code creates policy objects of PERSISTENT and
USER_ID:
CORBA::PolicyList policies;
policies.length (2);
policies[0] = poa–>create_lifespan_policy
(PortableServer::PERSISTENT)
policies[1] = poa–>create_id_assignment_policy
(PortableServer::USER_ID)
With the PERSISTENT policy, a POA can create object references that remain
valid across successive instantiations of this POA and its server process. The
USER_ID policy requires the application to autoassign all object IDs for a
POA.
Attaching policies to a POA After you create a PolicyList object, you attach it to a new POA by supplying
it as an argument to create_POA(). The following code creates POA
persistentPOA as a child of the root POA, and attaches to it the PolicyList
object just shown:
303
CHAPTER 10 | Managing Server Objects
In general, POA policies let you differentiate among various POAs within the
same server process, where each POA is defined in a way that best
accommodates the needs of the objects that it processes. For example, a
server process that contains the POA persistentPOA might also contain a
POA that supports only transient object references, and only handles
requests for callback objects.
Note: Orbix automatically removes policy objects when they are no longer
referenced by any POA.
POA Policy factories The PortableServer::POA interface contains factory methods for creating
CORBA Policy objects:
304
Creating a POA
Setting proprietary policies for a Orbix provides several proprietary policies to control POA behavior. To set
POA these policies, call create_policy() on the ORB to create Policy objects
with the desired policy value, and add these objects to the POA’s PolicyList.
For example, Orbix provides policies that determine how a POA handles
incoming requests for any object as it undergoes deactivation. You can
specify a DISCARD policy for a POA so it discards all incoming requests for
deactivating objects:
CORBA::PolicyList policies;
policies.length (1);
CORBA::Any obj_deactivation_policy_value;
obj_deactivation_policy_value <<= IT_PortableServer::DISCARD;
policies[0] = orb->create_policy(
( IT_PortableServer::OBJECT_DEACTIVATION_POLICY_ID,
obj_deactivation_policy_value );
Orbix-proprietary policies You can attach the following Orbix-proprietary Policy objects to a POA’s
PolicyList:
305
CHAPTER 10 | Managing Server Objects
306
Creating a POA
307
CHAPTER 10 | Managing Server Objects
In this section The following sections describe POA policies and setting options:
308
Using POA Policies
RETAIN: The POA retains active servants in its active object map.
NON_RETAIN: The POA has no active object map. For each request, the
POA relies on the servant manager or default servant to map between an
object and its servant; all mapping information is destroyed when request
processing returns. Thus, a NON_RETAIN policy also requires that the POA
have a request processing policy of USE_DEFAULT_SERVANT or
USE_SERVANT_MANAGER (see “Processing Object Requests” on page 310).
Servant manager and servant If a POA has a policy of USE_SERVANT_MANAGER, its servant retention policy
retention policy determines whether it uses a servant activator or servant locator as its
servant manager. A RETAIN policy requires the use of a servant activator; a
NON_RETAIN policy requires the use of a servant locator. For more
information about servant managers, see Chapter 11.
309
CHAPTER 10 | Managing Server Objects
310
Using POA Policies
311
CHAPTER 10 | Managing Server Objects
PERSISTENT: Object references can outlive the POA in which they are
created.
Transient object references When a POA creates an object reference, it encapsulates it within an IOR. If
the POA has a TRANSIENT policy, the IOR contains the server process’s
current location—its host address and port. Consequently, that object
reference is valid only as long as the server process remains alive. If the
server process dies, the object reference becomes invalid.
Persistent object references If the POA has a PERSISTENT policy, the IOR contains the address of the
location domain’s implementation repository, which maps all servers and
their POAs to their current locations. Given a request for a persistent object,
the location daemon uses the object’s “virtual” address first, and looks up
the server process’s actual location via the implementation repository.
Direct persistence Occasionally, you might want to generate persistent object references that
avoid the overhead of using the location daemon. In this case, Orbix
provides the proprietary policy of DIRECT_PERSISTENCE. A POA with policies
of PERSISTENT and DIRECT_PERSISTENCE generates IORs that contain a
well-known address list for the server process.
312
Using POA Policies
A POA that uses direct persistence must also indicate where the
configuration sets the well-known address list to be embedded in object
references. In order to do this, two requirements apply:
• The configuration must contain a well-known address configuration
variable, with this syntax:
prefix:transport:addr_list=[ address-spec [,...] ]
• The POA must have a WELL_KNOWN_ADDRESSING_POLICY whose value is
set to prefix.
For example, you might create a well-known address configuration variable
in name scope MyConfigApp as follows:
MyConfigApp {
...
wka:iiop:addr_list=["host.com:1075"];
...
}
Given this configuration, a POA is created in the ORB MyConfigApp can have
its PolicyList set so it generates object references that use direct persistence,
as follows:
CORBA::PolicyList policies;
policies.length (4);
CORBA::Any persistence_mode_policy_value;
CORBA::Any well_known_addressing_policy_value;
persistence_mode_policy_value
<<= IT_PortableServer::DIRECT_PERSISTENCE;
well_known_addressing_policy_value <<=
CORBA::Any::from_string("wka", IT_TRUE);
policies[0] = poa–>create_lifespan_policy
(PortableServer::PERSISTENT);
policies[1] = poa–>create_id_assignment_policy
(PortableServer::USER_ID);
policies[2] = orb->create_policy(
( IT_PortableServer::PERSISTENCE_MODE_POLICY_ID,
persistence_mode_policy_value );
policies[3] = orb->create_policy(
IT_CORBA::WELL_KNOWN_ADDRESSING_POLICY_ID,
well_known_addressing_policy_value );
313
CHAPTER 10 | Managing Server Objects
Object lifespan and ID assignment A POA’s lifespan and ID assignment policies have dependencies upon one
another.
TRANSIENT and SYSTEM_ID are the default settings for a new POA, becuase
system-assigned IDs are sufficient for transient object references. The
appication does not need tight control over the POA’s ID becuase the POA’s
object reference is only valid for the POA’s current incarnation.
However, PERSISTENT and USER_ID policies are usually set together,
because applications require explicit control over the object IDs of its
persistent object references. When using persistent object references the
POA’s name is part of the information used to resolve an object’s IOR. For
this reason, there is a possibility of conflicts when using multiple POA’s with
the same name and a lifespan policy of PERSISTENT. This is particularly true
when using indirect persistent IORs.
314
Using POA Policies
SYSTEM_ID: The POA generates and assigns IDs to its objects. Typically, a
POA with a SYSTEM_ID policy manages objects that are active for only a
short period of time, and so do not need to outlive their server process. In
this case, the POA also has an object lifespan policy of TRANSIENT. Note,
however, that system-generated IDs in a persistent POA are unique across
all instantiations of that POA.
USER_ID: The application assigns object IDs to objects in this POA. The
application must ensure that all user-assigned IDs are unique across all
instantiations of the same POA.
USER_ID is usually assigned to a POA that has an object lifespan policy of
PERSISTENT—that is, it generates object references whose validity can span
multiple instantiations of a POA or server process, so the application
requires explicit control over object IDs.
315
CHAPTER 10 | Managing Server Objects
UNIQUE_ID: Each servant in the POA can be associated with only one
object ID.
316
Using POA Policies
Activating Objects
A POA’s activation policy determines whether objects are explicitly or
implicitly associated with servants. If a POA is enabled for explicit
activation, you activate an object by calling activate_object() or
activate_object_with_id() on the POA. A POA that supports implicit
activation allows the server application to call the _this() function on a
servant to create an active object (see “Implicit Object Activation” on
page 320).
The activation policy determines whether the POA supports implicit
activation of servants.
Specify the POA’s activation policy by supplying one of these arguments:
317
CHAPTER 10 | Managing Server Objects
Default work queues Orbix maintains for each ORB two default work queues, one manual and the
other automatic. Depending on its thread policy, a POA that lacks its own
work queue uses one of the default work queues to process requests:
• A POA with a threading policy of SINGLE_THREAD_MODEL uses the
manual work queue. To remove requests from the manual work queue,
you must call either ORB::perform_work() or ORB::run() within the
main thread.
• A POA with a threading policy of ORB_CTRL_MODEL uses the automatic
work queue. Requests are automatically removed from this work
queue; however, because ORB::run() blocks until the ORB shuts
down, an application can call this method to detect when shutdown is
complete.
Both threading policies assume that the ORB and the application are using
compatible threading synchronization. All uses of the POA within the server
must conform to its threading policy.
For information about creating a POA workqueue, see page 327.
318
Explicit Object Activation
319
CHAPTER 10 | Managing Server Objects
320
Implicit Object Activation
interface Whatever {
Whatever get_self();
};
Whatever_ptr
WhateverImpl::get_self() throw(CORBA::SystemException)
{
return _this(); // Return reference to self
}
321
CHAPTER 10 | Managing Server Objects
class ServantBase {
public:
virtual POA_ptr _default_POA();
// ...
};
Servant inheritance of All skeleton classes and the servants that implement them derive from
_default_POA() implementation ServantBase, and therefore inherit its implementation of _default_POA().
The inherited _default_POA() always returns the root POA. Thus, calling
_this() on a servant that does not override _default_POA() returns a
transient object reference that points back to the root POA. All invocations
on that object are processed by the root POA.
As seen earlier, an application typically creates its own POAs to manage
objects and client requests. For example, to create and export persistent
object references, you must create a POA with a PERSISTENT lifespan policy
and use it to generate the desired object references. If this is the case, you
must be sure that the servants that incarnate those objects also override
_default_POA(); otherwise, calling _this() on those servants returns
transient object references whose mappings to servants are handled by the
root POA.
322
Implicit Object Activation
Overriding _default_POA() To ensure that _this() uses the right POA to generate object references, an
application’s servants must override the default POA. You can do this three
ways:
323
CHAPTER 10 | Managing Server Objects
//File: it_servant_base_overrides.h
...
class IT_ServantBaseOverrides :
virtual
~IT_ServantBaseOverrides();
virtual PortableServer::POA_ptr
3 _default_POA();
private:
4 PortableServer::POA_var m_poa;
...
};
324
Managing Request Flow
POA manager states A POA manager can be in four different states. The POAManager interface
provides four operations to change the state of a POA manager, as shown in
Table 13.
Holding hold_requests() All incoming requests are queued. If the queue fills to
capacity, incoming requests are returned with an
exception of TRANSIENT.
325
CHAPTER 10 | Managing Server Objects
Inactive deactivate() The POA manager is shutting down and destroying all
POAs that are associated with it. Incoming requests
are rejected with the exception CORBA::OBJ_ADAPTER.
Holding state The POA manager of the root POA is initially in a holding state, as is a new
POA manager. Until you call activate() on a POA’s manager, all requests
sent to that POA are queued. activate() can also reactivate a POA
manager that has reverted to a holding state (due to a hold_requests()
call) or is in a discarding state (due to a discard_requests() call).
If a new POA is associated with an existing active POA manager, it is
unnecessary to call activate(). However, it is generally a good idea to put
a POA manager in a holding state before creating a new POA with it.
The queue for a POA manager that is in a holding state has limited capacity,
so this state should be maintained for a short time only. Otherwise, the
queue is liable to fill to capacity with pending requests. When this happens,
all subsequent requests return to the client with a TRANSIENT exception.
326
Work Queues
Work Queues
Overview Orbix provides two proprietary policies, which allow you to associate a
WorkQueue with a POA and thereby control the flow of incoming requests for
that POA:
// IDL
interface WorkQueue
{
readonly attribute long max_size;
readonly attribute unsigned long count;
boolean is_full();
boolean is_empty();
boolean activate();
boolean deactivate();
boolean owns_current_thread();
void flush();
};
327
CHAPTER 10 | Managing Server Objects
WorkQueue types You can implement your own WorkQueue interface, or use IONA-supplied
WorkQueue factories to create one of two WorkQueue types:
• ManualWorkQueue
• AutomaticWorkQueue
328
Work Queues
ManualWorkQueue
Overview A ManualWorkQueue is a work queue that holds incoming requests until they
are explicitly dequeued. It allows the developer full control over how
requests are processed by the POA.
\\ IDL
interface ManualWorkQueue : WorkQueue
{
boolean dequeue(out WorkItem work, in long timeout);
interface ManualWorkQueueFactory
{
ManualWorkQueue create_work_queue(in long max_size);
};
max_size is the maximum number of work items that the queue can hold. If
the queue becomes full, the transport considers the server to be overloaded
and tries to gracefully close down connections to reduce the load.
329
CHAPTER 10 | Managing Server Objects
How requests are processed Applications that use a ManualWorkQueue must periodically call dequeue()
or do_work() to ensure that requests are processed. The developer is in full
control of time between calls and if the events are processed by multiple
threads or in a single thread. If the developer chooses a multithreaded
processing method, they are responsible for ensuring that the code is thread
safe.
A false return value from either do_work() or dequeue() indicates that the
timeout for the request has expired or that the queue has shut down.
330
Work Queues
AutomaticWorkQueue
Overview An AutomaticWorkQueue is a work queue that feeds a thread pool.
Automatic work queues process requests in the same way that the standard
ORB does; however, it does allow the developer to assign a customized
thread pool to a particular POA. Also, the developer can implement several
automatic work queues to process different types of requests at different
priorities.
// IDL
interface AutomaticWorkQueue : WorkQueue
{
readonly attribute unsigned long threads_total;
readonly attribute unsigned long threads_working;
331
CHAPTER 10 | Managing Server Objects
interface AutomaticWorkQueueFactory
{
AutomaticWorkQueue create_work_queue(
in long max_size,
in unsigned long initial_thread_count,
in long high_water_mark,
in long low_water_mark);
AutomaticWorkQueue create_work_queue_with_thread_stack_size(
in long max_size,
in unsigned long initial_thread_count,
in long high_water_mark,
in long low_water_mark,
in long thread_stack_size);
};
max_size is the maximum number of work items that the queue can hold.
To specify an unlimited queue size, supply a value of -1.
low_water_mark lets the ORB remove idle threads from the thread pool,
down to the value of low_water_mark. The number of available threads is
never less than this value.
If you wish to have greater control of the size of the work queue’s thread
stack, use create_work_queue_with_thread_stack(). It adds one
argument, thread_stack_size, to the end of the argument list. This
argument specifies the size of the workqueues thread stack.
332
Work Queues
How requests are processed Applications that use an AutomaticWorkQueue do not need to explicitly
dequeue work items; instead, work items are automatically dequeued and
processed by threads in the thread pool.
If all threads are busy and the number of threads is less than
high_water_mark, the ORB can start additional threads to process items in
the work queue, up to the value of high_water_mark. If the number of
threads is equal to high_water_mark and all are busy, and the work queue
is filled to capacity, the transport considers the server to be overloaded and
tries to gracefully close down connections to reduce the load.
333
CHAPTER 10 | Managing Server Objects
Using a WorkQueue
Creating the WorkQueue To create a POA with a WorkQueue policy, follow these steps:
1. Create a work queue factory by calling
resolve_initial_references() with the desired factory type by
supplying an argument of IT_AutomaticWorkQueueFactory or
IT_ManualWorkQueueFactory.
2. Set work queue parameters.
3. Create the work queue by calling create_work_queue() on the work
queue factory.
4. Insert the work queue into an Any.
5. Add a work queue policy object to a POA’s PolicyList.
Example 16 illustrates these steps:
// create PolicyList
CORBA::PolicyList policies;
policies.length(1);
334
Work Queues
Processing events in a manual When using a manual work queue, the developer must implement the loop
work queue which removes requests from the queue.
Example 17 demonstrates one way to remove requests from a manual work
queue. The code loops indefinitely and continuously polls the queue for
requests. When there are requests on the queue, they are removed from the
queue using the dequeue() method and then they processed with the
execute() method of the WorkItem object returned from dequeue().
WorkQueue::WorkItem work_item;
while (1)
{
if (wq->is_empty())
{
// Since there are no requests to process
// the object can sleep, or do whatever other work
// the developer needs done.
....
}
else
{
manual_work_queue->dequeue(work_item, 5000);
work_item->execute();
// no need to explicitly destroy as execute deletes the
// work item once completed.
}
}
Alternatively, you remove requests from the queue using the do_work()
method. The difference is that using do_work() you can process several
requests at one time.
335
CHAPTER 10 | Managing Server Objects
Processing events in an automatic Automatic work queues handle request processing under the covers.
work queue Therefore, the developer does not need to implement any request handling
logic.
336
Controlling POA Proxification
Policy The InterdictionPolicy controls the behavior of the firewall proxy service
plug-in, if it is loaded. The policy has two settings:
ENABLE This is the default behavior of the firewall proxy service
plug-in. A POA with its InterdictionPolicy set to
ENABLE will be proxified.
DISABLE This setting tells the firewall proxy service plug-in to not
proxify the POA. A POA with its InterdictionPolicy set
to DISABLE will not use the firewall proxy service and
requests made on objects under its control will come
directly from the requesting clients.
337
CHAPTER 10 | Managing Server Objects
C++
#include <orbix/fps.hh>
CORBA::PolicyList policies(1);
policies.length(1);
policies[0] =
m_orb->create_policy(IT_FPS::INTERDICTION_POLICY_ID,
interdiction);
338
CHAPTER 11
Managing
Servants
A POA that needs to manage a large number of objects can be
configured to incarnate servants only as they are needed.
Alternatively, a POA can use a single servant to service all
requests.
A POA’s default request processing policy is USE_ACTIVE_OBJECT_MAP_ONLY.
During POA initialization, the active object map must be populated with all
object-servant mappings that are required during the POA’s lifetime. The
active object map maintains object-servant mappings until the POA shuts
down, or an object is explicitly deactivated.
For example, you might implement the BankDemo::Account interface so that
at startup, a server instantiates a servant for each account and activates all
the account objects. Thus, a servant is always available for any client
invocation on that account—for example, balance() or withdraw().
Drawbacks of active object map Given the potential for many thousands of accounts, and the likelihood that
usage account information changes—accounts are closed down, new accounts are
created—the drawbacks of this static approach become obvious:
• Code duplication: For each account, the same code for servant creation
and activation must be repeated, increasing the potential for errors.
• Inflexibility: For each change in account information, you must modify
and recompile the server code, then stop and restart server processes.
339
CHAPTER 11 | Managing Servants
• Startup time: The time required to create and activate a large number
of servants prolongs server startup and delays its readiness to process
client requests.
• Memory usage: An excessive amount of memory might be required to
maintain all servants continuously.
This scenario makes it clear that you should usually configure a POA to rely
exclusively on an active object map only when it maintains a small number
of objects.
Policies for managing many If a POA is required to maintain a large number of objects, you should set its
objects request processing policy to one of the following:
• USE_SERVANT_MANAGER specifies that servants are instantiated on
demand.
• USE_DEFAULT_SERVANT specifies a default servant that handles requests
for any objects that are not registered in the active object map, or for
all requests in general.
This chapter shows how to implement both policies.
340
Using Servant Managers
Registering a servant manager An application registers its servant manager —whether activator or
locator— with the POA by calling set_servant_manager() on it; otherwise,
an OBJ_ADAPTER exception is returned to the client on attempts to invoke on
one of its objects.
The following sections show how to implement the BankDemo::Account
interface with a servant activator and a servant locator. Both servant
manager types activate account objects with instantiations of servant class
SingleAccountImpl, which inherits from skeleton class
POA_BankDemo::Account:
341
CHAPTER 11 | Managing Servants
class SingleAccountImpl :
public POA_BankDemo::Account
{
public:
SingleAccountImpl(
const char* account_id,
AccountDatabase& account_db
);
~SingleAccountImpl();
BankDemo::CashAmount balance()
throw(CORBA::SystemException);
private:
CORBA::String_var m_account_id;
BankDemo::CashAmount m_balance;
AccountDatabase& m_account_db;
};
342
Using Servant Managers
Servant Activators
A POA with policies of USE_SERVANT_MANAGER and RETAIN uses a servant
activator as its servant manager. The POA directs the first request for an
inactive object to the servant activator. If the servant activator returns a
servant, the POA associates it with the requested object in the active object
map and thereby activates the object. Subsequent requests for the object
are routed directly to its servant.
1
servant
Initial object requests are activator
directed to servant activator
servant-object ID
mappings
3 object IDs
Subsequent requests on
activated objects
are routed through
the active
active object
object map map
POA
Figure 16: On the first request on an object, the servant activator returns a
servant to the POA, which establishes the mapping in its active object
map.
343
CHAPTER 11 | Managing Servants
Servant activators are generally useful when a server can hold all its
servants in memory at once, but the servants are slow to initialize, or they
are not all needed each time the server runs. In both cases, you can
expedite server startup by deferring servant activation until it is actually
needed.
void
etherealize(
in ObjectId oid,
in POA adapter,
in Servant serv,
in boolean cleanup_in_progress,
in boolean remaining_activations
;
};
344
Using Servant Managers
#include <omg/PortableServerS.hh>
#include "account_db.h"
class AccountServantActivatorImpl :
public PortableServer::ServantActivator,
public CORBA::LocalObject
{
public:
AccountServantActivatorImpl(AccountDatabase& account_db);
PortableServer::Servant incarnate(
const PortableServer::ObjectId & oid,
PortableServer::POA_ptr adapter
) throw(CORBA::SystemException,
PortableServer::ForwardRequest);
void etherealize(
const PortableServer::ObjectId & oid,
PortableServer::POA_ptr adapter,
PortableServer::Servant serv,
CORBA::Boolean cleanup_in_progress,
CORBA::Boolean remaining_activations
) throw(CORBA::SystemException);
Activating objects incarnate() instantiates a servant for a requested object and returns the
servant to the POA. The POA registers the servant with the object’s ID,
thereby activating the object and making it available to process requests on
it.
345
CHAPTER 11 | Managing Servants
PortableServer::Servant
1 AccountServantActivatorImpl::incarnate(
const PortableServer::ObjectId & oid,
PortableServer::POA_ptr adapter
) throw(CORBA::SystemException, PortableServer::ForwardRequest)
{
CORBA::String_var account_id =
PortableServer::ObjectId_to_string(oid);
Deactivating objects The POA calls etherealize() when an object deactivates, either because
the object is destroyed or as part of general cleanup when the POA itself
deactivates or is destroyed.
The following implementation of etherealize() checks the
remaining_activations parameter to ensure that the servant does not
incarnate another object before it deletes the servant. Implementations can
346
Using Servant Managers
void
AccountServantActivatorImpl::etherealize(
const PortableServer::ObjectId & oid,
PortableServer::POA_ptr poa,
PortableServer::Servant servant,
CORBA::Boolean cleanup_in_progress,
CORBA::Boolean remaining_activations
) throw((CORBA::SystemException))
{
if (remaining_activations == 0)
delete serv;
}
Setting deactivation policies By default, a POA that uses a servant activator lets an object deactivate
(and its servant to etherealize) only after all pending requests on that object
return. You can modify the way the POA handles incoming requests for a
deactivating object by creating an Orbix-proprietary
ObjectDeactivationPolicy object and attaching it to the POA’s PolicyList
(see “Setting proprietary policies for a POA” on page 305).
Three settings are valid for this Policy object:
DELIVER: (default) The object deactivates only after processing all pending
requests, including any requests that arrive while the object is deactivating.
This behavior complies with CORBA specifications.
HOLD: Requests block until the object deactivates. A POA with a HOLD
policy maintains all requests until the object reactivates. However, this
policy can cause deadlock if the object calls back into itself.
347
CHAPTER 11 | Managing Servants
Setting a POA’s servant activator The following example shows how you can establish a POA’s servant
activator in two steps:
...
AccountDatabase account_database = new AccountDatabase();
348
Using Servant Managers
Servant Locators
A server that needs to manage a large number of objects might only require
short-term access to them. For example, the operations that are likely to be
invoked on most customer bank accounts—such as withdrawals and
deposits—are usually infrequent and of short duration. Thus, it is
unnecessary to keep account objects active beyond the lifetime of any given
request. A POA that services requests like this can use a servant locator,
which activates an object for each request, and deactivates it after the
request returns.
POA servant
object locator
{
request preinvoke()
operation()
postinvoke() servant
{
preinvoke() servant
object
request operation()
postinvoke()
Figure 17: The POA directs each object request to the servant locator,
which returns a servant to the POA to process the request.
349
CHAPTER 11 | Managing Servants
Controlling servant lifespan An application that uses a servant locator has full control over servant
creation and deletion, independently of object activation and deactivation.
Your application can assert this control in a number of ways. For example:
• Servant caching: A servant locator can manage a cache of servants for
applications that have a large number of objects. Because the locator
is called for each operation, it can determine which objects are
requested most recently or frequently and retain and remove servants
accordingly.
• Application-specific object map: A servant locator can implement its
own object-servant mapping algorithm. For example, a POA’s active
object map requires a unique servant for each interface. With a servant
locator, an application can implement an object map as a simple fixed
table that maps multiple objects with different interfaces to the same
servant. Objects can be directed to the appropriate servant through an
identifier that is embedded in their object IDs. For each incoming
request, the servant locator extracts the identifier from the object ID
and directs the request to the appropriate servant.
void
postinvoke(
in ObjectId oid,
in POA adapter,
in CORBA::Identifier operation,
in Cookie the_cookie,
in Servant the_servant
;
};
350
Using Servant Managers
Implementing a servant locator The following code defines a servant locator that handles account objects:
class AccountServantLocatorImpl :
public PortableServer::ServantLocator,
public CORBA::LocalObject
{
public:
AccountServantLocatorImpl(AccountDatabase& account_db);
public:
PortableServer::Servant preinvoke(
const PortableServer::ObjectId &id,
PortableServer::POA_ptr poa,
const char *operation,
PortableServer::Cookie &cookie )
throw( CORBA::SystemException );
void postinvoke (
const PortableServer::ObjectId &id,
PortableServer::POA_ptr poa,
const char *operation,
PortableServer::Cookie &cookie,
PortableServer::Servant the_servant )
throw(CORBA::SystemException);
351
CHAPTER 11 | Managing Servants
the_cookie lets you explicitly map data between preinvoke() and its
corresponding postinvoke() call. This can be useful in a multi-threaded
environment and in transactions where it is important to ensure that a pair
of preinvoke() and postinvoke() calls operate on the same servant. For
example, each preinvoke() call can set its the_cookie parameter to data
that identifies its servant; the postinvoke() code can then compare that
data to its the_servant parameter.
operation contains the name of the operation that is invoked on the CORBA
object, and thus provides the context of the servant’s instantiation. The
servant can use this to differentiate between different operations and
execute the appropriate code.
Incarnating objects with a servant The following implementation of preinvoke()is functionally identical to the
locator incarnate() implementation shown in Example 19.
PortableServer::Servant
MyAcctLocator::preinvoke(
const PortableServer::ObjectID &id,
PortableServer::POA_ptr poa
const char *operation
PortableServer::Cookie &cookie )
throw( CORBA::SystemException )
{
CORBA::String_var str =
PortableServer::ObjectId_to_string(id);
if (acctId == -1)
throw CORBA::OBJECT_NOT_EXIST ();
352
Using Servant Managers
PortableServer::Servant
MyAcctLocator::postinvoke(
const PortableServer::ObjectID &id,
PortableServer::POA_ptr poa,
const char *operation,
PortableServer::Cookie &cookie,
PortableServer::Servant the_servant )
throw( CORBA::SystemException )
{
delete servant;
}
Setting a POA’s servant locator You establish a POA’s servant locator in two steps, as shown in the
following example:
1 AccountServantLocatorImpl locator_impl(account_database);
353
CHAPTER 11 | Managing Servants
Obtaining the current object A default servant must be able to differentiate the objects that it is serving.
The PortableServer::Current interface offers this capability:
module PortableServer
{
interface Current : CORBA::Current
{
exception NoContext{};
POA get_POA () raises (NoContext);
ObjectID get_object_id() raises (NoContext);
};
...
}
Implementing a default servant To implement a default servant for account objects, modify the code as
follows:
• The SingleAccountImpl constructor identifies the ORB instead of an
object’s account ID.
354
Using a Default Servant
public:
// constructor
SingleAccountImpl (CORBA::ORB_ptr orb) : orb_ (orb) {}
private:
char *get_acct_id(void){
CORBA::Object_var obj =
orb_->resolve_initial_references("POACurrent");
PortableServer::Current_var cur =
PortableServer::Current::_narrow(obj);
try {
PortableServer::ObjectID_var id =
cur->get_object_id();
return PortableServer::ObjectID_to_string(id);
} catch (const PortableServer::Current::NoContext &) {
cerr << "NoContext error" << endl;
}
}
}
355
CHAPTER 11 | Managing Servants
356
Using a Default Servant
357
CHAPTER 11 | Managing Servants
358
Creating Inactive Objects
AccountServantLocatorImpl locator_impl(account_database);
// Start ORB
orb->run();
return 0;
}
359
CHAPTER 11 | Managing Servants
4. After the request returns from processing, the servant locator destroys
its servant.
360
CHAPTER 12
Asynchronous
Method
Invocations
Orbix support for asynchronous method invocations allows a
client to continue other work while it awaits responses from
previous requests.
Examples of client implementations in earlier chapters show client
invocations that follow a synchronous two-way model—that is, after a client
sends a request, it blocks on that thread until it receives a reply. If
single-threaded, the client is generally unable to perform any other work
while it awaits a response. This can be unacceptable in an application that
requires clients to issue requests in rapid succession and needs to process
replies as soon as they become available.
Callbacks to reply handlers To avoid this problem, Orbix supports asynchronous method invocations
(AMI) through callbacks to reply handlers. In its invocation, the client
supplies an object reference to the appropriate reply handler. When it is
ready to reply, the server invokes on this object reference. The client ORB
dispatches the invocation to the reply handler servant.
361
CHAPTER 12 | Asynchronous Method Invocations
In most cases, AMI usage affects only client implementations; servers are
unaware that an invocation is synchronous or asynchronous. Client
asynchrony matters only to transactional servers, and in this case can
require changes to the server.
Example IDL The examples in this chapter use the following IDL, which queries banking
institutions for current lending rates:
module LoanSearch
{
// nonexistent Bank
exception InvalidBank{};
// invalid loan type
exception InvalidLoanType{};
interface LoanRates{
float get_loan_rate(
in string bank_name,
in string loan_type
) raises (InvalidBank, InvalidLoanType);
};
// ...
};
362
static const char *loan_types[] =
{
"AUTO",
"MORTGAGE",
"EQUITY",
"PERSONAL",
"BUSINESS",
// ...
}
363
CHAPTER 12 | Asynchronous Method Invocations
Implied IDL
In order to support AMI, the IDL compiler provides the -xAMICallbacks
option. This generates an implied IDL sendc_ operation for each interface
operation and attribute, which supports AMI callbacks. You must supply the
-xAMICallbacks modifier with both -base and -poa switches, as in the
following example:
IDL -poa:-xAMICallbacks -base:-xAMICAllbacks LoanSearch.idl
For example, given the get_loan_rate() operation, the IDL compiler
generates an implied IDL sendc_get_loan_rate() operation that it adds to
the LoanRates interface. The compiler then generates stub and skeleton
code from the entire set of explicit and implicit IDL.
Mapping operations to implied In general, each in and inout parameter in an IDL operation is mapped to
IDL an argument of the same name and type in the corresponding sendc_
operation. sendc_ operations return void and supply as their first argument
an object reference to the client-implemented reply handler. They have the
following syntax
void sendc_op-name(
reply-hdlr-ref,
[ type argument[,type argument]... );
Mapping attributes to implied IDL Each IDL attribute is mapped to a sendc_get_ operation which takes an
object reference to its reply handler. If the attribute is not read-only, the IDL
compiler also generates a sendc_set_ operation, which takes an addition
argument of the same name and type as the attribute.
sendc_get_ and sendc_set_ operations return void and supply as their first
argument an object reference to the client-implemented reply handler. They
have the following syntax:
void sendc_get_attribute-name( reply-hdlr-ref );
void sendc_set_attribute-name(
reply-hdlr-ref,
type attribute-name);
364
Calling Back to Reply Handlers
365
CHAPTER 12 | Asynchronous Method Invocations
namespace POA_LoanSearch{
class AMI_LoanRatesHandler
: public POA_Messaging::ReplyHandler{
public:
// ...
virtual void get_loan_rate_complete(
CORBA::Float ami_return_val)
IT_THROW_DECL((CORBA::SystemException)) = 0;
// ...
virtual void get_loan_rate_excep(
Messaging::ExceptionHolder* ami_holder)
IT_THROW_DECL((CORBA::SystemException)) = 0;
};
}
366
Calling Back to Reply Handlers
Normal replies A reply handler can contain up to three types of operations to handle normal
replies—that is, replies on invocations that raise no exceptions:
Exceptional replies A reply handler can contain up to three types of operations to handle
exceptional replies:
367
CHAPTER 12 | Asynchronous Method Invocations
368
Calling Back to Reply Handlers
class MyLoanRatesHandler :
public POA_LoanSearch::AMI_LoanRatesHandler{
public:
// handler constructor
MyLoanRatesHandler(const char *bank_name,
const char *loan_type) :
bank_name_(CORBA::string_dup(bank_name)),
loan_type_(CORBA::string_dup(loan_type))
{ }
~MyLoanRatesHandler(void)
{ }
369
CHAPTER 12 | Asynchronous Method Invocations
private:
CORBA::String_var bank_name_, loan_type_ ;
};
370
Calling Back to Reply Handlers
Example 27:
1 void get_latest_rates(
CORBA::ORB_ptr,
LoanSearch::LoanRates_ref,
CORBA::String loan_type)
{
// array of pointers to bank reply handlers
MyLoanRatesHandler *handlers[MAX_BANKS];
int i;
371
CHAPTER 12 | Asynchronous Method Invocations
372
CHAPTER 13
Exceptions
Implementations of IDL operations and attributes throw
exceptions to indicate when a processing error occurs.
An IDL operation can throw two types of exceptions:
• User-defined exceptions are defined explicitly in your IDL definitions.
• System exceptions are predefined exceptions that all operations can
throw.
While IDL operations can throw user-defined and system exceptions,
accessor methods for IDL attributes can only throw system-defined
exceptions.
Example IDL This chapter shows how to throw and catch both types of exceptions. The
Bank interface is modified to include two user-defined exceptions:
373
CHAPTER 13 | Exceptions
module BankDemo
{
...
interface Bank {
exception AccountAlreadyExists { AccountId account_id; };
exception AccountNotFound { AccountId account_id; };
Account create_account(
in AccountId account_id,
in CashAmount initial_balance
) raises (AccountAlreadyExists);
};
};
374
Exception Code Mapping
CORBA::Exception
CORBA::SystemException CORBA::UserException
CORBA::TRANSIENT Bank::AccountAlreadyExists
CORBA::OBJ_ADAPTER Bank::AccountNotFound
CORBA::BAD_PARAM
Given this hierarchy, you can catch all CORBA exceptions in a single catch
handler. Alternatively, you can catch system and user exceptions separately,
or handle specific exceptions individually.
375
CHAPTER 13 | Exceptions
User-Defined Exceptions
Operations are defined to raise one or more user exceptions to indicate
application-specific error conditions. An exception definition can contain
multiple data members to convey specific information about the error, if
desired. For example, you might include a graphic image in the exception
data in order to display an error icon.
Exception design guidelines When you define exceptions, be sure to follow these guidelines:
Exceptions carry complete information. Ensure that exceptions carry all the
data the caller requires to handle an error. If an exception carries insufficient
information, the caller must make a second call to retrieve the missing
information. However, if the first call fails, it is likely that subsequent calls
will also fail.
376
User-Defined Exceptions
C++ mapping for user exceptions When you run the IDL compiler on IDL interface Bank, it translates user
exceptions into C++ classes. For example, the compiler translates
Bank::AccountAlreadyExists into a C++ class of the same name:
AccountAlreadyExists();
AccountAlreadyExists(const char* _itfld_account_id);
...
// string manager
ITGenAccountId_mgr account_id;
377
CHAPTER 13 | Exceptions
Handling Exceptions
Overview Client code uses standard try and catch blocks to isolate processing logic
from exception handling code. You can associate multiple catch blocks with
each try block. You should write the code so that handling for specific
exceptions takes precedence over handling for other unspecified exceptions.
378
Handling Exceptions
void
BankMenu::do_create()
throw(CORBA::SystemException)
{
cout << "Enter account name: " << flush;
char name[1024];
cin >> name;
cout << "Enter starting balance: " << flush;
BankDemo::CashAmount amount;
cin >> amount;
379
CHAPTER 13 | Exceptions
catch (
const BankDemo::Bank::AccountAlreadyExists& already_exists)
{
cout << "Account already exists: "
<< already_exists.account_id << endl;
}
}
380
Handling Exceptions
Precedence of exception handlers The handler for a specific system exception must appear before the handler
for CORBA::SystemException. C++ catch clauses are attempted in the
order specified, and the first matching handler is called. Because of implicit
casting, a handler for CORBA::SystemException matches all system
exceptions (all system exception classes are derived from class
CORBA::SystemException), so it should appear after all handlers for specific
system exceptions.
If you want to know the type of system exception that occurred, use the
message output by the proprietary operator<<() function on class
CORBA::SystemException. Handlers for individual system exceptions are
necessary only when they require a specific action.
The following client code specifically tests for a COMM_FAILURE exception; it
can also handle any other system exceptions:
void
BankMenu::run() {
// make sure bank reference is valid
if (CORBA::is_nil(m_bank)) {
cout << "Cannot proceed - bank reference is nil";
}
else {
// loop printing the menu and executing selections
for ( ; ; ) {
cout << endl;
cout << "0 - quit" << endl;
cout << "1 - create_account" << endl;
cout << "2 - find_account" << endl;
cout << "Selection [0-2]: " << flush;
int selection;
cin >> selection;
381
CHAPTER 13 | Exceptions
try {
switch(selection) {
case 0: return;
case 1: do_create(); break;
case 2: do_find(); break;
}
}
catch (CORBA::COMM_FAILURE& e) {
cout << "Communication failure exception: "
<< e << endl;
return;
}
catch (const CORBA::SystemException& e) {
cout << "Unexpected exception: " << e << endl;
return;
}
}
}
}
382
Handling Exceptions
Obtaining invocation completion Each standard exception includes a completion_status code that takes one
status of the following integer values:
Evaluating minor codes minor() returns an IDL unsigned long that offers more detail about the
particular system exception thrown. For example, if a client catches a
COMM_FAILURE system exception, it can access the system exception’s minor
field to determine why this occurred
All standard exceptions have an associated minor code that provides more
specific information about the exception in question. Given these minor
codes, the ORB is not required to maintain an exhaustive list of all possible
exceptions that might arise at runtime.
Minor exception codes are defined as an unsigned long that contains two
components:
• 20-bit vendor minor code ID (VMCID)
• Minor code that occupies the 12 low order bits
383
CHAPTER 13 | Exceptions
All minor codes are based on the vendor minor code ID (IONA_VMCID), which
is 0x49540000. The space reserved ends at 0x49540FFF.
The VMCID assigned to OMG standard exceptions is 0x4f4d000. You can
obtain the minor code value for any exception by OR'ing the VMCID with the
minor code for the exception in question. All minor code definitions are
associated with readable strings.
Subsystem minor codes Orbix defines minor codes within each subsystem. When an exception is
thrown, the current subsystem associates the exception with a valid minor
code that maps to a unique error condition. Table 16 lists Orbix subsystems
and base values for their minor codes:
384
Handling Exceptions
IT_IFR IT_IFR
385
CHAPTER 13 | Exceptions
386
Handling Exceptions
For example, the locator subsystem defines a number of minor codes for the
BAD_PARAM standard exception. These distinguish among the various
conditions under which the locator might throw the BAD_PARAM exception.
Definitions for all subsystem minor codes can be found in the directory
asp/Version/xml/minor_codes in a UNIX System Services installation, and
in the [email protected] PDS in a native z/OS installation.
387
CHAPTER 13 | Exceptions
Throwing Exceptions
Client code uses standard C++ syntax to initialize and throw both
user-defined and system exceptions.
This section modifies BankImpl::create_account() to throw an exception.
You can implement create_account() as follows:
BankDemo::Account_ptr BankImpl::create_account(
const char* account_id,
CashAmount initial_balance) throw(
CORBA::SystemException,
BankDemo::Bank::AccountAlreadyExists)
{
// create new account in database, then return a new
// reference to that account
if (!m_account_db.create_account(account_id,
initial_balance))
{
throw BankDemo::Bank::AccountAlreadyExists(account_id);
}
return create_account_ref(account_id);
}
388
Exception Safety
Exception Safety
You should be careful that your code does not throw user exceptions that are
not part of the operation’s raises expression. Doing so can throw an UNKNOWN
exception, or cause the program to terminate abruptly.
Throwing illegal exceptions For example, the following IDL defines operations some_operation() and
some_helper():
void ExampleImpl::some_operation()
throw(CORBA::SystemException, Failed) {
// do some work...
// call helper operation.
Helper_var help = ...;
help->some_helper(); // BAD!
// do remainder of work...
}
389
CHAPTER 13 | Exceptions
Catching illegal exceptions If an operation calls helper operations on other objects, make sure that it
can handle illegal exceptions. For example, the following example modifies
some_operation() so that it can translate DidntWork into a legal exception:
void ExampleImpl::some_operation()
throw(CORBA::SystemException, Failed) {
// do some work...
// call helper operation.
Helper_var help = ...;
try {
help->some_helper();
}
catch (const DidntWork &) {
throw Failed; // translate into legal exception
}
// do remainder of work...
return;
}
Avoiding resource leaks Be careful also to avoid resource leaks in the presence of exceptions. For
example, the IDL for some_operation() is modified here to return a string as
an out parameter:
void ExampleImpl::some_operation(CORBA::String_out s)
throw(CORBA::SystemException, Failed) {
390
Exception Safety
You can correct this problem by explicitly deallocating the parameter again,
as in the following example:
void ExampleImpl::some_operation(CORBA::String_out s)
throw(CORBA::SystemException, Failed) {
391
CHAPTER 13 | Exceptions
392
CHAPTER 14
393
CHAPTER 14 | Using Type Codes
TCKind enumerators The CORBA::TCKind enumeration defines all built-in IDL types:
// In module CORBA
enum TCKind {
tk_null, tk_void, tk_short, tk_long, tk_ushort, tk_ulong,
tk_float, tk_double, tk_boolean, tk_char, tk_octet, tk_any,
tk_TypeCode, tk_Principal, tk_objref, tk_struct, tk_union,
tk_enum, tk_string, tk_sequence, tk_array, tk_alias,
tk_except, tk_longlong, tk_ulonglong, tk_longdouble,
tk_wchar,
tk_wstring, tk_fixed, tk_value, tk_value_box, tk_native,
tk_abstract_interface
};
394
Type Code Components
TCKind Parameters
395
CHAPTER 14 | Using Type Codes
TCKind Parameters
tk_string max-lengtha
tk_wstring
396
Type Code Operations
397
CHAPTER 14 | Using Type Codes
equal(), equivalent()
boolean equal( in TypeCode tc );
boolean equivalent( in TypeCode tc );
equal() and equivalent() let you evaluate a type code for equality with the
specified type code, returning true if they are the same:
equal() requires that the two type codes be identical in their TCKind and all
parameters—member names, type names, repository IDs, and aliases.
398
Type Code Operations
You must use equal() and equivalent() to evaluate a type code. For
example, the following code is illegal:
CORBA::Any another_any;
another_any <<= "Hello world";
CORBA::TypeCode_ptr t = another_any.type();
CORBA::Any another_any;
another_any <<= "Hello world";
CORBA::TypeCode_ptr t = another_any.type();
get_compact_typecode()
TypeCode get_compact_typecode();
get_compact_typecode() removes type and member names from a type
code. This operation is generally useful only to applications that must
minimize the size of type codes that are sent over the wire.
kind()
TCKind kind();
399
CHAPTER 14 | Using Type Codes
kind() returns the TCKind of the target type code. You can call kind() on a
TypeCode to determine what other operations can be called for further
processing—for example, use the TCKind return as a switch discriminator:
switch(t->kind()){
case CORBA::tk_short:
...
case CORBA::tk_long:
...
// continue for all tk_ values
default:
...
}
Type-Specific Operations
Table 18 shows operations that can be invoked only on certain type codes.
In general, each operation gets information about a specific type-code
parameter. If invoked on the wrong type code, these operations raise an
exception of BadKind.
TCKind Operations
tk_alias id()
name()
content_type()
tk_array length()
content_type()
tk_enum id()
name()
member_count()
member_name()
tk_except id()
name()
member_count()
member_name()
member_type()
400
Type Code Operations
TCKind Operations
tk_fixed fixed_digits()
fixed_scale()
tk_native id()
name()
tk_objref id()
name()
tk_sequence length()
content_type()
tk_string length()
tk_wstring
tk_struct id()
name()
member_count()
member_name()
member_type()
tk_union id()
name()
member_count()
member_name()
member_label()
discriminator_type()
default_index()
tk_value id()
name()
member_count()
member_name()
member_type()
type_modifier()
concerte_base_type()
member_visibility()
tk_value_box id()
name()
member_name()
401
CHAPTER 14 | Using Type Codes
Table 19 briefly describes the information that you can access through type
code-specific operations. For detailed information about these operations,
see the CORBA Programmer’s Reference.
Operation Returns:
402
Type Code Operations
Operation Returns:
403
CHAPTER 14 | Using Type Codes
Built-in type code constants Orbix provides predefined CORBA::TypeCode object reference constants that
let you access type codes for standard types.
CORBA::_tc_any CORBA::_tc_string
CORBA::_tc_boolean CORBA::_tc_ulong
CORBA::_tc_char CORBA::_tc_ulonglong
CORBA::_tc_double CORBA::_tc_ushort
CORBA::_tc_float CORBA::_tc_void
CORBA::_tc_long CORBA::_tc_wchar
CORBA::_tc_longdouble CORBA::_tc_wstring
CORBA::_tc_longlong CORBA::_tc_Object
CORBA::_tc_null CORBA::_tc_TypeCode
CORBA::_tc_octet CORBA::_tc_ValueBase
CORBA::_tc_short
User-defined type code constants The IDL compiler generates type code constants for declarations of these
types:
interface
typedef
struct
union
enum
valuetype
valuebox
404
Type Code Constants
For each user-defined type that is declared in an IDL file, the IDL compiler
generates a CORBA::TypeCode_ptr that points to a type code constant.
These constants have the format _tc_type where type is the user-defined
type. For example, given the following IDL:
interface Interesting {
typedef long longType;
struct Useful
{
longType l;
};
};
405
CHAPTER 14 | Using Type Codes
406
CHAPTER 15
IDL-C++ mapping The IDL any type maps to the C++ CORBA::Any class. Conceptually, this
class contains the following two instance variables:
type is a TypeCode object that provides full type information for the value
contained in the any. The Any class provides a type() method to return the
TypeCode object.
407
CHAPTER 15 | Using the Any Data Type
// IDL
interface AnyDemo {
// Takes in any type that can be specified in IDL
void passSomethingIn (in any any_type_parameter);
...
};
Inserting and Extracting Booleans, Octets, Chars and WChars page 415
408
Inserting Typed Values Into Any
Type-specific insertion operator The C++ class CORBA::Any contains predefined overloaded versions of the
functions insertion operator function operator<<=(). Orbix provides insertion operator
functions for all IDL types that map unambiguously to C++ types, such as
long, float, or unbounded string. For a full listing of these functions and
their data types, refer to CORBA::Any::operator<<=(). The IDL compiler
also generates an insertion operator for each user-defined type.
For example, CORBA::Any contains the following insertion operator function
for short data types:
void operator<<=(CORBA::Short s);
Given this function, you can use the insertion operator to supply a short data
type to passSomethingIn() as follows:
void AnyDemo::do_send_short() {
try {
AnyDemo_var x = ...;
CORBA::Any a;
CORBA::Short toPass;
toPass = 26;
a <<= toPass;
x->passSomethingIn(a);
}
catch (CORBA::SystemException &sysEx) {
...
}
Type safety Insertion operators provide a type-safe mechanism for inserting data into an
any. The type of value to insert determines which insertion operator is used.
Attempts to insert a value that has no corresponding IDL type yield
compile-time errors.
409
CHAPTER 15 | Using the Any Data Type
Memory management of inserted Depending on the type of the data, insertion using an operator<<=() has
data one of the following effects:
• _duplicate() is called on an object reference.
• _add_ref() is called on a valuetype.
• a deep copy is made for all other data types.
When the Any is subsequently destroyed, the Any destructor performs one of
the following actions, depending on the Any.type() field:
• CORBA::release() is called on an object reference.
• _remove_ref() is called on a valuetype.
• delete is called on all other data types.
Inserting user-defined types The IDL shown earlier can be modified to include this typedef declaration:
// IDL
typedef sequence<long> LongSequence;
Given this statement, the IDL compiler generates the following insertion
operator function for LongSequence data types:
void operator<<=(CORBA::Any& a, const LongSequence& t);
Clients that call passSomethingIn() can use the insertion operator to insert
LongSequence data into the function’s any parameter:
void AnyDemo::do_send_sequence() {
try {
CORBA::Any a;
410
Inserting Typed Values Into Any
411
CHAPTER 15 | Using the Any Data Type
Type-specific extraction operator The C++ class CORBA::Any contains predefined overloaded versions of the
functions extraction operator function operator>>=(). Orbix provides extraction
operator functions for all IDL types that map unambiguously to C++ types,
such as long, float, or unbounded string. For a full listing of these
functions and their data types, refer to CORBA::Any::operator>>=(). The
IDL compiler also generates an extraction operator for each user-defined
type.
For example, CORBA::Any contains the following extraction operator function
for short data types:
CORBA::Boolean operator>>=(CORBA::Short& s) const;
Given this function, a server implementation of passSomethingIn() can use
the extraction operator to extract a short from the function’s parameter
anyIn:
CORBA::Short toExtract = 0;
Memory management of extracted When a user-defined type is extracted from an Any, the data is not copied or
data duplicated in any way. The extracted data is, therefore, subject to the
following restrictions:
412
Extracting Typed Values From Any
Extracting user-defined types More complex, user-defined types can be extracted with the extraction
operators generated by the IDL compiler. For example, the IDL shown
earlier can be modified to include this typedef declaration:
// IDL
typedef sequence<long> LongSequence;
Given this statement, the IDL compiler generates the following extraction
operator function for LongSequence data types:
void AnyDemo::do_get_any() {
CORBA::Any_var a;
cout << "Call getSomethingBack" << endl;
a = m_any_demo->getSomethingBack();
LongSequence* extracted_sequence = 0;
413
CHAPTER 15 | Using the Any Data Type
if (a >>= extracted_sequence) {
cout << "returned any contains sequence with value :"
<< endl;
print_sequence(extracted_sequence);
}
else {
cout << "unexpected value contained in any" << endl;
}
}
414
Inserting and Extracting Booleans, Octets, Chars and WChars
CORBA::Any a;
415
CHAPTER 15 | Using the Any Data Type
// Insertion
CORBA::Any a;
a <<= longArray_forany(m_array);
// Extraction
longArray_forany extractedValue;
if (a >>= extractedValue) {
cout << "Element [1][2] is "
<< extractedValue[1][2] << endl;
}
416
Inserting and Extracting String Data
Inserting strings The from_string and from_wstring struct types are used in combination
with the insertion operator >>= to insert strings and wide strings. Two
constructors are provided for the from_string type:
CORBA::Any::from_string(
char* s,
CORBA::ULong b,
CORBA::Boolean nocopy = 0
)
CORBA::Any::from_string(const char* s, CORBA::ULong b)
CORBA::Any::from_wstring(
CORBA::WChar* s,
CORBA::ULong b,
CORBA::Boolean nocopy = 0
)
CORBA::Any::from_wstring(const CORBA::WChar* s, CORBA::ULong b)
417
CHAPTER 15 | Using the Any Data Type
Examples of inserting bounded and unbounded string types are shown in the
following code:
Extracting strings The to_string and to_wstring struct types are used in combination with
the extraction operator >>= to extract strings and wide strings. One
constructor is provided for the to_string type:
s is a place holder that will point to the extracted string after a successful
extraction is made.
418
Inserting and Extracting String Data
419
CHAPTER 15 | Using the Any Data Type
Inserting alias types The BoundedString alias type can be inserted into an Any as follows:
CORBA::Any a;
BoundedString bs = "Less than 100 characters.";
420
Inserting and Extracting Alias Types
Extracting alias types The BoundedString alias type can be extracted from an Any as follows:
CORBA::Any a;
// The any ’a’ is initialized with a ’BoundedString’ alias
// (as shown previously)
...
421
CHAPTER 15 | Using the Any Data Type
// IDL
struct Example {
long l;
};
// IDL
interface Bar {
void op(in any a);
};
// Client code
Bar_var bVar;
CORBA::Any a = ... ; // somehow initialize
...
bVar->op(a);
The server can then query the actual type of the parameter to op() as
follows:
// Server code
void Bar_i::op(const CORBA::Any& a) {
CORBA::TypeCode_var t(a->type());
if(t->equivalent(_tc_Example)) {
cerr << "Don’t like struct Example!" << endl;
}
else... // Continue processing here.
}
422
Using DynAny Objects
Interface hierarchy The DynAny API consists of nine interfaces. One of these, interface
DynAnyFactory, lets you create DynAny objects. The rest of the DynAny API
consists of the DynAny interface itself and derived interfaces, as shown in
Figure 20.
DynFixed
DynStruct
DynSequence
DynArray
DynAny::
DynUnion
DynEnum
DynValue
DynValueBox
423
CHAPTER 15 | Using the Any Data Type
Generic operations The DynAny interface contains a number of operations that can be invoked
on any basic or constructed DynAny object:
interface DynAny {
exception InvalidValue{};
exception TypeMisMatch {};
// ...
void from_any(
in any value) raises(TypeMismatch, InvalidValue);
any to_any();
CORBA::TypeCode type();
// ...
};
assign() initializes one DynAny object’s value from another. The value must
be compatible with the target DynAny’s type code; otherwise, the operation
raises an exception of TypeMismatch.
copy() creates a DynAny whose value is a deep copy of the source DynAny’s
value.
equal() returns true if the type codes of the two DynAny objects are
equivalent and if (recursively) all component DynAny objects have identical
values.
to_any() initializes an any with the DynAny’s value and type code.
424
Using DynAny Objects
type() obtains the type code associated with the DynAny object. A DynAny
object’s type code is set at the time of creation and remains constant during
the object’s lifetime.
425
CHAPTER 15 | Using the Any Data Type
Creating a DynAny
The DynAnyFactory interface provides two creation operations for DynAny
objects:
module DynamicAny {
interface DynAny; // Forward declaration
//...
interface DynAnyFactory
{
exception InconsistentTypeCode {};
Create operations The create operations return a DynAny object that can be used to manipulate
any objects:
Returned types The type of the returned DynAny object depends on the type code used to
initialize it. For example: if a struct type code is passed to
create_dyn_any_from_type_code(), a DynStruct object is returned.
If the returned DynAny type is one of the constructed types, such as a
DynStruct, you can narrow the returned DynAny before processing it further.
426
Creating a DynAny
create_dyn_any()
create_dyn_any() is typically used when you need to parse an any to
analyse its contents. For example, given an any that contains an enum type,
you can extract its contents as follows:
//C++
#include <omg/DynamicAny.hh>
//...
void get_any_val(const CORBA::Any& a){
4 de->destroy();
}
break;
}
}
427
CHAPTER 15 | Using the Any Data Type
428
Creating a DynAny
create_dyn_any_from_type_code()
create_dyn_any_from_type_code() is typically used to create an any when
stub code is not available for the particular type.
For example, consider the IDL string<128> bounded string type. In C++
you can insert this anonymous bounded string using the
CORBA::Any::from_string helper type. Alternatively, you can use the
DynamicAny programming interface as follows:
//C++
#include <omg/DynamicAny.hh>
//...
// Get a reference to a ’DynamicAny::DynAnyFactory’ object
1 CORBA::Object_var obj
= global_orb->resolve_initial_references("DynAnyFactory");
DynamicAny::DynAnyFactory_var dyn_fact
= DynamicAny::DynAnyFactory::_narrow(obj);
if (CORBA::is_nil(dyn_fact)) {
// error: throw exception
}
429
CHAPTER 15 | Using the Any Data Type
430
Inserting and Extracting DynAny Values
Accessing basic DynAny values The DynAny interface contains two operations for each basic type code, to
insert and extract basic DynAny values:+
• An insert operation is used to set the value of the DynAny. The data
being inserted must match the DynAny’s type code.
The TypeMismatch exception is raised if the value to insert does not
match the DynAny’s type code.
The InvalidValue exception is raised if the value to insert is
unacceptable—for example, attempting to insert a bounded string that
is longer than the acceptable bound. The InvalidValue exception is
also raised if you attempt to insert a value into a DynAny that has
components when the current position is equal to -1. See “Iterating
Over DynAny Components” on page 436.
• Each extraction operation returns the corresponding IDL type.
The DynamicAny::DynAny::TypeMismatch exception is raised if the
value to extract does not match the DynAny’s type code.
The DynamicAny::DynAny::InvalidValue exception is raised if you
attempt to extract a value from a DynAny that has components when
the current position is equal to -1. See “Iterating Over DynAny
Components” on page 436.
It is generally unnecessary to use a DynAny object in order to access any
values, as it is always possible to access these values directly (see page 409
and see page 412). Insertion and extraction operations for basic DynAny
types are typically used in code that iterates over components of a
constructed DynAny, in order to compose and decompose its values in a
uniform way (see page 438).
The IDL for insertion and extraction operations is shown in the following
sections.
431
CHAPTER 15 | Using the Any Data Type
Insertion Operations
The DynAny interface supports the following insertion operations:
432
Inserting and Extracting DynAny Values
#include <omg/DynamicAny.hh>
//...
// Get a reference to a ’DynamicAny::DynAnyFactory’ object
CORBA::Object_var obj
= global_orb->resolve_initial_references("DynAnyFactory");
DynamicAny::DynAnyFactory_var dyn_fact
= DynamicAny::DynAnyFactory::_narrow(obj);
if (CORBA::is_nil(dyn_fact)) {
// error: throw exception
}
433
CHAPTER 15 | Using the Any Data Type
Extraction Operations
The IDL extraction operations supported by the DynAny interface are:
boolean get_boolean()
raises (TypeMismatch, InvalidValue);
octet get_octet()
raises (TypeMismatch, InvalidValue);
char get_char()
raises (TypeMismatch, InvalidValue);
short get_short()
raises (TypeMismatch, InvalidValue);
unsigned short get_ushort()
raises (TypeMismatch, InvalidValue);
long get_long()
raises (TypeMismatch, InvalidValue);
unsigned long get_ulong()
raises (TypeMismatch, InvalidValue);
float get_float()
raises (TypeMismatch, InvalidValue);
double get_double()
raises (TypeMismatch, InvalidValue);
string get_string()
raises (TypeMismatch, InvalidValue);
Object get_reference()
raises (TypeMismatch, InvalidValue);
CORBA::TypeCode get_typecode()
raises (TypeMismatch, InvalidValue);
long long get_longlong()
raises (TypeMismatch, InvalidValue);
unsigned long long get_ulonglong()
raises (InvalidValue,TypeMismatch);
long double get_longdouble()
raises (TypeMismatch, InvalidValue);
wchar get_wchar()
raises (TypeMismatch, InvalidValue);
wstring get_wstring()
raises (TypeMismatch, InvalidValue);
any get_any()
raises (TypeMismatch, InvalidValue);
DynAny get_dyn_any()
raises (TypeMismatch, InvalidValue);
ValueBase get_val()
raises (TypeMismatch, InvalidValue);
434
Inserting and Extracting DynAny Values
For example, the following code converts a basic any to a DynAny. It then
evaluates the DynAny’s type code in a switch statement and calls the
appropriate get_ operation to obtain its value:
#include <omg/DynamicAny.hh>
//...
// Get a reference to a ’DynamicAny::DynAnyFactory’ object
CORBA::Object_var obj
= global_orb->resolve_initial_references("DynAnyFactory");
DynamicAny::DynAnyFactory_var dyn_fact
= DynamicAny::DynAnyFactory::_narrow(obj);
if (CORBA::is_nil(dyn_fact)) {
// error: throw exception
}
dyn_a->destroy(); // cleanup
435
CHAPTER 15 | Using the Any Data Type
module DynamicAny {
//...
interface DynAny{
// ...
// Iteration operations
unsigned long component_count();
DynAny current_component() raises (TypeMismatch);
boolean seek(in long index);
boolean next();
void rewind();
};
};
436
Inserting and Extracting DynAny Values
boolean next();
rewind() resets the current position to the DynAny object’s first component:
void rewind();
Undefined current position In some circumstances the current position can be undefined. For example,
if a DynSequence object contains a zero length sequence, both the current
component and the value of the DynAny’s current position are undefined.
The special value -1 is used to represent an undefined current position.
When the current position is -1, an invocation of current_component()
yields a nil object reference.
The current position becomes undefined (equal to -1) under the following
circumstances:
• When the DynAny object has no components.
For example, a DynAny containing a zero-length sequence or array
would have no components.
• Immediately after next() returns false.
• If seek() is called with a negative integer argument, or with a positive
integer argument greater than the largest valid index.
437
CHAPTER 15 | Using the Any Data Type
module DynamicAny {
//...
interface DynEnum : DynAny {
string get_as_string();
void set_as_string(in string val) raises(InvalidValue);
unsigned long get_as_ulong();
void set_as_ulong(in unsigned long val)
raises(InvalidValue);
};
};
set_as_string("NASD") sets the enum’s value as NASD, while you can get its
current string value by calling get_as_string().
438
Inserting and Extracting DynAny Values
The following code uses a DynEnum to decompose an any value that contains
an enumeration:
switch(tcode->kind()){
case CORBA::tk_enum:
{
DynamicAny::DynEnum_var dyn_e =
DynamicAny::DynEnum::_narrow(dyn_a);
CORBA::String_var s = dyn_e->get_as_string();
cout << s << endl;
dyn_e->destroy();
}
439
CHAPTER 15 | Using the Any Data Type
DynStruct The DynStruct interface is used for struct and exception types. The
interface is defined as follows:
module DynamicAny {
// ...
typedef string FieldName;
struct NameValuePair{
FieldName id;
any value;
};
typedef sequence<NameValuePair> NameValuePairSeq;
struct NameDynAnyPair {
FieldName id;
DynAny value;
};
typedef sequence<NameDynAnyPair> NameDynAnyPairSeq;
440
Inserting and Extracting DynAny Values
441
CHAPTER 15 | Using the Any Data Type
DynUnion The DynUnion interface enables access to any values of union type:
module DynamicAny {
//...
typedef string FieldName;
442
Inserting and Extracting DynAny Values
member() returns the currently active member. If the union has no active
member, the operation raises InvalidValue. Note that the returned
reference remains valid only as long as the currently active member does not
change. Using the returned reference beyond the life time of the currently
active member raises OBJECT_NOT_EXIST.
DynSequence and DynArray The interfaces for DynSequence and DynArray are virtually identical:
module DynamicAny {
//...
typedef sequence<any> AnySeq;
typedef sequence<DynAny> DynAnySeq;
443
CHAPTER 15 | Using the Any Data Type
You can get and set element values in a DynSequence or DynArray with
operations get_elements() and set_elements(), respectively. Members are
defined as an AnySeq sequence of any objects.
Operations get_elements_as_dyn_any() and set_elements_as_dyn_any()
are functionally equivalent to get_elements() and set_elements(); unlike
their counterparts, they return and accept sequences of DynAny elements.
DynSequence has two of its own operations:
444
Inserting and Extracting DynAny Values
DynFixed The DynFixed interface lets you manipulate an any that contains fixed-point
values.
interface DynAny{
...
interface DynFixed : DynAny{
string get_value();
void set_value(in string val)
raises (TypeMismatch, InvalidValue);
};
};
DynValue The DynValue interface lets you manipulate an any that contains a value
type (excluding boxed value types):
module DynamicAny {
//...
typedef string FieldName;
struct NameValuePair
{
FieldName id;
any value;
};
typedef sequence<NameValuePair> NameValuePairSeq;
struct NameDynAnyPair
{
FieldName id;
DynAny value;
};
typedef sequence<NameDynAnyPair> NameDynAnyPairSeq;
445
CHAPTER 15 | Using the Any Data Type
current_member_kind() returns the type code kind for the value type
member indexed by the current position.
get_members() returns the complete list of value type members in the form
of a NameValuePairSeq.
446
Inserting and Extracting DynAny Values
DynValueBox The DynValueBox interface lets you manipulate an any that contains a boxed
value type:
module DynamicAny {
//...
interface DynValueBox : DynAny
{
any get_boxed_value();
void set_boxed_value(in any val)
raises (TypeMismatch);
DynAny get_boxed_value_as_dyn_any();
void set_boxed_value_as_dyn_any(in DynAny val)
raises (TypeMismatch);
};
};
447
CHAPTER 15 | Using the Any Data Type
448
CHAPTER 16
Generating
Interfaces at
Runtime
The dynamic invocation interface lets a client invoke on
objects whose interfaces are known only at runtime; similarly,
the dynamic skeleton interface lets a server process requests
on objects whose interfaces are known only at runtime.
An application’s IDL usually describes interfaces to all the CORBA objects
that it requires at runtime. Accordingly, the IDL compiler generates the stub
and skeleton code that clients and servers need in order to issue and
process requests. The client can issue requests only on those objects whose
interfaces are known when the client program is compiled; similarly, the
server can process requests only on those objects that are known when the
server program is compiled.
Some applications cannot know ahead of time which objects might be
required at runtime. In this case, Orbix provides two interfaces that let you
construct stub and skeleton code at runtime, so clients and servers can
issue and process requests on those objects:
• The dynamic invocation interface (DII) builds stub code for a client so
it can call operations on IDL interfaces that were unknown at compile
time.
449
CHAPTER 16 | Generating Interfaces at Runtime
• The dynamic skeleton interface (DSI) builds skeleton code for a server,
so it can receive operation or attribute invocations on an object whose
IDL interface is unknown at compile time.
450
Using the DII
Clients that use DII Two types of client programs commonly use the DII:
• A client interacts with the interface repository to determine a target
object’s interface, including the name and parameters of one or all of
its operations, then uses this information to construct DII requests.
• A client, such as a gateway, receives the details of a request. In the
case of a gateway, the request details might arrive as part of a network
package. The gateway can then translate this into a DII call without
checking the details with the interface repository. If a mismatch
occurs, an exception is raised to the gateway, which in turn can report
an error to the caller.
451
CHAPTER 16 | Generating Interfaces at Runtime
Example IDL The bank example is modified here to show how to use the DII. The
Bank::newAccount() operation now takes an inout parameter that sets a
new account’s initial balance:
// IDL
interface Account {
readonly attribute float balance;
interface Bank {
exception Reject {string reason;};
// Create an account
Account newAccount(
in string owner,
inout float initialBalance,
out long status)
raises (Reject);
// Delete an account
void deleteAccount(in Account a);
};
The following section shows how to construct a Request object that can
deliver client requests for newAccount() operations such as this one:
bankVar->newAccount(ownerName, initialBalance, status);
452
Using the DII
453
CHAPTER 16 | Generating Interfaces at Runtime
_request()
Overview You can use _request() to create a Request object in these steps:
1. Create a request object and set the name of its operation.
2. Set the operation’s return type.
3. Set operation parameters and supply the corresponding arguments.
4. Set exception type codes.
5. Set the operation’s context clause, if necessary.
Create a request object Call _request() on the target object and specify the name of the operation
to invoke:
Set the operation’s return type After you create a Request object, set the TypeCode of the operation’s return
value by calling set_return_type() on the Request object.
set_return_type() takes a single argument, the TypeCode constant of the
return type. For example, given the Request object newAcctRequest, set the
return type of its newAccount() operation to Account as follows:
newAcctRequest->set_return_type(_tc_Account);
Set operation parameters A request object uses an NVList to store the data for an operation’s
parameters. To set the parameters in the NVList you need to know the
operations parameters and insert the proper values in the exact order the
454
Using the DII
add_in_arg()
add_inout_arg()
add_out_arg()
// C++
newAcctRequest->add_in_arg() <<= "Norman Fellows";
CORBA::Float initBal = 1000.00;
newAcctRequest->add_inout_arg() <<= initBal;
CORBA::Long status;
newAcctRequest->add_out_arg() <<= status;
be set because they will be changed when the operation returns. However,
the values for all in and inout parameters must be specified.
You can also fill the NVList object using NVList::add_value(). This
operation has the following signature:
Set exception type codes You must set the type codes for any exceptions defined for the Request
object’s operation. To do this use the add() operation defined for the
Request object’s exceptions() list.
455
CHAPTER 16 | Generating Interfaces at Runtime
add() takes the exceptions type codes as its only argument. To add the
Reject exception to newAcctRequest use the following operation:
newAcctRequest->exceptions()->add(Bank::_tc_Reject);
If the type code for the exception was not available in the stub code, you
would need to dynamically generate the exceptions type code.
Set the operation’s context clause If the IDL operation has a context clause, you can add a Context object to
its Request object with CORBA::Request::ctx().
456
Using the DII
_create_request()
Overview You can also create a Request object by calling _create_request() on an
object reference and passing the request details as arguments. The
advantage of using _create_request() is that you can create a Request
object that contains all of the information needed to invoke a request.
_create_request() has the following signature:
Creating the parameter list There are two operations provided by CORBA::ORB to create the NVList
passed to _create_object() to specify the Request object’s parameter list:
• create_list()
• create_operation_list()
create_list()
create_list() has the fololwing signiture:
457
CHAPTER 16 | Generating Interfaces at Runtime
The operation allocates the space for an NVList of the specified number of
elements and returns a pointer to the empty NVList. You then add the
required parameters using the following operation on the NVList:
add()
add_item()
add_item_consume()
add_value()
add_value_consume()
create_operation_list()
create_operation_list() extends the functionality of create_list() by
creating a prefilled parameter list based on informaiton stored in the
interface repository. It has the following signature:
458
Using the DII
CORBA::Request_ptr newAcctRequest;
CORBA::NamedValue_ptr result;
459
CHAPTER 16 | Generating Interfaces at Runtime
Invoking a Request
After you set a Request object’s data, you can use one of several methods to
invoke the request on the target object. The following methods are invoked
on a Request object:
invoke() blocks the client until the operation returns with a reply. Exceptions
are handled the same as static function invocations.
send_deferred() sends the request to the target object and allows the client
to continue processing while it awaits a reply. The client must poll for the
request’s reply (see “Invoking Deferred Synchronous Requests” on
page 462).
try {
if (request->invoke())
// Call to invoke() succeeded
else
// Call to invoke() failed.
}
catch (CORBA::SystemException& se) {
cout << "Unexpected exception" << &se << endl;
}
460
Using the DII
CORBA::Object_var newAccount;
request->return_value() >>= newAccount;
461
CHAPTER 16 | Generating Interfaces at Runtime
462
Using the DSI
463
CHAPTER 16 | Generating Interfaces at Runtime
DSI Applications
Overview The DSI is designed to help write gateways that accept operation or attribute
invocations on any specified set of interfaces and pass them to another
system. A gateway can be written to interface between CORBA and some
non-CORBA system. This gateway is the only part of the CORBA system that
must know the non-CORBA system’s protocol; the rest of the CORBA
system simply issues IDL calls as usual.
Invoking on a gateway The IIOP protocol lets an object invoke on objects in another ORB. If a
non-CORBA system does not support IIOP, you can use DSI to provide a
gateway between the CORBA and non-CORBA systems. To the CORBA
system, this gateway appears as a CORBA-compliant server that contains
CORBA objects. In reality, the server uses DSI to trap incoming invocations
and translate them into calls that the non-CORBA system can understand.
Bidirectional gateways You can use DSI and DII together to construct a bidirectional gateway. This
gateway receives messages from the non-CORBA system and uses the DII to
make CORBA client calls. It uses DSI to receive requests from clients on a
CORBA system and translate these into messages in the non-CORBA
system.
DSI has other uses. For example, a server might contain many non-CORBA
objects that it wants to make available to its clients. In an application that
uses DSI, clients invoke on only one CORBA object for each non-CORBA
object. The server indicates that it uses DSI to accept invocations on the IDL
interface. When it receives an invocation, it identifies the target object, the
operation or attribute to call, and its parameters. It then makes the call on
the non-CORBA object. When it receives the result, it returns it to the client.
464
Using the DSI
Dynamic implementation routine When a client invokes on a DSI-generated object reference, the POA delivers
the client request as an argument to the DSI servant’s invoke() method—
also known as the dynamic implementation routine (DIR). invoke() takes a
single argument, a CORBA::ServerRequest pseudo-object, which
encapsulates all data that pertains to the client request—the operation’s
signature and arguments. CORBA::ServerRequest maps to the following
C++ class:
class ServerRequest{
public:
const char* operation() cont;
void arguments( NVList_ptr& parameters);
Context_ptr ctx();
void set_result(const Any& value);
void set_exception(const Any& value);
};
465
CHAPTER 16 | Generating Interfaces at Runtime
invoke() processing invoke() processing varies across different implementations, but it always
includes the following steps:
1. Obtains the operation’s name by calling operation() on the
ServerRequest object.
2. Builds an NVList that contains definitions for the operation’s
parameters—often, from an interface definition obtained from the
interface repository. Then, invoke() populates the NVList with the
operation’s input arguments by calling arguments() on the
ServerRequest object.
3. Reconstructs the client invocation and processes it.
4. If required, sets the operation’s output in one of two ways:
♦ If the operation’s signature defines output parameters, invoke()
sets the NVList as needed. If the operation’s signature defines a
return value, invoke() calls set_result() on the ServerRequest
object.
♦ If the operation’s signature defines an exception, invoke() calls
set_exception() on the ServerRequest object.
Note: invoke() can either set the operation’s output by initializing its
output parameters and setting its return value, or by setting an exception;
however, it cannot do both.
466
CHAPTER 17
467
CHAPTER 17 | Using the Interface Repository
468
Interface Repository Data
// In module CORBA
enum DefinitionKind
{
dk_none, dk_all,
dk_Attribute, dk_Constant, dk_Exception, dk_Interface,
dk_Module, dk_Operation, dk_Typedef,
dk_Alias, dk_Struct, dk_Union, dk_Enum,
dk_Primitive, dk_String, dk_Sequence, dk_Array,
dk_Repository, dk_Wstring, dk_Fixed,
dk_Value, dk_ValueBox, dk_ValueMember, dk_Native
};
...
interface IRObject
{
// read interface
readonly attribute DefinitionKind def_kind;
// write interface
void
destroy();
};
469
CHAPTER 17 | Using the Interface Repository
IDLType: All interface repository interfaces that hold the definition of a type
inherit directly or indirectly from this interface. See “IDL-type objects” on
page 474.
TypedefDef: The base interface for the following interface repository types
that have names: StructDef, UnionDef, EnumDef, and AliasDef, which
represents IDL typedef definitions.
470
Interface Repository Data
471
CHAPTER 17 | Using the Interface Repository
472
Interface Repository Data
Given an object of any interface repository type, you can obtain its full
interface definition. For example, InterfaceDef defines operations or
attributes to determine an interface’s name, its inheritance hierarchy, and
the description of each operation and each attribute.
473
CHAPTER 17 | Using the Interface Repository
IRObject
TypedefDef
Repository
ExceptionDef
ModuleDef
IDL-type objects Most repository objects represent IDL types—for example, InterfaceDef
objects represent IDL interfaces, StructDef interfaces represent struct
definitions, and so on. These objects all inherit, directly or indirectly, from
the abstract base interface IDLType:
// In module CORBA
interface IDLType : IRObject {
readonly attribute TypeCode type;
};
This base interface defines a single attribute that contains the TypeCode of
the defined type.
474
Interface Repository Data
Named types
The interface repository can contain these named IDL types:
AliasDef StructDef
EnumDef UnionDef
InterfaceDef ValueBoxDef
NativeDef ValueDef
For example, the following IDL defines enum type UD and typedef type
AccountName, which the interface repository represents as named object
types EnumDef and AliasDef objects, respectively:
// IDL
enum UD {UP, DOWN};
typedef string AccountName;
The following named object types inherit from the abstract base interface
TypedefDef:
AliasDef StructDef
EnumDef ValueBoxDef
NativeDef UnionDef
// IDL
// In module CORBA
interface TypedefDef : Contained, IDLType {
};
TypedefDef serves the sole purpose of enabling its derived object types to
inherit Contained and IDLType attributes and operations:
• Attribute Contained::name enables access to the object’s name. For
example, the IDL enum definition UD shown earlier is represented by the
repository object EnumDef, whose inherited name attribute is set to UD.
• Operation Contained::describe() gets a detailed description of the
object. For more information about this operation, see “Repository
Object Descriptions” on page 485.
475
CHAPTER 17 | Using the Interface Repository
Interfaces InterfaceDef and ValueDef are also named object types that
inherit from three base interfaces: Contained, Container, and IDLType.
Because IDL object and value references can be used like other types,
IntefaceDef and ValueDef inherit from the base interface IDLType. For
example, given the IDL definition of interface Account, the interface
repository creates an InterfaceDef object whose name attribute is set to
Account. This name can be reused as a type.
Unnamed types
The interface repository can contain the following unnamed object types:
ArrayDef SequenceDef
FixedDef StringDef
PrimitiveDef WStringDef
// IDL
interface Mailer {
string getLongAddress();
};
476
Interface Repository Data
477
CHAPTER 17 | Using the Interface Repository
Containment interfaces The interface repository abstracts the properties of containment into two
abstract base interfaces:
• Contained
• Container
These interfaces provide operations and attributes that let you traverse the
hierarchy of relationships in an interface repository in order to list its
contents, or ascertain a given object’s container. Most repository objects are
derived from one or both of Container or Contained; the exceptions are
instances of PrimitiveDef, StringDef, SequenceDef, and ArrayDef.
Example In the following IDL, module Finance is defined with two interface
definitions, Bank and Account. In turn, interface Account contains attribute
and operation definitions:
// IDL
module Finance {
interface Account {
readonly attribute float balance;
void makeDeposit(in float amount);
void makeWithdrawal(in float amount);
};
interface Bank {
Account newAccount();
};
};
The corresponding interface repository objects for these definitions are each
described as Container or Contained objects. Thus, the interface repository
represents module Finance as a ModuleDef container for InterfaceDef
478
Containment in the Interface Repository
objects Account and Bank; these, in turn, serve as containers for their
respective attributes and operations. ModuleDef object Finance is also
viewed as a contained object within the container object RepositoryDef.
Containment properties of Table 21 shows the relationship between Container and Contained objects
interface repository objects in the interface repository.
Repository ConstantDef
TypedefDef
ExceptionDef
InterfaceDef*
ModuleDef*
ValueDef*
ModuleDef ConstantDef
TypedefDef
ExceptionDef
ModuleDef*
InterfaceDef*
ValueDef*
InterfaceDef ConstantDef
TypedefDef
ExceptionDef
AttributeDef
OperationDef
ValueDef ConstantDef
TypedefDef
ExceptionDef
AttributeDef
OperationDef
ValueMemberDef
479
CHAPTER 17 | Using the Interface Repository
480
Containment in the Interface Repository
Contained Interface
The Contained interface is defined as follows:
//IDL
typedef string VersionSpec;
// read interface
readonly attribute Container defined_in;
readonly attribute ScopedName absolute_name;
readonly attribute Repository containing_repository;
struct Description
{
DefinitionKind kind;
any value;
};
Description
describe();
// write interface
void
move(
in Container new_container,
in Identifier new_name,
in VersionSpec new_version
);
};
Name attribute Attribute Contained::name is of type Identifier, a typedef for a string, and
contains the IDL object’s name. For example, module Finance is
represented in the repository by a ModuleDef object. Its inherited
ModuleDef::name attribute resolves to the string Finance. Similarly the
makeWithdrawal operation is represented by an OperationDef object whose
OperationDef::name attribute resolves to makeWithdrawal.
481
CHAPTER 17 | Using the Interface Repository
defined_in attribute Contained also defines the attribute defined_in, which stores a reference to
an object’s Container. Because IDL definitions within a repository must be
unique, defined_in stores a unique Container reference. However, given
inheritance among interfaces, an object can be contained in multiple
interfaces. For example, the following IDL defines interface CurrentAccount
to inherit from interface Account:
//IDL
// in module Finance
interface CurrentAccount : Account {
readonly attribute overDraftLimit;
};
balance attribute Given this definition, attribute balance is contained in interfaces Account
and CurrentAccount; however, attribute balance is defined only in the base
interface Account. Thus, if you invoke AttributeDef::defined_in() on
either Account::balance or CurrentAccount::balance, it always returns
Account as the Container object.
A Contained object can include more than containment information. For
example, an OperationDef object has a list of parameters associated with it
and details of the return type. The operation Contained::describe()
provides access to these details by returning a generic Description
structure (see “Repository Object Descriptions” on page 485).
482
Containment in the Interface Repository
Container Interface
Interface Container is defined as follows:
//IDL
enum DefinitionKind
{
dk_none, dk_all,
dk_Attribute, dk_Constant, dk_Exception, dk_Interface,
dk_Module, dk_Operation, dk_Typedef,
dk_Alias, dk_Struct, dk_Union, dk_Enum,
dk_Primitive, dk_String, dk_Sequence, dk_Array,
dk_Repository, dk_Wstring, dk_Fixed,
dk_Value, dk_ValueBox, dk_ValueMember, dk_Native
};
...
Contained
lookup(
in ScopedName search_name
);
ContainedSeq
contents(
in DefinitionKind limit_type,
in boolean exclude_inherited
);
ContainedSeq
lookup_name (
in Identifier search_name,
in long levels_to_search,
in DefinitionKind limit_type,
in boolean exclude_inherited
);
483
CHAPTER 17 | Using the Interface Repository
struct Description
{
Contained contained_object;
DefinitionKind kind;
any value;
};
typedef sequence<Description> DescriptionSeq;
DescriptionSeq
describe_contents(
in DefinitionKind limit_type,
in boolean exclude_inherited,
in long max_returned_objs
);
// write interface
lookup operations The container interface provides four lookup operations that let you browse
a given container for its contents: lookup(), lookup_name(), contents(),
and describe_contents(). For more information about these operations,
see “Browsing and listing repository contents” on page 488.
484
Repository Object Descriptions
How to obtain object descriptions You can generally get an object’s description in two ways:
• The interface for each contained object type often defines attributes
that get specific aspects of an object’s description. For example,
attribute OperationDef::result gets an operation’s return type.
• You can obtain all the information stored for a given object through the
inherited operation Contained::describe(), which returns the general
purpose structure Contained::Description. This structure’s value
member is of type any, whose value stores the object type’s structure.
For example, interface OperationDef has the following definition:
Accessing attributes Interface OperationDef defines a number of attributes that allow direct
access to specific aspects of an operation, such as its parameters (params)
and return type (result_def).
485
CHAPTER 17 | Using the Interface Repository
// IDL
struct OperationDescription
{
Identifier name;
RepositoryId id;
RepositoryId defined_in;
VersionSpec version;
TypeCode result;
OperationMode mode;
ContextIdSeq contexts;
ParDescriptionSeq parameters;
ExcDescriptionSeq exceptions;
};
OperationDescription structure
OperationDescription members store the following information:
486
Repository Object Descriptions
TypeDescription structure
Several repository object types use the TypeDescription structure to store
their information: EnumDef, UnionDef, AliasDef, and StructDef.
487
CHAPTER 17 | Using the Interface Repository
Getting a CORBA object’s Given a reference to a CORBA object, you can obtain its interface from the
interface interface repository by invoking _get_interface() on it. For example, given
CORBA object objVar, you can get a reference to its corresponding
InterfaceDef object as follows:
CORBA::InterfaceDef_var ifVar =
objVar->_get_interface();
The member function _get_interface() returns a reference to an object
within the interface repository. You can then use this reference to browse
the repository, and to obtain the details of an interface definition.
Browsing and listing repository After you obtain a reference to a Repository object, you can browse or list
contents its contents. To obtain a Repository’s object reference, invoke
resolve_initial_references("InterfaceRepository") on the ORB. This
returns an object reference of type CORBA::Object, which you narrow to a
CORBA::Repository reference.
The abstract interface Container has four operations that enable repository
browsing:
• lookup()
• lookup_name()
• contents()
• describe_contents()
488
Retrieving Repository Information
CORBA::Contained_var cVar;
cVar = moduleVar->lookup("Account::balance");
The ScopedName argument that you supply can specify to search outside the
cope of the actual container on which you invoke lookup(). For example,
the following statement invokes lookup() on an InterfaceDef in order to
start searching for the newAccount operation from the Repository container:
CORBA::Contained_var cVar;
cVar = ifVar->lookup("::Finance::Bank::newAccount");
489
CHAPTER 17 | Using the Interface Repository
490
Retrieving Repository Information
Finding an object using its You can use a repository ID to find any object in a repository by invoking
repository id Container::lookup_id() on that repository. lookup_id() returns a
reference to a Contained object, which can be narrowed to the appropriate
object reference type.
491
CHAPTER 17 | Using the Interface Repository
Sample Usage
This section contains code that uses the interface repository; it prints the list
of operation names and attribute names that are defined in a given object’s
interface.
int i;
Repository_var rVar;
Contained_var cVar;
InterfaceDef_var interfaceVar;
InterfaceDef::FullInterfaceDescription_var full;
CORBA::Object_var obj;
try {
// get an object reference to the IFR:
obj =
orb->resolve_initial_references("InterfaceRepository");
rVar = Repository::_narrow(obj);
492
Sample Usage
ContainedSeq_var opSeq;
OperationDef_var doitOpVar;
try {
cout << "Looking up operation doit()"
<< endl;
opSeq = interfaceVar->lookup_name(
"doit", 1, dk_Operation, 0);
if (opSeq->length() != 1) {
cout << "Incorrect result for lookup_name()";
exit(1);
} else {
// Narrow the result to be an OperationDef.
doitOpVar =
OperationDef::_narrow(opSeq[0]))
}
...
}
catch (...) {
...
}
493
CHAPTER 17 | Using the Interface Repository
OMG IDL The default format used by Orbix, the OMG IDL format is derived from the
IDL definition’s scoped name:
IDL:identifier[/identifier]...:version-number
This format contains three colon-delimited components:
• The first component identifies the repository ID format as the OMG IDL
format.
• A list of identifiers specifies the scoped name, substituting backslash
(/) for double colon (::).
• version-number contains a version number with the following format:
major.minor
// IDL
interface Account {
readonly attribute float balance;
void makeDeposit(in float amount);
};
The IDL format repository ID for attribute Account::balance looks like this:
IDL:Account/balance:1.0
494
Repository IDs and Formats
LOCAL Local format IDs are for local use within an interface repository and are not
intended to be known outside that repository. They have the following
format:
LOCAL:ID
Local format repository IDs can be useful in a development environment as
a way to avoid conflicts with repository IDs that use other formats.
495
CHAPTER 17 | Using the Interface Repository
ID pragma You can explicitly associate an interface repository ID with an IDL definition,
such as an interface name or typedef. The definition can be fully or partially
scoped and must conform with one of the IDL formats approved by the OMG
(see “Repository IDs and Formats” on page 494).
For example, the following IDL assigns repository ID idl:test:1.1 to
interface test:
module Y {
interface test {
// ...
};
#pragma ID test "idl:test:1.1"
};
496
Controlling Repository IDs with Pragma Directives
Prefix pragma The IDL prefix pragma lets you prepend a unique identifier to repository
IDs. This is especially useful in ensuring against the chance of name
conflicts among different applications. For example, you can modify the IDL
for the Finance module to include a prefix pragma as follows:
// IDL
# pragma prefix "USB"
module Finance {
interface Account {
readonly attribute float balance;
...
};
interface Bank {
Account newAccount();
};
};
These definitions yield the following repository IDs:
IDL:USB/Finance:1.0
IDL:USB/Finance/Account:1.0
IDL:USB/Finance/Account/balance:1.0
IDL:USB/Finance/Bank:1.0
IDL:USB/Finance/Bank/newAccount:1.0
Version pragma A version number for an IDL definition’s repository ID can be specified with
a version pragma. The version pragma directive uses the following format:
#pragma version name major.minor
name can be a fully scoped name or an identifier whose scope is interpreted
relative to the scope in which the pragma directive is included. If no version
pragma is specified for an IDL definition, the default version number is 1.0.
For example:
// IDL
module Finance {
#pragma version Account 2.5
interface Account {
// ...
};
};
497
CHAPTER 17 | Using the Interface Repository
IDL:Finance/Account:2.5
Version numbers are embedded in the string format of an object reference. A
client can invoke on the corresponding server object only if its interface has
a matching version number, or has no version associated with it.
Note: You cannot populate the interface repository with two IDL
interfaces that share the same name but have different version numbers.
498
CHAPTER 18
Naming Service
The Orbix naming service lets you associate names with
objects. Servers can register object references by name with
the naming service repository, and advertise those names to
clients. Clients, in turn, can resolve the desired objects in the
naming service by supplying the appropriate name.
The Orbix naming service implements the OMG COS Interoperable Naming
Service, which describes how applications can map object references to
names.
Benefits Using the naming service can offer the following benefits:
• Clients can locate objects through standard names that are
independent of the corresponding object references. This affords
greater flexibility to developers and administrators, who can direct
client requests to the most appropriate implementation. For example,
you can make changes to an object’s implementation or its location
that are transparent to the client.
• The naming service provides a single repository for object references.
Thus, application components can rely on it to obtain an application’s
initial references.
499
CHAPTER 18 | Naming Service
In this chapter This chapter describes how to build and maintain naming graphs
programmatically. It also shows how to use object groups to achieve load
balancing. It contains these sections:
Defining Names
Sample Code
500
Naming Service Design
Example Figure 22 shows how the Account interface described in earlier chapters
might be extended (through inheritance) into multiple objects, and
organized into a hierarchy of naming contexts. In this graph, hollow nodes
are naming contexts and solid nodes are application objects. Naming
contexts are typically intermediate nodes, although they can also be leaf
nodes; application objects can only be leaf nodes.
Checking Loans
Savings
NOW Premium
Mortgage
Basic Personal
Regular Auto
Pension
UTMA
501
CHAPTER 18 | Naming Service
Each leaf node in this naming graph associates a name with a reference to
an account object such as a basic checking account or a personal loan
account. Given the full path from the initial naming context—for example,
Savings/Regular—a client can obtain the associated reference and invoke
requests on it.
The operations and types that the naming service requires are defined in the
IDL file CosNaming.idl. This file contains a single module, CosNaming,
which in turn contains three interfaces: NamingContext, NamingContextExt,
and BindingIterator.
502
Defining Names
Defining Names
Name sequence A naming graph is composed of Name sequences of NameComponent
structures, defined in the CosNaming module:
module CosNaming{
typedef string Istring;
struct NameComponent {
Istring id;
Istring kind;
}
typedef sequence<NameComponent> Name;
...
};
Figure 0.1:
Index id kind
0 Loans
1 Personal
503
CHAPTER 18 | Naming Service
In order to bind another Personal account object to the same Loan naming
context, you must differentiate it from the existing one. You might do so by
setting their kind fields as follows:
Figure 0.2:
Index id kind
0 Loans
1 Personal unsecured
1 Personal secured
504
Defining Names
505
CHAPTER 18 | Naming Service
Initializing a Name
You can initialize a CosNaming::Name sequence in one of two ways:
• Set the members of each name component.
• Call to_name() on the initial naming context and supply a StringName
argument. This operation converts the supplied string to a Name
sequence.
Setting name component Given the loan account objects shown earlier, you can set the name for an
members unsecured personal loan as follows:
CosNaming::Name name(2);
name.length(2);
name[0].id = CORBA::string_dup("Loans");
name[0].kind = CORBA::string_dup( "" );
name[1].id = CORBA::string_dup("Personal");
name[1].kind = CORBA::string_dup( "unsecured" );
Converting a stringname to a The name shown in the previous example can also be set in a more
name straightforward way by calling to_name() on the initial naming context (see
“Obtaining the Initial Naming Context” on page 509):
CosNaming::Name_var name;
name = root_cxt->to_name("Loans/Personal.unsecured");
Figure 0.3:
Index id kind
0 Loans
506
Defining Names
Figure 0.3:
Index id kind
1 Personal unsecured
507
CHAPTER 18 | Naming Service
// initialize name
CosNaming::Name_var name = ...;
...
str_n = root_cxt->to_string(name);
508
Obtaining the Initial Naming Context
...
// Initialize the ORB
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
CosNaming::NamingContextExt_var root_cxt;
if (root_cxt =
CosNaming::NamingContextExt::_narrow(obj)) {
509
CHAPTER 18 | Naming Service
510
Building a Naming Graph
CosNaming::NamingContext_var checking_cxt;
// initialize name
CosNaming::Name_var name;
name.length(1);
name[0].id = CORBA::string_dup("Checking");
name[0].kind = CORBA::string_dup( "" );
511
CHAPTER 18 | Naming Service
Checking
Similarly, you can bind the Savings and Loans naming contexts to the initial
naming context. The following code uses the shortcut operation
bind_new_context(), which combines new_context() and bind(). It also
uses the to_name() operation to set the Name variable.
name = root_cxt->to_name("Loan");
loan_cxt = root_cxt->bind_new_context(name);
Checking Loans
Savings
Figure 24: Savings and Loans naming contexts bound to initial naming
context
Orphaned naming contexts The naming service can contain naming contexts that are unbound to any
other context. Because these naming contexts have no parent context, they
are regarded as orphaned. Any naming context that you create with
512
Building a Naming Graph
Erroneous usage of orphaned Orphaned contexts can also occur inadvertently, often as a result of
naming contexts carelessly written code. For example, you can create orphaned contexts as a
result of calling rebind() or rebind_context() to replace one name binding
with another (see “Rebinding” on page 516). The following code shows how
you might orphan the Savings naming context:
CosNaming::NamingContext_var savings_cxt;
// initialize name
CosNaming::Name_var name;
name.length(1);
name[0].id = CORBA::string_dup("Savings");
name[0].kind = CORBA::string_dup( "" );
513
CHAPTER 18 | Naming Service
In both cases, if the application exits without destroying the context objects,
they remain in the naming service but are inaccessible and cannot be
deleted.
514
Building a Naming Graph
name->length(1);
name[0].id = CORBA::string_dup("Basic");
name[0].kind = CORBA::string_dup("");
checking_cxt->bind(name, basic_check);
Checking
Loans
Savings
Basic
name = root_cxt->to_name("Checking/Basic");
root_cxt->bind(name, basic_check);
515
CHAPTER 18 | Naming Service
Rebinding
If you call bind() or bind_context() on a naming context that already
contains the specified binding, the naming service throws an exception of
AlreadyBound. To ensure the success of a binding operation whether or not
the desired binding already exists, call one of the following naming context
operations:
• rebind() rebinds an application object.
• rebind_context() rebinds a naming context.
Either operation replaces an existing binding of the same name with the new
binding. Calls to rebind() in particular can be useful on server startup, to
ensure that the naming service has the latest object references.
516
Using Names to Access Objects
Setting object names You specify the path to the desired object reference in a CosNaming::Name.
You can set this name in one of two ways:
Explicitly set the id and kind members of each Name element. For
example, the following code sets the name of a Basic checking account
object:
CosNaming::Name_var name;
name.length(2);
name[0].id = CORBA::string_dup("Checking");
name[0].kind = CORBA::string_dup("");
name[1].id = CORBA::string_dup("Basic");
name[1].kind = CORBA::string_dup("");
Call to_name() on the initial naming context. This option is available if the
client code narrows the initial naming context to the NamingContextExt
interface. to_name() takes a CosNaming::CosNamingExt::StringName
argument and returns a CosNaming::Name as follows:
CosNaming::Name_var name;
name = root_cxt->to_name("Checking/Basic");
517
CHAPTER 18 | Naming Service
Resolving names Clients call resolve() on the initial naming context to obtain the object
associated with the supplied name:
CORBA::Object_var obj;
...
obj = root_cxt->resolve(name);
Alternatively, the client can call resolve_str() on the initial naming context
to resolve the same name using its StringName equivalent:
CORBA::Object_var obj;
...
obj = root_cxt->resolve_str("Checking/Basic");
BasicChecking_var checking_var;
...
try {
checking_var = BasicChecking::_narrow(obj)) {
// perform some operation on basic checking object
...
} // end of try clause, catch clauses not shown
Resolving names with corbaname You can resolve names with a corbaname URL, which is similar to a
corbaloc URL (see “Using corbaloc URL strings” on page 221). However, a
corbaname URL also contains a stringified name that identifies a binding in a
naming context. For example, the following code uses a corbaname URL to
obtain a reference to a BasicChecking object:
CORBA::Object_var obj;
obj = orb->string_to_object(
"corbaname:rir:/NameService#Checking/Basic"
);
518
Using Names to Access Objects
519
CHAPTER 18 | Naming Service
NotFound The specified name does not resolve to an existing binding. This
exception contains two data members:
why Explains why a lookup failed with one of the following values:
• missing_node: one of the name components specifies a
non-existent binding.
• not_context: one of the intermediate name components
specifies a binding to an application object instead of a
naming context.
• not_object: one of the name components points to a
non-existent object.
rest_of_nameContains the trailing part of the name that could not be
resolved.
Not Empty Attempts to delete a context that contains bindings throw this
exception. Contexts must be empty before you delete them.
520
Listing Naming Context Bindings
void list(
in unsigned long how_many,
out BindingList bl,
out BindingIterator it);
struct Binding{
Name binding_name
BindingType binding_type;
}
typedef sequence<Binding> BindingList
Iterating over binding list Given a binding list, the client can iterate over its elements to obtain their
elements binding name and type. Given a Binding element’s name, the client
application can call resolve() to obtain an object reference; it can use the
binding type information to determine whether the object is a naming
context or an application object.
For example, given the naming graph in Figure 22, a client application can
invoke list() on the initial naming context and return a binding list with
three Binding elements:
Figure 0.4:
0 Checking ncontext
1 Savings ncontext
521
CHAPTER 18 | Naming Service
Figure 0.4:
2 Loan ncontext
522
Listing Naming Context Bindings
interface BindingIterator{
boolean next_one(out Binding b);
boolean next_n(in unsigned long how_many, out BindingList
bl);
void destroy();
}
Obtaining remainder of bindings If list() returns with a BindingIterator object, the client can invoke on it
either next_n() to retrieve the next specified number of remaining bindings,
or next_one() to retrieve one remaining binding at a time. Both functions
return true if the naming context contains more bindings to fetch. Together,
these BindingIterator operations and list() let a client safely obtain all
bindings in a context.
523
CHAPTER 18 | Naming Service
The following client code gets a binding list from a naming context and
prints each element’s binding name and type:
// printing function
void
print_binding_list(const CosNaming::BindingList &bl)
{
for( CORBA::Ulong i = 0; i < bl.length(); i++ ){
cout << bl[i].binding_name[0].id;
if( bl[i].binding_name[0].kind != ’\0’ )
cout << "(" << bl[i].binding_name[0].kind << ")";
if( bl[i].binding_type == CosNaming::ncontext )
cout << ": naming context" << endl;
else
cout << ": object reference" << endl;
}
}
void
get_context_bindings(CosNaming::NamingContext_ptr cxt)
{
CosNaming::BindingList_var b_list;
CosNaming::BindingIterator_var b_iter;
const CORBA::ULong MAX_BINDINGS = 50;
if (!CORBA::is_nil(cxt)) {
524
Listing Naming Context Bindings
}
// get rid of iterator
b_iter->destroy();
}
}
When you run this code on the initial naming context shown earlier, it yields
the following output:
525
CHAPTER 18 | Naming Service
These operations can be called in any order; but it is important to call both.
If you remove the bindings to a context without destroying it, you leave an
orphaned context within the naming graph that might be impossible to
access and destroy later (see “Orphaned naming contexts” on page 512). If
you destroy a context but do not remove its bindings to other contexts, you
leave behind bindings that point nowhere, or dangling bindings.
For example, given the partial naming graph in Figure 26, you can destroy
the Loans context and its bindings to the loan account objects as follows:
CosNaming::Name_var name;
526
Maintaining the Naming Service
Before After
Loans
Mortgage
Personal
Auto
527
CHAPTER 18 | Naming Service
Federation models Each naming graph in a federation must obtain the initial naming context of
other members in order to bind itself to them. The binding possibilities are
virtually infinite; however, two federation models are widely used:
• Hierarchal federation — All naming graphs are bound to a root server’s
naming graph. Clients access objects via the initial naming context of
the root server.
• Fully-connected federation — Each naming graph directly binds itself
to all other naming graphs. Typically, each naming graph binds the
initial naming contexts of all other naming graphs into its own initial
naming context. Clients can access all objects via the initial naming
context of their local naming service.
528
Federating Naming Graphs
Hierarchal federation Figure 27 shows a hierarchal naming service federation that comprises
three servers. The Deposits server maintains naming contexts for checking
and savings accounts, while the Loans server maintains naming contexts for
loan accounts. A single root server serves as the logical starting point for all
naming contexts.
Root server
Mortgage
Checking Personal
Savings
Basic
Pension
Regular
UTMA
In this hierarchical structure, the naming graphs in the Deposits and Loans
servers are federated through an intermediary root server. The initial naming
contexts of the Deposits and Loans servers are bound to the root server’s
initial naming context. Thus, clients gain access to either naming graph
through the root server’s initial naming context.
529
CHAPTER 18 | Naming Service
The following code binds the initial naming contexts of the Deposits and
Loans servers to the root server’s initial naming context:
// Root server
#include <omg/CosNaming.hh>
...
int main (int argc, char** argv) {
CosNaming::NamingContextExt_var
root_inc, deposits_inc, loans,_inc;
CosNaming::Name_var name;
CORBA::Object_var obj;
CORBA::ORB_var orb_var;
char *loans_inc_ior, deposits_inc_ior
...
try {
orb_var = CORBA::ORB_init(argc, argv, "Orbix");
530
Federating Naming Graphs
This yields the following bindings between the three naming graphs:
Root server
Figure 28: Multiple naming graphs are linked by binding initial naming
contexts of several servers to a root server.
Fully-connected federation In a purely hierarchical model like the naming graph just shown, clients
obtain their initial naming context from the root server, and the root server
acts as the sole gateway into all federated naming services. To avoid
bottlenecks, it is possible to modify this model so that clients can gain
access to a federated naming graph via the initial naming context of any
member naming service.
The next code example shows how the Deposits and Loans servers can bind
the root server’s initial naming context into their respective initial naming
contexts. Clients can use this binding to locate the root server’s initial
naming context, and then use root-relative names to locate objects.
531
CHAPTER 18 | Naming Service
Root server
parent parent
Figure 29: The root server’s initial naming context is bound to the initial
naming contexts of other servers, allowing clients to locate the root naming
context.
The code for both Deposits and Loans server processes is virtually identical:
Example 66: Federating naming graphs through the initial naming contexts
of multiple servers
#include <omg/CosNaming.hh>
...
int main (int argc, char** argv) {
CosNaming::NamingContextExt_var
root_inc, this_inc;
CosNaming::Name_var name;
CORBA::Object_var obj;
CORBA::ORB_var orb_var;
char *root_inc_ior;
...
532
Federating Naming Graphs
Example 66: Federating naming graphs through the initial naming contexts
of multiple servers
try {
orb_var = CORBA::ORB_init (argc, argv, "Orbix");
name = this_inc->to_name("parent");
533
CHAPTER 18 | Naming Service
Sample Code
The following sections show the server and client code that is discussed in
previous sections of this chapter.
Server code
#include <omg/CosNaming.hh>
...
int main (int argc, char** argv) {
CosNaming::NamingContextExt_var root_cxt;
CosNaming::NamingContext_var
checking_cxt, savings_cxt, loan_cxt;
CosNaming::Name_var name;
CORBA::ORB_var orb;
CORBA::Object_var obj;
Checking_var basic_check, now_check, premium_check;
// Checking_var objects initialized from
// persistent data (not shown)
try {
// Initialize the ORB
orb = CORBA::ORB_init(argc, argv, "Orbix");
// initialize name
name = root_cxt->to_name("Checking");
// bind new naming context to root
checking_cxt = root_cxt->bind_new_context(name);
534
Sample Code
name = root_cxt->to_name("Savings");
savings_cxt = root_cxt->bind_new_context(name);
name = root_cxt->to_name("Loan");
loan_cxt = root_cxt->bind_new_context(name);
Client code
#include <omg/CosNaming.hh>
...
int main (int argc, char** argv) {
CosNaming::NamingContextExt_var root_cxt;
CosNaming::Name_var name;
BasicChecking_var checking_var;
CORBA::Object_var obj;
CORBA::ORB_var orb_var;
try {
orb_var = CORBA::ORB_init (argc, argv, "Orbix");
535
CHAPTER 18 | Naming Service
536
Object Groups and Load Balancing
Selection algorithms Each object group has a selection algorithm that is set when the object
group is created (see page 541). This algorithm is applied when a client
resolves the name associated with the object group; and the naming service
directs client requests to objects accordingly.
Three selection algorithms are supported:
Random: The locator randomly selects an active server to handle the client.
Active load balancing: Each object group member is assigned a load value.
The naming service satisfies client resolve() invocations by returning
references to members with the lowest load values.
537
CHAPTER 18 | Naming Service
Figure 30 shows how a name can bind to multiple objects through an object
group.
bind_object_group()
Name Object 1
Object 2
Object 3
Optional
Object Group
Orbix
extension
Orbix supports object groups through its own IDL interfaces. These
interfaces let you create object groups and manipulate them: add objects to
and remove objects from groups, and find out which objects are members of
a particular group. Object groups are transparent to clients.
module IT_LoadBalancing
{
exception NoSuchMember{};
exception DuplicateMember{};
exception DuplicateGroup{};
exception NoSuchGroup{};
538
Object Groups and Load Balancing
enum SelectionMethod
{ ROUND_ROBIN_METHOD, RANDOM_METHOD, ACTIVE_METHOD };
struct Member
{
Object obj;
MemberId id;
};
interface ObjectGroup
{
readonly attribute string id;
attribute SelectionMethod selection_method;
Object pick();
void add_member (in Member mem)
raises (DuplicateMember);
void remove_member (in MemberId id)
raises (NoSuchMember);
Object get_member (in MemberId id)
raises (NoSuchMember);
MemberIdList members();
void destroy();
void update_member_load(
in MemberIdList ids,
in double curr_load
) raises (NoSuchMember);
double get_member_load(
in MemberId id
) raises (NoSuchMember);
void set_member_timeout(
in MemberIdList ids,
in long timeout_sec
) raises (NoSuchMember);
long get_member_timeout(
in MemberId id
) raises (NoSuchMember);
};
539
CHAPTER 18 | Naming Service
interface ObjectGroupFactory
{
ObjectGroup create_round_robin (in GroupId id)
raises (DuplicateGroup);
ObjectGroup create_random (in GroupId id)
raises (DuplicateGroup);
ObjectGroup create_active (in GroupId id)
raises (DuplicateGroup);
ObjectGroup find_group (in GroupId id)
raises (NoSuchGroup);
GroupList rr_groups();
GroupList random_groups();
GroupList active_groups();
};
};
540
Object Groups and Load Balancing
Create an object group You create an object group and add objects to it in the following steps:
1. Get a reference to a naming context such as the initial naming context
and narrow to IT_NamingContextExt.
2. Create an object group factory by calling og_factory() on the naming
context object. This returns a reference to an
IT_LoadBalancing::ObjectGroupFactory object.
3. Create an object group by calling create_random(),
create_round_robin(), or create_active() on the object group
factory. These operations return a reference to an object group of
interface IT_LoadBalancing::ObjectGroup that uses the desired
selection algorithm.
4. Add application objects to the newly created object group by calling
add_member() on it.
5. Bind a name to the object group by calling bind_object_group() on
the naming context object created in step 1.
When you create the object group, you must supply a group identifier. This
identifier is a string value that is unique among other object groups.
Similarly, when you add a member to the object group, you must supply a
reference to the object and a corresponding member identifier. This
identifier is a string value that must be unique within the object group.
In both cases, you decide the format of the identifier string. Orbix does not
interpret these identifiers.
541
CHAPTER 18 | Naming Service
Add objects to an existing object Before you add objects to an existing object group, you must get a reference
group to the corresponding IT_LoadBalancing::ObjectGroup object. You can do
this by using either the group identifier or the name that is bound to the
object group. This section uses the group identifier.
To add objects to an existing object group:
1. Get a reference to a naming context such as the initial naming context.
2. Narrow the reference to IT_NamingContextExt.
3. Call og_factory() on the naming context object. This returns a
reference to an ObjectGroupFactory object.
4. Call find_group() on the object group factory, passing the identifier for
the group as a parameter. This returns a reference to the object group.
5. Add application objects to the object group by calling add_member() on
it.
Remove objects from an object Removing an object from a group is straightforward if you know the object
group group identifier and the member identifier for the object:
1. Get a reference to a naming context such as the initial naming context
and narrow to IT_NamingContextExt.
2. Call og_factory() on the naming context object. This returns a
reference to an ObjectGroupFactory object.
3. On the object group factory, call find_group(), passing the identifier
for the target object group as a parameter. This operation returns a
reference to the object group.
4. Call remove_member() on the object group to remove the required
object from the group. You must specify the member identifier for the
object as a parameter to this operation.
If you already have a reference to the object group, the first three steps are
unnecessary.
542
Object Groups and Load Balancing
Remove an object group To remove an object group for which you have no reference:
1. Call unbind() on the initial naming context to unbind the name
associated with the object group.
2. Call og_factory() on the initial naming context object. This returns a
reference to an ObjectGroupFactory object.
3. Call find_group() on the object group factory, passing the identifier for
the target object group as a parameter. This operation returns a
reference to the object group.
4. Call destroy() on the object group to remove it from the naming
service.
If you already have a reference to the target object group, steps 2 and 3 are
unnecessary.
Set member load values In an object group that uses active load balancing, each object group
member is assigned a load value. The naming service satisfies client
resolve() invocations by returning references to members with the lowest
load values.
A member’s default load value can be set administratively through the
configuration variable plugins:naming:lb_default_initial_load.
Thereafter, load counts should be updated with periodic calls to
ObjectGroup::update_member_load(). itadmin provides an equivalent
command, nsog update_member_load, in cases where manual intervention
is required, or scripting is feasible.
You should also set or modify member timeouts with
ObjectGroup::set_member_timeout() or with itadmin nsog
set_member_timeout. You can configure default timeout values with the
configuration variable plugins:naming:lb_default_load_timeout. If an
object’s load value is not updated within its timeout interval, its object
reference becomes unavailable to client resolve() invocations. This
typically happens because the object itself or an associated process is no
longer running, and therefore cannot update the object’s load value.
A member reference can be made available again to client resolve()
invocations by resetting its load value with
ObjectGroup::update_member_load() or itadmin nsog
update_member_load. In general, an object’s timeout should be set to an
interval greater than the frequency of load count updates.
543
CHAPTER 18 | Naming Service
3
StockMarketFeed object 1
Object Group
StockMarketFeed object 4
Client
4 Get stock price
544
Load Balancing Example
Defining the IDL for the The IDL for the load balancing example consists of a single interface
application StockMarketFeed, which is defined in module ObjectGroupDemo:
// IDL
module ObjectGroupDemo
{
exception StockSymbolNotFound{};
interface StockMarketFeed
{
double read_stock (in string stock_symbol)
raises(StockSymbolNotfound);
};
};
545
CHAPTER 18 | Naming Service
#include <stdlib.h>
#include <string.h>
#include <iostream.h>
#include <omg/orb.hh>
#include <omg/PortableServer.hh>
#include <it_ts/termination_handler.h>
#include <orbix/naming.hh>
#include "stock_i.h"
546
Load Balancing Example
static void
termination_handler(long sig)
{
try
{
cout << "Removing members: " << id1 << " and "
<< id2 << endl;
rr_og_var->remove_member(id1);
rr_og_var->remove_member(id2);
}
catch (...)
{
cerr << "Could not remove members." << endl;
}
IT_LoadBalancing::MemberIdList_var members =
rr_og_var->members();
if (members->length() == 0) // Last one to remove members
{
try
{
cout << "Unbinding object group..." << endl;
it_ins_var->unbind(nm);
cout << "Destroying group..." << endl;
rr_og_var->destroy();
}
catch (...)
{
cerr << "Unbind/destroy failed." << endl;
}
}
cout << "Shutting down the ORB." << endl;
global_orb->shutdown(0);
}
547
CHAPTER 18 | Naming Service
int
main(
int argc,
char *argv[]
)
{
if (argc != 2)
{
cerr << "Usage: ./server <name>" << endl;
return 1;
}
try
{
global_orb = CORBA::ORB_init(argc, argv);
}
catch (CORBA::Exception &ex)
{
cerr << "Could not initialize the ORB." << endl;
cerr << "Exception info: " << ex << endl;
return 1;
}
IT_TerminationHandler::set_signal_handler(
termination_handler);
548
Load Balancing Example
global_orb->resolve_initial_references("NameService");
it_ins_var =
IT_Naming::IT_NamingContextExt::_narrow(ins_obj);
3 ogf_var = it_ins_var->og_factory();
}
catch (CORBA::Exception &ex)
{
cerr << "Could not obtain or _narrow() reference to "
<< "IT_Naming::IT_NamingContextExt " << endl
<< "interface. Is the Naming Service running?" <<
endl;
cerr << "Exception info: " << ex << endl;
return 1;
}
549
CHAPTER 18 | Naming Service
4 rr_og_var = ogf_var->create_round_robin(rr_id_str);
nm = it_ins_var->to_name("StockSvc");
5 it_ins_var->bind_object_group(nm,rr_og_var);
}
catch (...)
{
// OK: assume other server created object group and
// bound it in NS
rr_og_var = ogf_var->find_group(rr_id_str);
}
member_info.id = CORBA::string_dup(id1);
member_info.obj = stk_svnt1->_this();
rr_og_var->add_member(member_info);
member_info.id = CORBA::string_dup(id2);
member_info.obj = stk_svnt2->_this();
rr_og_var->add_member(member_info);
}
550
Load Balancing Example
7 global_orb->run();
}
catch (CORBA::Exception &ex)
{
cerr << "Could not activate the POAManager,
or orb->run() failed."
<< endl;
cerr << "Exception info: " << ex << endl;
return 1;
}
return 0;
}
551
CHAPTER 18 | Naming Service
552
Load Balancing Example
#include <iostream.h>
#include <omg/orb.hh>
#include <orbix/naming.hh>
#include "stock_demo.hh"
int
main(
int argc,
char *argv[]
)
{
if (argc != 2) {
cerr << "Usage: ./client <stock_symbol>" << endl;
return 1;
}
CosNaming::NamingContextExt_var ins;
try {
global_orb = CORBA::ORB_init(argc, argv);
CORBA::Object_var ins_obj =
global_orb->resolve_initial_references("NameService");
ins = CosNaming::NamingContextExt::_narrow(ins_obj);
}
553
CHAPTER 18 | Naming Service
StockDemo::StockMarketFeed_var stk_ref;
try {
CORBA::Object_var stk_obj = ins->resolve_str("StockSvc");
stk_ref = StockDemo::StockMarketFeed::_narrow(stk_obj);
}
catch (CORBA::Exception &ex) {
cerr << "Could not resolve/narrow the stock_svc IOR from "
<< "the Naming Service." << endl;
cerr << "Exception info: " << ex << endl;
return 1;
}
double curr_price;
try {
curr_price = stk_ref->read_stock(argv[1]);
}
catch (StockDemo::StockSymbolNotFound &ex) {
cerr << "Stock symbol not found: " << argv[1] << endl;
cerr << "Try another stock symbol." << endl;
return 1;
}
catch (CORBA::Exception &ex) {
cerr << "Exception received: " << ex << endl;
return 1;
}
cout << argv[1] << " stock price is " << curr_price << endl;
return 0;
}
554
CHAPTER 19
Persistent State
Service
The persistent state service (PSS) is a CORBA service for
building CORBA servers that access persistent data.
555
CHAPTER 19 | Persistent State Service
Programming with the PSS Writing a CORBA application that uses PSS is a three-step process:
• Define the data in PSDL (persistent state data language), which is an
extension of IDL, then run the IDL compiler on the PSDL files to
generate C++ code.
• Write a server program that uses PSS to access and manipulate
persistent data.
• Set PSS plug-in variables in the application’s configuration as required.
556
Defining Persistent Data
Reserved keywords The file CosPersistentState.psdl contains all PSDL type definitions, and is
implicitly included in any PSDL specification. The following identifiers are
reserved for use as PSDL keywords (asterisks indicate keywords reserved for
use in future PSS implementations). Avoid using any of the following
keywords as user-defined identifiers:
as*
catalog*
factory
implements
key
of
primary
provides*
ref
scope*
storagehome
storagetype
stores*
strong*
557
CHAPTER 19 | Persistent State Service
Datastore Model
PSDL contains several constructs that you use to describe datastore
components. These include:
• storagetype describes how data is organized in storage objects of that
type.
• storagehome describes a container for storage objects. Each storage
home is defined by a storage type and can only contain storage objects
of that type. Storage homes are themselves contained by a datastore,
which manages the data—for example a database, a set of files, or a
schema in a relational database. A datastore can contain only one
storage home of a given storage type.
Within a datastore, a storage home manages its own storage objects and the
storage objects of all derived storage homes.
For example, the following two PSDL files describe a simple datastore with
a single Account storage type and its Bank storage home:
// in bank_demo_store_base.psdl
#include<BankDemo.idl>
module BankDemoStoreBase {
abstract storagetype AccountBase {
state BankDemo::AccountId account_id;
state BankDemo::CashAmount balance;
};
558
Defining Persistent Data
// in bank_demo_store.psdl
#include <BankDemo.idl>
#include <BankDemoStoreBase.psdl>
module BankDemoStore {
storagetype Account implements BankDemoStoreBase::AccountBase
{
ref(account_id);
};
559
CHAPTER 19 | Persistent State Service
The IDL compiler generates C++ classes for each storagetype and
storagehome that is defined in this file.
Note: If you maintain all PSDL code in a single file, you should compile it
only with the -pss_r switch.
560
Defining Persistent Data
Syntax The syntax for an abstract storage type definition is similar to the syntax for
an IDL interface; unlike an interface, however, an abstract storage type
definition cannot contain constants or type definitions.
You define an abstract storage type with this syntax:
For example:
561
CHAPTER 19 | Persistent State Service
A state member’s type can be any IDL type, or an abstract storage type
reference.
module CosPersistentState {
// ...
native StorageObjectBase;
Forward declarations As with IDL interface definitions, PSDL can contain forward declarations of
abstract storage types. The actual definition must follow later in the PSDL
specification.
562
Defining Persistent Data
For example, the following PSDL defines abstract storage home BankBase of
storage type AccountBase:
A storage home lacks state but it can have behavior, which is described by
operations that are defined in its abstract storage homes. For example, you
locate and create a storage object by calling operations on the storage home
where this object is stored.
Inheritance from interface All storage home instances implicitly derive from local interface
StorageHomeBase CosPersistentState::StorageHomeBase:
module CosPersistentState {
exception NotFound {};
native StorageObjectBase;
// ...
local interface StorageHomeBase {
StorageObjectBase
find_by_short_pid(
in ShortPid short_pid
) raises (NotFound);
};
};
563
CHAPTER 19 | Persistent State Service
find_by_short_pid() looks for a storage object with the given short pid in
the target storage home. If the search fails, the operation raises exception
CosPersistentState::NotFound.
Keys An abstract storage home can define one key. A key is composed from one
or more state members that belong to the storage home’s abstract storage
type, either directly or through inheritance. This key gives the storage home
a unique identifier for the storage objects that it manages.
Two IDL types are not valid for use as key members: valuetype and struct.
A key declaration implicitly declares a pair of finder operations; for more
information, see page 565.
Simple Keys A simple key is composed of a single state member. You declare a simple
key as follows:
key key-name (state-member);
For example, the PSDL shown earlier defines abstract storage home
BankBase for storage objects of abstract type AccountBase. This definition
can use state member account_id to define a simple key as follows:
key accno(account_id);
If the key’s name is the same as its state member, you can declare it in this
abbreviated form:
key account_id;
Composite Keys A composite key is composed of multiple state members. You declare a
composite key as follows:
key key-name (state-member, state-member[,... )
A composite key declaration must specify a key name. The types of all state
members must be comparable. The following types are comparable:
• integral types: octet, short, unsigned short, long, unsigned long,
long long, unsigned long long
• fixed types
• char, wchar, string, wstring
• sequence<octet>
• struct with only comparable members
564
Defining Persistent Data
Finder operations A key declaration is equivalent to the declaration of two PSDL finder
operations that use a given key to search for a storage object among the
storage objects that are managed directly or indirectly by the target storage
home:
The accno key declaration implicitly yields these two PSDL operations:
565
CHAPTER 19 | Persistent State Service
Operations Each parameter of a local operation can be of a valid IDL parameter type, or
of an abstract PSDL type.
Factory operations In the PSDL shown earlier, abstract storage home BankBase is defined with
the factory create operation. This operation provides a way to create
Account objects in a bank, given values for account_id and balance.
Each parameter that you supply to a factory create operation must be the
name of a state member of the abstract storage home’s abstract storage
type, including inherited state members.
The definition of a factory operation is equivalent to the definition of the
following operation:
abstract-storage-type factory-op-name(parameter-list);
where parameter-list is composed of in parameters that correspond to
each state member in the factory operation declaration, listed in the same
order.
For example, given this factory declaration:
The create factory declaration implicitly yields this operation, which uses
conventional IDL-to-C++ mapping rules:
Account create(
in BankDemo::AccountId account_id,
in BankDemo::CashAmount balance
);
566
Defining Persistent Data
Inheritance An abstract storage home can inherit from one or more abstract storage
homes, and support diamond-shape inheritance. The following constraints
apply to a base abstract storage home:
• The base abstract storage homes must already be defined.
• The base abstract storage homes must use the same abstract storage
type or base abstract storage type as the derived abstract storage
home.
• An abstract storage home cannot inherit two operations with the same
name.
Forward declarations As with IDL interface definitions, PSDL can contain forward declarations of
abstract storage homes.
567
CHAPTER 19 | Persistent State Service
};
State members A storage type can define state members; these state members supplement
any state members in the abstract storage types that it implements, or that
it inherits from other implementations. You define a state member with the
following syntax:
[readonly] state type-spec member-name;
Reference representation A storage type can define a reference representation that serves as a unique
identifier for storage objects in a storage home of that storage type. A
storage type without any base storage type can define a single reference
representation, which is composed of one or more state members. For
example:
568
Defining Persistent Data
569
CHAPTER 19 | Persistent State Service
Inheritance A storage home can inherit form a previously defined storage home. The
following constraints apply:
• The storage type of the base storage home must be a base of the
storage home’s own storage type.
• Two storage homes in a storage home inheritance tree cannot have the
same storage type.
For example, the following specification is not legal:
570
Defining Persistent Data
Primary key declaration A primary key declaration specifies a distinguished key, as implemented in
relational systems. You can define a primary key in any storage home
without a base storage home.
You can define a primary key in two ways:
• primary key key-spec, where key-spec denotes a key that is declared
in one of the implemented abstract storagehomes.
• primary key ref tells the PSS implementation to use the state
members of the reference representation as the primary key.
571
CHAPTER 19 | Persistent State Service
Storage object
incarnations
Process A Storage
objects
Sessions
Storage home instances
Process B
Storage
homes
Datastore
Storage home instances
572
Accessing Storage Objects
3 Create storage object and storage home factories and register them with a
Connector object. This allows PSS to create storage object incarnations and
storage home instances in the server process, and thereby enable access to
the corresponding datastore objects.
For each PSDL storage home and storage object implementation, the IDL
compiler, using the -pss_r switch, generates a factory creation and
registration operation.For example, given a PSDL storage home definition of
BankDemoStore::Bank, you can instantiate its storage home factory as
follows:
573
CHAPTER 19 | Persistent State Service
int
main(int argc, char** argv)
{
// ...
try
{
// Initialise the ORB as configured in the IMR
CORBA::Object_var obj =
global_orb->resolve_initial_references("TransactionCurrent");
CosTransactions::Current_var tx_current =
IT_PSS::Connector::_narrow(obj);
assert(!CORBA::is_nil(tx_current));
574
Accessing Storage Objects
CORBA::Object_var obj =
global_orb->resolve_initial_references("PSS");
IT_PSS::connector_var connector =
IT_PSS::Connector::_narrow(obj);
assert(!CORBA::is_nil(connector));
connector->register_storage_object_factory(
BankDemoStore::_tc_Account->id(),
acct_factory);
connector->register_storage_home_factory(
BankDemoStore::_tc_Bank->id(),
bank_factory);
acct_factory->_remove_ref();
bank_factory->_remove_ref();
// ...
// continuation depends on whether you use Orbix
SessionManager
// or PSS TransactionalSessions
//...
The sections that follow describe the different ways to continue this code,
depending on whether you use a SessionManager or standard PSS
transactional sessions.
575
CHAPTER 19 | Persistent State Service
576
Accessing Storage Objects
CosPersistentState::ParameterList parameters(2);
parameters.length(2);
parameters[0].name = CORBA::string_dup("to");
parameters[0].val <<= CORBA::Any::from_string("bank", true);
parameters[1].name = CORBA::string_dup("single writer");
parameters[1].val <<= CORBA::Any::from_boolean(true);
IT_PSS::SessionManager_var session_mgr =
connector->it_create_session_manager(parameters);
IT_PSS::Statement_var statement =
association.get_session_nc()->it_create_statement();
result_set->close();
association.suspend();
// ...
return 0;
}
577
CHAPTER 19 | Persistent State Service
The parameter list must specify the Resource that sessions connect to—for
example, an ODBC datasource name or Oracle database name. Table 22
describes all parameter settings
rw pool size long Initial size of the pool of read-write transactional sessions
managed by the session manager. The value must be
between 1 and 1000, inclusive.
The default value is 1.
grow pool boolean If set to TRUE, specifies to create a new session to process
a new request when all read-write transactional sessions
are busy. A value of FALSE, specifies to wait until a
read-write transactional session becomes available.
The default value is FALSE.
single writer boolean Can be set to TRUE only if rw pool size is 1. In this case,
specifies to create a single read-write transactional
session that allows only one writer at a time.
The default value is FALSE.
578
Accessing Storage Objects
module IT_PSS {
// ...
local interface Connector : CosPersistentState::Connector
{
SessionManager
it_create_session_manager(
in CosPersistentState::ParameterList parameters);
};
}
namespace IT_PSS {
//...
class TxSessionAssociation {
public:
TxSessionAssociation(
SessionManager_ptr session_mgr,
CosPersistentState::AccessMode access_mode
) throw (CORBA::SystemException);
579
CHAPTER 19 | Persistent State Service
TxSessionAssociation(
SessionManager_ptr session_mgr,
CosPersistentState::AccessMode access_mode,
CosTransactions::Coordinator_ptr tx_coordinator
) throw (CORBA::SystemException);
~TxSessionAssociation()
throw(CORBA::SystemException);
// ...
};
The first constructor supplies only the session manager and access mode.
This constructor uses the default coordinator object that is associated with
the current transaction (CosTransactions::Current). The second
constructor lets you explicitly specify a coordinator; or to specify no
coordinator by supplying _nil(). If you specify _nil(), the association uses
the shared transaction that is associated with the shared read-only session;
therefore, the access mode must be READ_ONLY.
A new association is initially in an active state—that is, it allows
transactions to use the session to access storage objects. You can change
the association’s state by calling suspend() or end() operations on it (see
page 581).
Association object operations An association object has several operations that are defined as follows:
namespace IT_PSS {
// ...
class TxSessionAssociation{
public:
// ...
TransactionalSession_ptr get_session_nc
const throw();
CosTransactions::Coordinator_ptr get_tx_coordinator_nc()
const throw();
void suspend()
throw (CORBA::SystemException);
580
Accessing Storage Objects
get_session_nc() returns the session for this association object. After you
obtain the session, you can access storage objects in the datastore that this
session connects to.
581
CHAPTER 19 | Persistent State Service
Using an association to access You can use an association object to access the data in storage objects. The
storage objects example shown earlier (see page 576) queries the data in all Account
storage objects in the Bank storage home. In order to obtain data from a
given storage object, you typically follow this procedure:
1. Create an association between a session manager and the current
transaction.
2. Call get_session_nc() on the association to retrieve the session
manager’s current session.
3. Call find_storage_home() on the session to retrieve the storage home.
4. Use the storage home to access the storage objects that it maintains.
The methods used to retrieve and access the storage objects are left up to
the developer to implement. The most basic way is to use the
find_by_pid() and find_by_short_pid() operations provided by the API.
This does not stop the developer from providing implementation specific
methods of to locate and manipulate storage objects.
582
Accessing Storage Objects
After you create and register storage object and storage home factories, you
create a session and associate transactions with it as follows:
1. Create a TransactionalSession by calling
create_transactional_session() on a Connector object.
2. Activate the transactional session by calling start() on it. The
transactional session creates a new CosTransactions::Resource, and
registers it with the transaction.
For more information about CosTransactions::Resource objects, see
the CORBA OTS Programmers Guide.
3. Use the session-Resource association to perform transactional
operations on the datastore’s storage objects.
Creating a transactional session Sessions are created through Connector objects. A Connector is a local
object that represents a given PSS implementation.
Each ORB-implementation provides a single instance of the local Connector
interface, which you obtain through resolve_initial_references("PSS")
then narrowing the returned reference to a
583
CHAPTER 19 | Persistent State Service
module CosPersistentState {
// ...
// forward declarations
local interface TransactionalSession;
// ...
struct Parameter
{
string name;
any val;
};
// ...
TransactionalSession create_transactional_session(
in AccessMode access_mode,
in IsolationLevel default_isolation_level,
in EndOfAssociationCallback callback,
in TypeId catalog_type_name,
in ParameterList additional_parameters);
};
// ...
};
584
Accessing Storage Objects
• The default isolation level for all Resource objects to be created by the
session. The CosPersistentState module defines four
IsolationLevel constants:
♦ READ_UNCOMMITTED
♦ READ_COMMITTED
♦ REPEATABLE_READ
♦ SERIALIZABLE
• A callback object to invoke when a session-Resource association ends
(see page 585).
• A ParameterList that specifies the datastore to connect to, and
optionally other session characteristics (see page 585).
End-of-association callbacks
When a session-Resource association ends, the session might not become
available immediately. For example, if the session is implemented with an
ODBC or JDBC connection, the PSS implementation needs this connection
until the Resource is committed or rolled back.
A session pooling mechanism might want to be notified when PSS releases
a session. You can do this by passing a EndOfAssociationCallback local
object to the Connector::create_transactional_session operation:
module CosPersistentState {
// ...
local interface EndOfAssociationCallback {
void released(in TransactionalSession session);
};
};
ParameterList settings
You set session parameters in a ParameterList, which is a sequence of
Parameter types. Each Parameter is a struct with name and val members:
585
CHAPTER 19 | Persistent State Service
The parameter list must specify the Resource that sessions connect to—for
example, a Berkeley DB environment name. Table 23 describes all
parameter settings
single writer boolean Can be set to TRUE only if this session is the only session
that writes to this database. A value of TRUE eliminates
the risk of deadlock; the cache can remain unchanged
after a commit.
The default value is FALSE.
Activating a transactional session When you create a transactional session, it is initially in an inactive state—
that is, the session is not associated with any Resource. You associate the
session with a Resource by calling start() on it, supplying the name of a
transaction’s coordinator object (see page 588). This function associates the
session with a Resource, and registers the Resource with the coordinator’s
transaction.
586
Accessing Storage Objects
destruction
creation
INACTIVE ENDING
end
sta rt
end
start
suspend
ACTIVE SUSPENDED
start
module CosPersistentState {
// ...
typedef short IsolationLevel;
const IsolationLevel READ_UNCOMMITTED = 0;
const IsolationLevel READ_COMMITTED = 1;
const IsolationLevel REPEATABLE_READ = 2;
const IsolationLevel SERIALIZABLE = 3;
587
CHAPTER 19 | Persistent State Service
AssociationStatus get_association_status();
CosTransactions::Coordinator get_transaction();
IsolationLevel
get_isolation_level_of_associated_resource();
};
};
588
Accessing Storage Objects
589
CHAPTER 19 | Persistent State Service
SERIALIZABLE None
590
Accessing Storage Objects
module CosPersistentState {
interface CatalogBase {
readonly attribute AccessMode access_mode;
StorageHomeBase
find_storage_home(in string storage_home_type_id)
raises (NotFound);
StorageObjectBase
find_by_pid(in Pid the_pid) raises (NotFound);
void flush();
void refresh();
void free_all();
void close();
};
// ...
find_by_pid() searches for the specified storage object among the storage
homes that are provided by the target session. If successful, the operation
returns an incarnation of the specified storage object; otherwise, it raises the
exception NotFound.
591
CHAPTER 19 | Persistent State Service
refresh() refreshes any cached storage object incarnations that are accessed
by this session. This operation is liable to invalidate any direct reference to a
storage object incarnation’s data member.
free_all() sets to 0 the reference count of all PSDL storage objects that have
been incarnated for the given session.
PSDL storage object instances are reference-counted by the application.
Freeing references can be problematic for storage objects that hold
references to other storage objects. For example, if storage object A holds a
reference to storage object B, A’s incarnation owns a reference count of B’s
incarnation. When storage objects form a cyclic graph, the corresponding
instances own reference count of each other. For example, the following
PSDL storage type definition contains a reference to itself:
abstract storagetype Person {
readonly state string full_name;
state ref<Person> spouse;
};
When a couple is formed, each Person incarnation maintains the other
Person’s incarnation in memory. Therefore, the cyclic graph can never be
completely released even if you correctly release all reference counts. In this
case, the application must call free_all().
close() terminates the session. When the session is closed, it is also flushed.
If the session is associated with one or more transactions (see below) when
close() is called, these transactions are marked as roll-back only.
592
Accessing Storage Objects
593
CHAPTER 19 | Persistent State Service
Querying Data
Orbix PSS provides simple JDBC-like queries.You use an
IT_PSS::CatalogBase to create a Statement. For example:
IT_PSS::Statement_var stmt = catalog->it_create_statement();
Then you execute a query that returns a result set:
594
Accessing Storage Objects
595
CHAPTER 19 | Persistent State Service
Thread Safety
A storage object can be used like a struct: it is safe to read concurrently the
same storage object incarnation, but concurrent writes or concurrent
read/write are unsafe. This behavior assumes that a writer typically uses its
own transaction in a single thread; it is rare for an application to make
concurrent updates in the same transaction.
Flushing or locking a storage object is like reading this object. Discarding an
object is like updating it.
A number of CosPersistentState::Session operations are not thread-safe
and should not be called concurrently. No thread should use the target
session, or any object in the target session such as a storage object
incarnation or storage home, when one of the following operations is called:
Session::free_all()
Session::it_discard_all()
Session::refresh()
Session::close()
TransactionalSession::start()
TransactionalSession::suspend()
TransactionalSession::end()
OTS operations are thread-safe. For example one thread can call
tx_current->rollback() while another thread calls start(), suspend(), or
end() on a session involved in this transaction, or while a thread is using
storage objects managed by that session.
596
Using Replication
Using Replication
Overview The persistent state service provides the ability to create replicated
databases. This facility can be used to make persistent data more highly
available and provide some load distribution.
Replication model The persistent state service implements a simple replication model where
there is one master that allows both read and write access to data and many
slaves that provide read-only access to the data. Failure of a master results
in automatic promotion of a slave to being the new master. Depending on
the implementation of the data server, the slaves can forward read requests
on to their associated master. By implementing the server in such a way,
you minimize the impact on clients wishing to access replicated data.
Platform constraints Due to limitations in Berkeley DB, the master and all its replicas must be on
platforms with a common transaction log format. This means that the
platforms must use the same endian format and the same data width.
597
CHAPTER 19 | Persistent State Service
Ordinary read operations An ordinary read operation is a non-transactional read operation or a local
(non-distributed) transactional read operation. In this case, the replica can
access the database directly, as shown in Figure 34.
598
Using Replication
Transactional read operations A transactional read operation is a read operation that requires read access
to the database backend and takes place in the context of a distributed OTS
transaction. In this case, the replica must delegate to the master in order to
access the database, as shown in Figure 35.
599
CHAPTER 19 | Persistent State Service
Write operations A write operation can either be an ordinary write operation or a transactional
write operation. In both cases, the replica must delegate to the master in
order to access the database, as shown in Figure 36.
600
Using Replication
Outline of a delegation interface Example 82 shows the outline of a typical delegation interface,
MasterDelegate. You can use any name for the delegation interface.
// IDL
interface MasterDelegate {
// Part I - Operations that update the Database
// Two kinds of operations must delegate to the master:
// - Transactional reads
// - Writes (ordinary or transactional)
...
... // <--- Insert your operations here!
601
CHAPTER 19 | Persistent State Service
602
Using Replication
Information needed for replica In additional to the usual application configuration, the following
group configuration information is needed to configure a replica group:
• Replica names.
• Delegate IORs.
Replica names Each replica in a group has a unique name. By default the name used is the
ORB’s name, but this can be changed through configuration. For more
details see the plugins:pss_db namespace in the Configuration Guide.
Delegate IORs The delegation interface is the primary point of contact between replicas in a
group (see “Custom Delegation Interface” on page 601). Therefore, each
replica must have access to the complete list of delegate IORs for the group.
Approaches to configuring a Orbix does not mandate a specific approach to configuring a replica group.
replica group There are a number of options:
• Using the Orbix configuration file.
• Using the CORBA naming service.
• Using another method.
603
CHAPTER 19 | Persistent State Service
Using the Orbix configuration file You can use the Orbix configuration file to store a list of replica
name/delegate IOR pairs. For this approach, you need to use the IT_Config
programming interface to access a custom setting in the Orbix
configuration—see “Configuring and Logging” on page 771 for details.
For example, the CORBA naming service uses the following setting to
configure itself as a replicated cluster:
IT_NameServiceReplicas = [
"iona_services.naming.host01=IOR:......",
"iona_services.naming.host02=IOR:......",
"iona_services.naming.host03=IOR:......", ... ];
Where each entry in the list has the form:
ReplicaName=StringifiedDelegateIOR
Using the CORBA naming service You can use the CORBA naming service to store the delegate IORs.
For example, you could designate a naming context to hold the data for a
particular replica group. In this naming context, you can store all of the
delegate object references for the replica group, where the name of each
delegate object reference is the replica name.
Using another method Since there are no restrictions on the approach to storing configuration
information for a replica group, you can use any other method—for example,
storing the information in a custom configuration file.
604
Using Replication
Initializing a replica group Initializing a replica group consists of essentially the following steps:
1. Read a table of replica name/delegate IOR pairs from configuration.
2. Iterate over all of the delegate objects, calling get_pss_replica() on
each one to obtain a list of dynamic replica objects.
3. Use the list of dynamic replica objects (excluding the replica object for
the current application) to initialize the PSS.
Example NamedReplica class Example 83 shows an example of a class, NamedReplica, that is used to
store a replica name/delegate IOR pair. A list of NamedReplica objects can
be used to store a table of delegate IORs.
// C++
class NamedReplica {
public:
IT_String m_name;
CORBA::Object_var m_obj
NamedReplica();
NamedReplica(const char* name, CORBA::Object_var obj);
~NamedReplica();
};
605
CHAPTER 19 | Persistent State Service
Creating a replica group using the Example 84 shows how to initialize a replica group for a session using the
SessionManager interface SessionManager interface. Starting with a table of replica name/delegate
IOR pairs (stored in ns_list), this code fragment iterates over all of the
remote delegate objects, calling get_pss_replica() on each to construct a
list of remote IT_PSS::DynamicReplica object references.
// C++
2 IT_PSS::DynamicReplicaSeq replicas(10);
replicas.length(0);
3 ReplicaList ns_list(it_orb);
replicas.length(replicas.length() + 1);
replicas[replicas.length() - 1] =
IT_PSS::DynamicReplica::_duplicate(replica.in());
8 if (replica_is_master)
{
606
Using Replication
m_remote_master_delegate =
MasterDelegate::_duplicate(delegate.in());
}
}
9 catch (const CORBA::TRANSIENT&)
{
// Caught TRANSIENT exception; ignoring replica.
continue;
}
catch (const CORBA::COMM_FAILURE&)
{
// Caught COMM_FAILURE exception; ignoring replica.
continue;
}
...
}
}
CosPersistentState::ParameterList parameters(4);
parameters.length(4);
parameters[0].name = CORBA::string_dup("to");
parameters[0].val <<= to_string.in();
parameters[1].name = CORBA::string_dup("single writer");
parameters[1].val <<= CORBA::Any::from_boolean(IT_TRUE);
parameters[2].name = CORBA::string_dup("ping period");
parameters[2].val <<= CORBA::ULong(10);
10 parameters[3].name = CORBA::string_dup("replicas");
parameters[3].val <<= replicas;
607
CHAPTER 19 | Persistent State Service
608
Using Replication
Creating a replica group using the Example 85 shows how to create a replica using the TransactionalSession
TransactionalSession interface interface. Use this to replace the last line of code from Example 84 on
page 606.
1 CosPersistentState::TransactionalSession_var =
connector->create_transactional_session(
2 READ_ONLY,
3 SERIALIZABLE,
4 NULL,
5 NULL,
6 parameters);
Election of a master When there is only one member in a replica group, that replica is started as
a master. When there are two or more members, each replica is started as a
slave and an election is used to decide which replica will be the master.
The operation IT_PSS::TransactionalSession::is_replica() can be used
to determine whether the current replica is a master or a slave. This function
has the following return values: true, if the current replica is a slave; false,
if the current replica is the master.
609
CHAPTER 19 | Persistent State Service
Implementing a replicated Example 86 shows the typical outline implementation of an operation that is
operation capable of replicating any updates to the back-end database.
// C++
610
Using Replication
{
// Error: Failed to find master
// Log and throw appropriate exception...
}
}
catch (...)
{
5 // Log and throw appropriate exception...
}
}
611
CHAPTER 19 | Persistent State Service
Refreshing the master Example 87 shows the implementation of the find_master() function,
which is responsible for refreshing the master and finding the new master
delegate’s object reference. This function is called whenever the current
master becomes uncontactable.
// C++
IT_Bool
PerORBInfo::find_master()
{
1 IT_PSS::TransactionalSession_ptr tx_session =
session_mgr_nc()->get_shared_read_only_session_nc();
IT_PSS::TransactionalSession2_var tx_session2 =
IT_PSS::TransactionalSession2::_narrow(tx_session);
if (CORBA::is_nil(tx_session2.in()))
{
return IT_false;
}
TimeBase::TimeT timeout =
refresh_master_interval() * 10000000L;
2 CORBA::String_var name =
tx_session2->refresh_master(timeout);
3 if(strcmp(name.in(),"") == 0)
{
// No master found!
return IT_false;
}
4 if(strcmp(name.in(),replica_name_nc()) == 0)
{
// We are the master
return IT_true;
}
5 ReplicaList ns_list(orb_nc());
IT_Bool found_master = IT_false;
6 for (ReplicaList::iterator iter = ns_list.begin();
iter != ns_list.end();
iter++)
{
if (strcmp (name.in(),(*iter).name().c_str()) == 0)
{
MasterDelegate_var delegate =
MasterDelegate::_narrow((*iter).obj_nc());
612
Using Replication
7 set_remote_master_delegate(delegate);
found_master = IT_true;
break;
}
}
8 if(!found_master)
{
// Couldn't find a new master.
return IT_false;
}
return IT_true;
}
Note: You can check at any time whether or not the current replica
is the master by calling the
IT_PSS::TransactionalSession::is_replica() operation. A return
value of false implies the current application is the master.
613
CHAPTER 19 | Persistent State Service
Current master replica name The name of the current master replica is available from the operation
IT_PSS::TransactionalSession2::refresh_master()—see Example 88.
This returns either the name of the current master or an empty string, if
there is no master available. It takes a timeout parameter that allows the
function to block for a period until a new master is found.
// C++
// Set timeout for 30 seconds
TimeBase::TimeT timeout = 30 * 10000000L;
IT_PSS::TransactionalSession_var tx_session = ...
IT_PSS::TransactionalSession2_var tx_session2 =
IT_PSS::TransactionalSession2::_narrow(session.in());
CORBA::String_var name = tx_session2->refresh_master(timeout);
614
PSDL Language Mappings
Factories and connector The CosPersistentState module defines factories to create instances of all
operations user-defined classes, and operations to register them with a given
connector:
module CosPersistentState {
native StorageObjectFactory;
native StorageHomeFactory;
native SessionFactory;
interface Connector {
StorageObjectFactory
register_storage_object_factory(
in TypeId storage_type_name,
in StorageObjectFactory factory
);
615
CHAPTER 19 | Persistent State Service
StorageHomeFactory
register_storage_home_factory(
in TypeId storage_home_type_name,
in StorageHomeFactory factory
);
SessionFactory
register_session_factory(
in TypeId catalog_type_name,
in SessionFactory factory
);
// ...
};
};
Each register_ operation returns the factory previously registered with the
given name; it returns NULL if there is no previously registered factory.
module CosPersistentState {
enum YieldRef { YIELD_REF };
enum ForUpdate { FOR_UPDATE };
};
616
PSDL Language Mappings
abstract storagehome
The language mappings for abstract storage homes are defined in terms of
an equivalent local interface: the mapping of an abstract storage home is
the same as the mapping of a local interface of the same name.
Inherited abstract storages homes map to inherited equivalent local
interfaces in the equivalent definition.
The equivalent local interface of an abstract storage home that does not
inherit from any other abstract storage home inherits from local interface
CosPersistentState::StorageHomeBase.
617
CHAPTER 19 | Persistent State Service
abstract storagetype
An abstract storage type definition is mapped to a C++ abstract base class
of the same name. The mapped C++ class inherits (with public virtual
inheritance) from the mapped classes of all the abstract storage type
inherited by this abstract storage type.
For example, given this PSDL abstract storage type definition:
class A :
public virtual CosPersistentState::StorageObject {};
class ARef { /* ... */};
class B : public virtual A {};
class BRef {/*... */};
Ref class For each abstract storage type and concrete storage type definition, the IDL
compiler generates the declaration of a concrete C++ class with Ref
appended to its name.
A Ref class behaves like a smart pointer: it provides an operator->() that
returns the storage object incarnation corresponding to this reference; and
conversion operators to convert this reference to the reference of any base
type.
Note: Ref types manage memory in the same way as _ptr reference
types. For functionality that is equivalent to a _var reference type, the IDL
compiler (with the -psdl switch) also generates Ref_var types (see
page 622).
618
PSDL Language Mappings
Ref class members Each Ref class has the following public members:
• Default constructor that creates a NULL reference.
• Non-explicit constructor takes an incarnation of the target storage type.
• Copy constructor.
• Destructor.
• Assignment operator.
• Assignment operator that takes an incarnation of the target [abstract]
storage type.
• operator->() that dereferences this reference and returns the target
object. The caller is not supposed to release this incarnation.
• deref() function that behaves like operator->()
• release() function that releases this reference
• destroy_object() that destroys the target object
• get_pid() function which returns the pid of the target object.
• get_short_pid() function which returns the short-pid of the target
object.
• is_null() function that returns true only if this reference is NULL.
• get_storage_home() function that returns the storage home of the
target object.
• For each direct or indirect base class of the abstract storage type, a
conversion operator that converts this object to the corresponding Ref.
Each reference class also provides a typedef to its target type,
_target_type. This is useful for programming with templates.
Reference class example For example, given this abstract storage type:
abstract storagetype A {};
619
CHAPTER 19 | Persistent State Service
class ARef
{
public:
typedef A _target_type;
// Constructors
ARef() throw ();
ARef( A* target ) throw ();
ARef( const ARef& ref) throw ();
// Destructor
~ARef() throw ();
// Assignment operator
ARef& operator=( const ARef& ref ) throw ();
ARef& operator=(T* obj) throw ();
// Conversion operators
operator CosPersistentState::StorageObjectRef() const
throw();
CosPersistentState::Pid*
get_pid() const throw (CORBA::SystemException);
CosPersistentState::ShortPid*
get_short_pid() const throw (CORBA::SystemException);
CosPersistentState::StorageHomeBase_ptr
get_storage_home() const throw (CORBA::SystemException);
620
PSDL Language Mappings
PSDL C++
in ref<S> SRef
621
CHAPTER 19 | Persistent State Service
Ref_var Classes
The _var class associated with a _var provides the same member functions
as the corresponding Ref class, and with the same behavior. It also provides
these members:
• The ref() function returns a pointer to the managed reference, or 0 if
the managed reference is NULL.
• Constructors and assignment operators that accept Ref pointers.
622
PSDL Language Mappings
State Members
Each state member is mapped to a number of overloaded public pure virtual
accessor and modifier functions, with the same name as the state member.
These functions can raise any CORBA standard exception.
A state member of a basic C++ type is mapped like a value data member.
There is no modifier function if the state member is read-only.
For example, the following PSDL definition:
// PSDL
abstract storagetype Person {
state string name;
};
Reference to abstract storage type A state member whose type is a reference to an abstract storage type is
mapped to two accessors and two modifier functions. One of the accessor
functions takes no parameter and returns a storage object incarnation, the
other takes a CosPersistentState::YieldRef parameter and returns a
reference. One of the modifier functions takes an incarnation, the other one
takes a reference. If the state member is read-only, only the accessor
functions are generated.
623
CHAPTER 19 | Persistent State Service
Read-only state member If the state member is read-only, only the read-only accessor is generated.
For example, the following PSDL definition:
624
PSDL Language Mappings
Operation Parameters
Table 26 shows the mapping for parameters of type S and ref <S> (where S
is an abstract storage type:.
(return) S (return) S*
625
CHAPTER 19 | Persistent State Service
storagetype
A storagetype is mapped to a C++ class of the same name. This class
inherits from the mapped classes of all the abstract storage types
implemented by the storage type, and from the mapped class of its base
storage type, if any. This class also provides a public default constructor.
All state members that are implemented directly by the storage type are
implemented by the mapped class as public functions.
For example, the following PSDL definition:
// a portable implementation:
struct Entry {
string from;
string to;
};
typedef sequence<Entry> EntryList;
626
PSDL Language Mappings
For each storage type, a concrete Ref class is also generated. This Ref class
inherits from the Ref classes of all the abstract storage types that the
storage type implements, and from the Ref class of the base storage type, if
any.
The IDL compiler generates Ref class declarations for a storage type exactly
as it does for an abstract storage type. For more information, see page 618.
627
CHAPTER 19 | Persistent State Service
storagehome
A storagehome is mapped to a C++ class of the same name. This class
inherits from the mapped classes of all the abstract storage homes
implemented by the storage home, and from the mapped class of its base
storage home, if any. This class also provides a public default constructor.
A storage home class implements all finder operations implicitly defined by
the abstract storage homes that the storage home directly implements.
The mapped C++ class provides two public non-virtual _create() member
functions with these signatures:
• A parameter for each storage type state member. This _create()
function returns an incarnation.
• A parameter for each storage type state member, and a
CosPersistentState::YieldRef parameter. This _create() function
returns a reference.
It also provides two public virtual _create() member functions with these
signatures:
• A parameter for each storage type’s reference representation members.
This _create() function returns an incarnation
• A parameter for each storage type’s reference representation members,
and a CosPersistentState::YieldRef parameter. This _create()
function returns a reference.
For example, given the following definition of storage home
PortableBookStore:
628
PSDL Language Mappings
The IDL compiler (with the pss_r backend) generates the C++ class
PortableBookStore:
629
CHAPTER 19 | Persistent State Service
namespace CosPersistentState {
template class<T>
class Factory {
public:
virtual T* create()
throw (SystemException) = 0;
virtual void _add_ref() {}
virtual void _remove_ref() {}
virtual ~Factory() {}
};
630
CHAPTER 20
Event Service
The event service enables decoupled communication between
client consumers and suppliers by forwarding messages
through an event channel.
An event originates at a client supplier and is forwarded through an event
channel to any number of client consumers. Suppliers and consumers are
completely decoupled: a supplier has no knowledge of the number of
consumers or their identities, and consumers have no knowledge of which
supplier generated a given event.
631
CHAPTER 20 | Event Service
Overview
Service capabilities An event channel provides the following capabilities for forwarding events:
• Enables consumers to subscribe to events of certain types.
• Accepts incoming events from client suppliers.
• Forwards supplier-generated events to all connected consumers.
• Forwarding messages using well defined IDL interfaces.
Connections Suppliers and consumers connect to an event channel and not directly to
each other, as shown in Figure 37. From a supplier’s perspective, the event
channel appears as a single consumer; from a consumer’s perspective, the
event channel appears as a single supplier. In this way, the event channel
decouples suppliers and consumers.
Event propagation
Event Channel
Suppliers
Consumers
How many clients? Any number of suppliers can issue events to any number of consumers using
a single event channel. There is no correlation between the number of
suppliers and the number of consumers. New suppliers and consumers can
be easily added to or removed from the system. Furthermore, any supplier
or consumer can connect to more than one event channel.
632
Overview
Supplier
Consumers
Event Channel
1. Supplier calls operation
on event channel
633
CHAPTER 20 | Event Service
Push model In the push model, suppliers generate events and actively pass them to an
event channel. In this model, consumers wait for events to arrive from the
channel.
Figure 39 illustrates a push model architecture in which push suppliers
communicate with push consumers through the event channel.
Event propagation
Event Channel
Push
suppliers
Push
consumers
634
Event Communication Models
Pull model In the pull model, a consumer actively requests events from the channel.
The supplier waits for a pull request to arrive from the channel. When a pull
request arrives, event data is generated and returned to the channel.
Figure 40 illustrates a pull model architecture in which pull consumers
communicate with pull suppliers through the event channel.
Event propagation
Event Channel
Pull
suppliers
Pull
consumers
Mixing push and pull models Because suppliers and consumers are completely decoupled by the event
channel, push and pull models can be mixed in a single system.
635
CHAPTER 20 | Event Service
For example, suppliers can connect to an event channel using the push
model, while consumers connect using the pull model, as shown in
Figure 41.
Event propagation
Event Channel
Push
suppliers
Pull
consumers
Typed push model In the typed push model suppliers connect to the channel using a consumer
proxy that supports a user defined interface. The supplier then pushes
strongly typed events to the channel by invoking the operations supported
by the interface.
636
Event Communication Models
Figure 42 shows how typed push suppliers forward events to typed push
consumers through a typed event channel. Push suppliers can only forward
event messages to typed push consumers that support the agreed upon
interface.
Event propagation
Interfa eJ
ce I rfac
In t e
Interface I
Typed Event Channel Interface J
Interface J
I I nt e
ce rfac
a e J
e rf
Int
Push Typed push
suppliers consumers
Figure 42: Push consumers pushing typed events to typed push consumers
637
CHAPTER 20 | Event Service
638
Developing an Application Using Untyped Events
Event channel factory Orbix provides the EventChannelFactory interface, which provides the
operations to create and discover event channels:
module IT_EventChannelAdmin
{
typedef long ChannelID;
struct EventChannelInfo
{
string name;
ChannelID id;
CosEventChannelAdmin::EventChannel reference;
};
typedef sequence<EventChannelInfo> EventChannelInfoList;
CosEventChannelAdmin::EventChannel find_channel(
in string name,
out ChannelID id)
raises (ChannelNotFound);
639
CHAPTER 20 | Event Service
CosEventChannelAdmin::EventChannel find_channel_by_id(
in ChannelID id,
out string name)
raises (ChannelNotFound);
EventChannelInfoList list_channels();
};
};
Event channel factory operations You can call one of several operations on an event channel factory to create
or find an event channel. By providing both create and find operations, the
event service allows any client or supplier to create an event channel, which
other clients and suppliers can subsequently discover:
Example The following code can be used by any supplier or consumer to obtain an
event channel.
CosEventChannelAdmin::EventChannel_var ec;
IT_EventChannelAdmin::ChannelID id;
1 CORBA::Object_var obj =
orb->resolve_initial_references("EventChannelFactory");
IT_EventChannelAdmin::EventChannelFactory_var factory =
IT_EventChannelAdmin::EventChannelFactory::_narrow(obj);
2 try {
ec = factory->create_channel("EventChannel", id);
}
3 catch (IT_EventChannelAdmin::ChannelAlreadyExists&) {
640
Developing an Application Using Untyped Events
4
// Channel has been previously created, so find it
try {
ec = factory->find_channel("EventChannel", id);
}
catch (IT_EventChannelAdmin::ChannelNotFound&) {
cerr << "Couldn't create or find the event channel" <<
endl;
exit(1);
}
catch (CORBA::SystemException& event_msg) {
cerr << "System exception occurred during find_channel: "
<< event_msg << endl;
exit(1);
}
} // catch ChannelAlreadyExists
641
CHAPTER 20 | Event Service
Implementing a Supplier
Actions A client supplier program performs the following actions:
1. Instantiates suppliers using the appropriate interface in module
CosEventComm.
2. Connects suppliers to the event channel.
3. Sends event messages to the event channel.
4. Disconnects from the event channel.
Instantiating the Supplier You instantiate a push supplier with the PushSupplier interface; and a pull
supplier with the PullSupplier interface. Both are defined in the IDL
module CosEventComm:
module CosEventComm {
exception Disconnected {};
interface PullSupplier
{
any pull() raises (Disconnected);
any try_pull (out boolean has_event)
raises (Disconnected);
void disconnect_pull_supplier();
};
interface PushSupplier
{
void disconnect_push_supplier();
};
};
Connecting to a Channel In order to pass messages to the event channel, a supplier must connect to
it through a proxy consumer that receives events from the supplier. Each
supplier must have its own proxy consumer. The proxy consumer passes the
events down the channel.
642
Developing an Application Using Untyped Events
Obtain a SupplierAdmin
On creation, an event channel instantiates a default SupplierAdmin object,
which you obtain by calling for_suppliers() on the event channel. For
example:
CosEventChannelAdmin::SupplierAdmin_var sa =
channel->for_suppliers();
module CosEventChannelAdmin
{
exception AlreadyConnected {};
exception TypeError {};
643
CHAPTER 20 | Event Service
Example
The following code obtains a ProxyPushConsumer for a PushSupplier by
calling obtain_push_consumer().
try
{
CosEventChannelAdmin::ProxyConsumer_var ppc =
sa->obtain_push_consumer();
}
644
Developing an Application Using Untyped Events
Example
The following code shows one way to implement a PushSupplier client that
connects itself to a proxy consumer.
Sending Event Messages A client supplier sends event messages in one of two ways:
• A push supplier invokes the push operation on its proxy consumer and
supplies the event as an input argument.
• A pull supplier implements try_pull(). When the proxy consumer
invokes a pull operation, the supplier returns an event message if one
is available.
645
CHAPTER 20 | Event Service
Push supplier
A push supplier invokes the push() operation on its proxy consumer. For
example:
Pull supplier
A pull supplier sends event messages only on request. Whether a client
consumer invokes pull() or try_pull(), the pull supplier’s proxy consumer
always invokes try_pull() on its supplier.
Pull suppliers are responsible for implementing try_pull(), which returns a
CORBA::Any. This operation is non-blocking; it returns immediately with an
output parameter of type boolean to indicate whether the return value
actually contains an event.
For example, the following code implements try_pull() by attempting to
populate an event message with the latest baseball scores.
PullSupplier_i::try_pull(boolean has_event)
throw(CORBA::SystemException)
{
boolean has_scores = false;
boolean has_event = false;
CORBA::Any event_msg;
646
Developing an Application Using Untyped Events
Disconnecting From the Event A client supplier can disconnect from the event channel at any time by
Channel invoking the disconnect operation on its proxy consumer. This operation
terminates the connection between a supplier and its target proxy consumer.
The channel then releases all resources allocated to support its connection
to the supplier, including destruction of the target proxy consumer.
Each proxy consumer interface supports a disconnect operation. For
example, interface ProxyPushConsumer defines
disconnect_push_consumer().
647
CHAPTER 20 | Event Service
Implementing a Consumer
Actions A client consumer program performs the following actions:
1. Instantiates consumers with the appropriate CosEventComm interface.
2. Connects consumers to the event channel.
3. Obtains event messages.
4. Disconnects from the event channel.
Instantiating a Consumer You instantiate a push consumer with the PushConsumer interface; and a
pull consumer with the PullConsumer interface. Both are defined in the IDL
module CosEventComm:
module CosEventComm
{
exception Disconnected { };
interface PushConsumer {
void push( in any data) raises (Disconnected);
interface PullConsumer {
void disconnect_pull_consumer();
};
};
Connecting to the Channel Consumers receive messages from the event channel through a proxy
supplier. Each consumer on the channel has its own proxy supplier. Proxy
suppliers use the same delivery method as their consumers and send the
appropriate message type.
648
Developing an Application Using Untyped Events
Obtain a ConsumerAdmin
On creation, an event channel instantiates a default ConsumerAdmin object,
which you obtain by calling for_consumers() on the event channel. For
example:
CosEventChannelAdmin::ConsumerAdmin_var ca =
channel->for_consumers();
module CosEventChannelAdmin
{
exception AlreadyConnected {};
exception TypeError {};
649
CHAPTER 20 | Event Service
Example
The following code obtains a proxy supplier for a PushConsumer by calling
obtain_push_supplier().
try
{
CosEventChannelAdmin::ProxySupplier_var pps =
ca->obtain_push_supplier();
}
650
Developing an Application Using Untyped Events
interface ProxyPushSupplier :
ProxySupplier,
CosEventComm::PushSupplier
{
void connect_push_consumer
(in CosEventComm::PushConsumer push_consumer)
raises(CosEventChannelAdmin::AlreadyConnected,
CosEventChannelAdmin::TypeError);
};
Example
The following example shows how you might implement a PushConsumer
client that connects itself to a proxy supplier.
Obtaining Event Messages A client consumer obtains event messages in one of two ways:
• A push consumer implements the push() operation. As events become
available, the proxy supplier pushes them to its client consumer.
• A pull consumer invokes pull() or try_pull() on its proxy supplier;
the proxy supplier returns with the next available event.
651
CHAPTER 20 | Event Service
Push consumer
A push consumer implements the push() operation. For example:
Pull consumer
A pull client consumer invokes the pull() or try_pull() operation on its
proxy supplier to solicit event messages; the proxy supplier returns with the
next available event.
The proxy supplier interface supports operations pull() and try_pull(). A
pull consumer invokes one of these operations on its ProxyPullSupplier.
Both operations return a CORBA::Any argument; they differ only in their
blocking mode:
// C++
CORBA::Any* event;
const char * scores;
boolean has_data = false;
652
Developing an Application Using Untyped Events
try{
event = proxy->try_pull(has_data);
}
catch (CosEventComm::Disconnected&){
cerr << "Disconnected exception occurred during pull" <<
endl;
exit(1);
}
catch (CORBA::SystemException& event_msg){
cerr << "System exception occurred during pull" << endl;
exit(1);
}
if (has_data)
{
if (*event >>= scores)
{
cout << "Received event number " << n << "using try_pull"
<< endl;
}
}
Disconnecting From the Event A client consumer can disconnect from the event channel at any time by
Channel invoking the disconnect operation on its proxy supplier. This operation
terminates the connection between the consumer and its target proxy
supplier. The event channel then releases all resources allocated to support
its connection to the consumer, including destruction of the target proxy
supplier.
Each proxy supplier interface supports a disconnect operation. For example,
interface ProxyPushSupplier defines disconnect_push_supplier().
653
CHAPTER 20 | Event Service
654
Developing an Application Using Typed Events
Interface restrictions Because typed event communication is strictly from the supplier to the
consumer, there are two restrictions on the operations of an interface used
for typed event communication:
• They can only have in parameters.
• They cannot have a return type other than void.
Messages cannot be passed through the event channel from consumer to
supplier and these restrictions help reinforce the unidirectional nature of
event forwarding.
\\IDL
interface ScorePusher
{
void push_score(in string team_a, in long score_a,
in string team_b, in long score_b);
};
Once you have written the interface, you must place it into the interface
repository using the following command:
idl -R filename
655
CHAPTER 20 | Event Service
Event channel factory Orbix provides the TypedEventChannelFactory interface, which define the
operations to create and discover typed event channels:
module IT_TypedEventChannelAdmin
{
struct TypedEventChannelInfo
{
string name;
IT_EventChannelAdmin::ChannelID id;
CosTypedEventChannelAdmin::TypedEventChannel reference;
};
typedef sequence<TypedEventChannelInfo>
TypedEventChannelInfoList;
interface TypedEventChannelFactory :
IT_MessagingAdmin::Manager
{
CosTypedEventChannelAdmin::TypedEventChannel
create_typed_channel(in string name,
out IT_EventChannelAdmin::ChannelID id)
raises(IT_EventChannelAdmin::ChannelAlreadyExists);
CosTypedEventChannelAdmin::TypedEventChannel
find_typed_channel(in string name,
out IT_EventChannelAdmin::ChannelID id)
raises(IT_EventChannelAdmin::ChannelNotFound);
656
Developing an Application Using Typed Events
CosTypedEventChannelAdmin::TypedEventChannel
find_typed_channel_by_id(
in IT_EventChannelAdmin::ChannelID id,
out string name)
raises(IT_EventChannelAdmin::ChannelNotFound);
TypedEventChannelInfoList list_typed_channels();
};
};
Typed event channel factory You can call one of several operations on an event channel factory to create
operations or find an event channel. By providing both create and find operations, the
event service allows any client or supplier to create an event channel, which
other clients and suppliers can subsequently discover:
Example The following code can be used by any supplier or consumer to obtain a
typed event channel.
CosTypedEventChannelAdmin::TypedEventChannel_var tec;
IT_EventChannelAdmin::ChannelID id;
657
CHAPTER 20 | Event Service
1 try
{
CORBA::Object_var obj =
orb->resolve_initial_references("EventService");
}
catch (InvalidName)
{
// handle the exception
}
IT_TypedEventChannelAdmin::TypedEventChannelFactory_var
factory =
IT_TypedEventChannelAdmin::TypedEventChannelFactory::_narrow
(obj);
2 try
{
tec = factory->create_typed_channel("TypedChannel", id);
}
3 catch (IT_EventChannelAdmin::ChannelAlreadyExists&)
{
4 // Channel has been previously created, so find it
try
{
tec = factory->find_typed_channel("TypedChannel", id);
}
catch (IT_EventChannelAdmin::ChannelNotFound&)
{
cerr << "Couldn't create or find the event channel" <<
endl;
exit(1);
}
catch (CORBA::SystemException& event_msg)
{
cerr << "System exception occurred during find_channel: "
<< event_msg << endl;
exit(1);
}
} // catch ChannelAlreadyExists
658
Developing an Application Using Typed Events
659
CHAPTER 20 | Event Service
Instantiate the supplier Typed push style event communication uses a generic push supplier to
supply events to typed push consumers. An application that is intended to
push typed events to typed event consumers can instantiate an instance of
the CosEventComm::PushSupplier interface.
If the supplier does not need to be informed if its proxy disconnects from the
channel, the supplier can connect a CosEventComm::PushSupplier::_nil()
reference to the typed proxy consumer.
Connecting to a typed event In order to pass messages to the typed event channel, a supplier must
channel connect to it through a typed proxy consumer that receives events from the
supplier. The proxy consumer passes the events down the channel.
A supplier connects to the typed event channel in three steps:
1. Obtain a TypedSupplierAdmin from the typed event channel.
2. Obtain a typed proxy consumer in the typed event channel, to receive
the events generated by the supplier.
3. Connect a supplier to a typed proxy consumer.
Obtain a TypedSupplierAdmin
On creation, a typed event channel instantiates a default
TypedSupplierAdmin, which you obtain by calling for_suppliers() on the
typed event channel. For example:
CosTypedEventChannelAdmin::TypedSupplierAdmin_var tsa =
tec->for_suppliers();
660
Developing an Application Using Typed Events
module CosTypedEventChannelAdmin
{
exception InterfaceNotSupported {};
exception NoSuchImplementation {};
interface TypedProxyPushConsumer :
CosTypedEventComm::TypedPushConsumer,
CosEventChannelAdmin::ProxyPushConsumer
{
};
}
Example
The following code obtains a TypedProxyPushConsumer for a PushSupplier
by calling obtain_typed_push_consumer().
try
{
CosTypedEventChannelAdmin::TypedProxyConsumer_var tpc =
tsa->obtain_typed_push_consumer("IDL:ScorePusher:1.0");
}
catch (CosTypedEventChannelAdmin::InterfaceNotSupported)
{
// handle the exception
}
661
CHAPTER 20 | Event Service
Pushing typed events In typed push event communication the supplier pushes events to the
consumers by invoking operations on an interface that has been mutually
agreed upon by both the developer responsible for implementing the
supplier and the developer responsible for implementing the consumer.
The supplier obtains a reference to the appropriate interface by invoking its
associated typed proxy consumer’s get_typed_consumer() operation. This
operation returns a reference to the interface specified when
obtain_typed_push_consumer() was invoked to obtain the typed proxy
consumer. The returned reference is of type Object and must be narrowed
to the appropriate interface.
Note: If the supplier and the client do not support the identical interface
the narrow() operation will fail.
662
Developing an Application Using Typed Events
Example 106 shows how a push supplier would pass typed messages to
typed consumers that supported the ScorePusher interface defined earlier.
// C++
Disconnecting From the Event A supplier can disconnect from a typed event channel at any time by
Channel invoking the disconnect_push_consumer() operation. This operation
terminates the connection between a supplier and its target typed proxy
consumer. The channel then releases all resources allocated to support its
connection to the supplier and destroys the target typed proxy consumer.
663
CHAPTER 20 | Event Service
Development tasks The developer of a typed push consumer must complete the following tasks:
• Implement the mutually agreed upon interface.
• Instantiate the consumer using the
CosTypedEventComm::TypedPushConsumer interface.
• Connect the consumer to a typed event channel.
• Receive event messages from the channel and process them.
• Disconnect the consumer from the typed event channel.
Implement the interface The first step in developing a typed push consumer is to implement the
interface that will be used to support the typed events. To do this complete
the following steps:
1. Create a new IDL interface that inherits from the interface that will be
used for event communication and from
CosEventComm::PushConsumer. For the ScorePusher interface the
combined interface for the consumer might look like:
\\IDL
#include <ScorePusher.idl>
#include <omg/CosEventComm.idl>
664
Developing an Application Using Typed Events
// C++
#include <omg/orb.hh>
#include <omg/CosTypedEventChannelAdmin.hh>
#include <orbix/typed_event_channel_admin.hh>
class ScoreConsumer_i: virtual public POA_ScoreConsmuer;
{
// constructor and destructor
// ...
void disconnect_push_consumer()
{
}
};
665
CHAPTER 20 | Event Service
Connecting to the channel Typed push consumers connect to a typed event channel through a proxy
push supplier which receives the events from the channel and forwards
them to the consumer.
The steps to connect a typed push consumer to a typed event channel are
the same as the steps to connect a generic consumer to an event channel,
They are:
1. Obtain a typed consumer admin object from the typed event channel.
2. Obtain a proxy push supplier from the consumer admin.
3. Connect the consumer to the proxy supplier.
CosTypedEventChannelAdmin::TypedConsumerAdmin_var tca =
tec->for_consumers();
try
{
CosEventChannelAdmin::ProxyPushSupplier pps =
tca->obtain_typed_push_supplier("IDL:ScorePusher:1.0");
}
catch (CosTypedEventChannelAdmin::NoSuchImplementation)
{
// no push supplier implements the appropriate interface
// handle the exception
}
666
Developing an Application Using Typed Events
try
{
org.omg.CosEventChannelAdmin.ProxyPushSupplier pps =
tca.obtain_typed_push_supplier("IDL:ScorePusher:1.0");
}
catch (CosTypedEventChannelAdmin.NoSuchImplementation)
{
// no supplier implements the interface
// handle the exception
}
Receiving event messages Typed push consumers passively receive messages from the channel. As
events become available the proxy supplier forwards them to the consumer
using one of the operations in the mutually agreed upon interface. The
operation, which was implemented previously, is responsible for processing
the event.
667
CHAPTER 20 | Event Service
Disconnecting from the event A client consumer can disconnect from the event channel at any time by
channel invoking disconnect_push_consumer(). This operation terminates the
connection between the consumer and its target proxy supplier. The typed
event channel then releases all resources allocated to support its connection
to the consumer and destroys the target proxy supplier.
668
CHAPTER 21
Portable
Interceptors
Portable interceptors providehooks, or interception points,
which define stages within the request and reply sequence.
Services can use these interception points to query
request/reply data, and to transfer service contexts between
clients and servers.
Sample application This chapter shows an application that uses interceptors to secure a server
with a password authorization service as follows:
• A password policy is created and set on the server’s POA.
• An IOR interceptor adds a tagged component to all object references
exported from that POA. This tagged component encodes data that
indicates whether a password is required.
• A client interceptor checks the profile of each object reference that the
client invokes on. It ascertains whether the object is
password-protected; if so, it adds to the outgoing request a service
context that contains the password data.
669
CHAPTER 21 | Portable Interceptors
670
Interceptor Components
Interceptor Components
Portable interceptors require the following components:
671
CHAPTER 21 | Portable Interceptors
Interceptor Types
All portable interceptors are based on the Interceptor interface:
module PortableInterceptor{
local interface Interceptor{
readonly attribute string name;
};
};
An interceptor can be named or unnamed. Among an ORB’s interceptors of
the same type, all names must be unique. Any number of unnamed, or
anonymous interceptors can be registered with an ORB.
All interceptors implement one of the interceptor types that inherit from the
Interceptor interface:
Interception points Each interceptor type defines a set of interception points, which represent
stages in the request/reply sequence. Interception points are specific to each
interceptor type, and are discussed fully in later sections that describe these
types. Generally, in a successful request-reply sequence, the ORB calls
interception points on each interceptor.
For example, Figure 43 shows client-side interceptors A and B. Each
interceptor implements interception points send_request and
receive_reply. As each outgoing request passes through interceptors A and
B, their send_request implementations add service context data a and b to
672
Interceptor Components
Client Server
B
A request
send_request a
send_request add b b
add a
receive_reply
reply receive_reply
client interceptors
Interception point data For each interception point, the ORB supplies an object that enables the
interceptor to evaluate the request or reply data at its current stage of flow:
• A PortableInterceptor::IORInfo object is supplied to an IOR
interceptor’s single interception point establish_components (see
page 682).
• A PortableInterceptor::ClientRequestInfo object is supplied to all
ClientRequestInterceptor interception points (see page 695).
• A PortableInterceptor::ServerRequestInfo object is supplied to all
ServerRequestInterceptor interception points (see page 704).
Much of the information that client and server interceptors require is similar;
so ClientRequestInfo and ServerRequestInfo both inherit from interface
PortableInterceptor::RequestInfo. For more information on
RequestInfo, see page 685.
673
CHAPTER 21 | Portable Interceptors
Service Contexts
Service contexts supply the information a client or server needs to identify
and access an ORB service. The IOP module defines the ServiceContext
structure as follows:
module IOP
{
// ...
typedef unsigned long ServiceId;
struct ServiceContext {
ServiceId context_id;
sequence <octet> context_data;
};
};
674
Interceptor Components
PICurrent
PICurrent is a table of slots that different services can use to transfer their
data to request or reply service contexts. For example, in order to send a
request to a password-protected server, a client application can set the
required password in PICurrent. On each client invocation, a client
interceptor’s send_request interception point obtains the password from
PICurrent and attaches it as service context data to the request.
Client request
client interceptor
client(“vermilion”) send_request
{
get password slot data
add service context "vermilion"
with password
}
PICurrent
Server
Interface definition The PortableInterceptor module defines the interface for PICurrent as
follows:
module PortableInterceptor
{
// ...
typedef unsigned long SlotId;
exception InvalidSlot {};
675
CHAPTER 21 | Portable Interceptors
void
set_slot(in SlotId id, in any data
) raises (InvalidSlot);
};
};
676
Interceptor Components
Tagged Components
Object references that support an interoperability protocol such as IIOP or
SIOP can include one or more tagged components, which supply
information about optional IIOP features and ORB services. A tagged
component contains an identifier, or tag, and component data, defined as
follows:
Note: The OMG is responsible for allocating and registering the tag IDs of
tagged components. Requests to allocate tag IDs can be sent to
[email protected].
677
CHAPTER 21 | Portable Interceptors
Codec
Interface definition The data of service contexts and tagged components must be encoded as a
CDR encapsulation. Therefore, the IOP module defines the Codec interface,
so interceptors can encode and decode octet sequences:
CORBA::OctetSeq
encode(in any data
) raises (InvalidTypeForEncoding);
any
decode(in CORBA::OctetSeq data
) raises (FormatMismatch);
CORBA::OctetSeq
encode_value(in any data
) raises (InvalidTypeForEncoding);
any
decode_value(
in CORBA::OctetSeq data,
in CORBA::TypeCode tc
) raises (FormatMismatch, TypeMismatch);
};
encode converts the supplied any into an octet sequence, based on the
encoding format effective for this Codec. The returned octet sequence
contains both the TypeCode and the data of the type.
decode decodes the given octet sequence into an any, based on the
encoding format effective for this Codec.
678
Interceptor Components
encode_value converts the given any into an octet sequence, based on the
encoding format effective for this Codec. Only the data from the any is
encoded.
decode_value decodes the given octet sequence into an any based on the
given TypeCode and the encoding format effective for this Codec.
679
CHAPTER 21 | Portable Interceptors
Policy Factory
An ORB service can be associated with a user-defined policy. The
PortableInterceptor module provides the PolicyFactory interface, which
applications can use to implement their own policy factories:
local interface PolicyFactory {
CORBA::Policy
create_policy(
in CORBA::PolicyType type,
in any value
) raises (CORBA::PolicyError);
};
Policy factories are created during ORB initialization, and registered through
the ORB initializer (see “Create and register policy factories” on page 719).
680
Interceptor Components
ORB Initializer
ORB initializers implement interface
PortableInterceptor::OrbInitializer:
void
post_init(in ORBInitInfo info);
};
681
CHAPTER 21 | Portable Interceptors
CORBA::Policy
get_effective_policy(in CORBA::PolicyType type);
void
add_ior_component(in IOP::TaggedComponent component);
682
Writing IOR Interceptors
add_ior_component_to_profile (
in IOP::TaggedComponent component,
in IOP::ProfileId profile_id
);
};
ACL_IORInterceptorImpl::ACL_IORInterceptorImpl(
IOP::Codec_ptr codec
) IT_THROW_DECL(()) :
m_codec(IOP::Codec::_duplicate(codec))
{
}
void
ACL_IORInterceptorImpl::establish_components(
PortableInterceptor::IORInfo_ptr ior_info
) IT_THROW_DECL((CORBA::SystemException))
{
CORBA::Boolean requires_password = IT_FALSE;
1 try {
CORBA::Policy_var policy =
ior_info->get_effective_policy(
AccessControl::PASSWORD_POLICY_ID);
AccessControl::PasswordPolicy_var password_policy =
AccessControl::PasswordPolicy::_narrow(policy);
assert(!CORBA::is_nil(password_policy));
683
CHAPTER 21 | Portable Interceptors
2 requires_password = password_policy->requires_password();
}
catch (const CORBA::INV_POLICY&) {
// Policy wasn't set...don't add component
}
CORBA::Any component_data_as_any;
component_data_as_any <<=
CORBA::Any::from_boolean(requires_password);
3 CORBA::OctetSeq_var octets =
m_codec->encode_value(component_data_as_any);
4 IOP::TaggedComponent component;
component.tag = AccessControlService::TAG_REQUIRES_PASSWORD;
component.component_data.replace(octets->length(),
octets->length(),
octets->get_buffer(),
IT_FALSE);
5 ior_info->add_ior_component(component);
}
684
Using RequestInfo Objects
685
CHAPTER 21 | Portable Interceptors
points. Table 28 on page 696 and Table 29 on page 709 show which
RequestInfo operations and attributes are valid for a given interception
point.
Timeout attributes A client might specify one or more timout policies on request or reply
delivery. If portable interceptors are present in the bindings, these
interceptors must be aware of the relevant timeouts so that they can bound
any potentially blocking activities that they undertake.
The current OMG specification for portable interceptors does not account for
timeout policy constraints; consequently, Orbix provides its own derivation
of the RequestInfo interface, IT_PortableInterceptor::RequestInfo,
which adds two attributes:
module IT_PortableInterceptor
{
local interface RequestInfo : PortableInterceptor::RequestInfo
{
readonly attribute TimeBase::UtcT request_end_time;
readonly attribute TimeBase::UtcT reply_end_time;
};
};
request_end_time send_request
send_poll
receive_request_service_contexts
receive_request
686
Using RequestInfo Objects
reply_end_time send_reply
send_exception
send_other
receive_reply
receive_exception
receive_other
687
CHAPTER 21 | Portable Interceptors
Client interceptor constructor As noted earlier, the ORB initializer instantiates and registers the client
interceptor. This interceptor’s constructor is implemented as follows:
688
Writing Client Interceptors
689
CHAPTER 21 | Portable Interceptors
Interception Points
A client interceptor implements one or more interception points. During a
successful request-reply sequence, each client-side interceptor executes one
starting interception point and one ending interception point.
Starting interception points Depending on the nature of the request, the ORB calls one of the following
starting interception points:
Ending interception points Before the client receives a reply to a given request, the ORB executes one
of the following ending interception points on that reply:
690
Writing Client Interceptors
Scenario 1: Request-reply Interception points A and B are registered with the server ORB. The
sequence is successful interception point flow shown in Figure 45 depicts a successful
reply-request sequence, where the server returns a normal reply:
Client
A
send_request
receive_reply
B Server
send_request
receive_reply C
send_request
receive_reply
691
CHAPTER 21 | Portable Interceptors
Scenario 2: Client receives If the server throws an exception or returns some other reply, such as
LOCATION_FORWARD LOCATION_FORWARD, the ORB directs the reply flow to the appropriate
interception points, as shown in Figure 46:
Client
A
send_request
Server
receive_other
B replies with
LOCATION_FORWARD
send_request
receive_other C
send_request
receive_other
692
Writing Client Interceptors
Scenario 3: Exception aborts Any number of events can abort or shorten the interception flow. Figure 47
interception flow shows the following interception flow:
1. Interceptor B’s send_request throws an exception.
2. Because interceptor B’s start point does not complete, no end point is
called on it, and interceptor C is never called. Instead, the request flow
returns to interceptor A’s receive_exception end point.
Client
A
send_request
receive_exception B
send_request
throws exception
C
C
Scenario 4: Interceptor changes An interceptor can change a normal reply to a system exception; it can also
reply change the exception it receives, whether user or system exception to a
different system exception. Figure 48 shows the following interception flow:
1. The server returns a normal reply.
2. The ORB calls receive_reply on interceptor C.
3. Interceptor C’s receive_reply raises exception foo_x, which the ORB
delivers to interceptor B’s receive_exception.
4. Interceptor B’s receive_exception changes exception foo_x to
exception foo_y.
5. Interceptor A’s receive_exception receives exception foo_y and
returns it to the client.
693
CHAPTER 21 | Portable Interceptors
Client
A
send_request
Server
receive_exception
B servant returns
normal reply
send_request
receive_exception
foo_y throws exception
foo_y C
send_request
receive_reply
foo_x throws exception
foo_x
Figure 48: Client interceptors can change the nature of the reply.
694
Writing Client Interceptors
ClientRequestInfo
Each client interception point gets a single ClientRequestInfo argument,
which provides the necessary hooks to access and modify client request
data:
IOP::TaggedComponent
get_effective_component(in IOP::ComponentId id);
IOP::TaggedComponentSeq
get_effective_components(in IOP::ComponentId id);
CORBA::Policy
get_request_policy(in CORBA::PolicyType type);
void
add_request_service_context(
in IOP::ServiceContext service_context,
in boolean replace
);
};
695
CHAPTER 21 | Portable Interceptors
request_id y y y y y
operation y y y y y
arguments ya y
exceptions y y y y
contexts y y y y
operation_context y y y y
result y
response_expected y y y y y
sync_scope y y y y
reply_status y y y
forward_reference yb
get_slot y y y y y
get_request_service_context y y y y
get_reply_service_context y y y
target y y y y y
effective_target y y y y y
effective_profile y y y y y
received_exception y
received_exception_id y
get_effective_component y y y y
696
Writing Client Interceptors
get_effective_components y y y y
get_request_policy y y y y
add_request_service_context y
a. When ClientRequestInfo is passed to send_request, the arguments list contains an entry for all
arguments, but only in and inout arguments are available.
b. Access to forward_reference is valid only if reply_status is set to LOCATION_FORWARD or
LOCATION_FORWARD_PERMANENT.
697
CHAPTER 21 | Portable Interceptors
698
Writing Client Interceptors
Evaluating tagged components The sample application’s implementation of send_request checks each
outgoing request for tagged component TAG_REQUIRES_PASSWORD by calling
get_effective_component() on the interceptor’s ClientRequestInfo:
void
ACL_ClientInterceptorImpl::send_request(
PortableInterceptor::ClientRequestInfo_ptr request
) IT_THROW_DECL((
CORBA::SystemException,
PortableInterceptor::ForwardRequest
))
try {
// Check if the object requires a password
1 if (requires_password(request))
{ // ...
}
}
// ...
CORBA::Boolean
ACL_ClientInterceptorImpl::requires_password(
PortableInterceptor::ClientRequestInfo_ptr request
) IT_THROW_DECL((CORBA::SystemException))
{
try {
2 IOP::TaggedComponent_var password_required_component =
request->get_effective_component(
AccessControlService::TAG_REQUIRES_PASSWORD
);
3 IOP::TaggedComponent::_component_data_seq& component_data =
password_required_component->component_data;
CORBA::OctetSeq octets(component_data.length(),
component_data.length(),
component_data.get_buffer(),
IT_FALSE);
699
CHAPTER 21 | Portable Interceptors
4 CORBA::Any_var password_required_as_any =
m_codec->decode_value(octets, CORBA::_tc_boolean);
CORBA::Boolean password_required;
5 if (password_required_as_any >>=
CORBA::Any::to_boolean(password_required))
{
return password_required;
}
}
return IT_FALSE;
}
700
Writing Client Interceptors
Obtaining service data After the client interceptor verifies that the request requires a password, it
calls RequestInfo::get_slot() to obtain the client password from the
appropriate slot:
Encoding service context data After the client interceptor gets the password string, it must convert the
string and related data into a CDR encapsulation, so it can be embedded in
a service context that is added to the request. To perform the data
conversion, it calls encode_value on an IOP::Codec:
Adding service contexts to a After initializing the service context, the client interceptor adds it to the
request outgoing request by calling add_request_service_context():
IOP::ServiceContext service_context;
service_context.context_id =
AccessControlService::PASSWORD_SERVICE_ID;
service_context.context_data = seq;
request->add_request_service_context(
service_context, IT_TRUE);
701
CHAPTER 21 | Portable Interceptors
void
receive_request(in ServerRequestInfo ri
) raises (ForwardRequest);
void
send_reply(in ServerRequestInfo ri);
void
send_exception(in ServerRequestInfo ri
) raises (ForwardRequest);
void
send_other(in ServerRequestInfo ri
) raises (ForwardRequest);
};
702
Writing Server Interceptors
Interception Points
During a successful request-reply sequence, each server interceptor
executes one starting interception point and one intermediate interception
point for incoming requests. For outgoing replies, a server interceptor
executes an ending interception point.
Starting interception point A server interceptor has a single starting interception point:
Intermediate interception point A server interceptor has a single intermediate interception point:
Ending interception points An ending interception point is called after the target operation is invoked,
and before the reply returns to the client. The ORB executes one of the
following ending interception points, depending on the nature of the reply:
send_reply lets an interceptor query reply information and modify the reply
service context after the target operation is invoked and before the reply
returns to the client.
703
CHAPTER 21 | Portable Interceptors
Scenario 1: Target object throws Interceptors A and B are registered with the server ORB. Figure 49 shows
exception the following interception flow:
1. The interception point receive_request_server_contexts processes
an incoming request on interceptor A, then B. Neither interception
point throws an exception.
2. Intermediate interception point receive_request processes the request
first on interceptor A, then B. Neither interception point throws an
exception.
3. The ORB delivers the request to the target object. The object throws an
exception.
4. The ORB calls interception point send_exception, first on interceptor
B., then A, to handle the exception.
704
Writing Server Interceptors
Client
A B
r_req_serv_cxts r_req_serv_cxts
receive_request receive_request
send_exception send_exception
object throws
exception
Server
Figure 49: Server interceptors receive request and send exception thrown
by target object.
Scenario 2: Exception aborts Any number of events can abort interception flow. Figure 50 shows the
interception flow following interception flow.
1. A request starts server-side interceptor processing, starting with
interceptor A’s receive_request_service_contexts. The request is
passed on to interceptor B.
2. Interceptor B’s receive_request_service_contexts throws an
exception. The ORB aborts interceptor flow and returns the exception
to interceptor A’s end interception point send_exception.
3. The exception is returned to the client.
705
CHAPTER 21 | Portable Interceptors
Because interceptor B’s start point does not complete execution, its
intermediate and end points are not called. Interceptor A’s intermediate
point receive_request also is not called.
Client
A B
r_req_serv_cxts r_req_serv_cxts
throws exception
receive_request
send_exception
Server
Scenario 3: Interceptors change An interceptor can change a normal reply to a system exception; it can also
reply type change the exception it receives, whether user or system exception to a
different system exception. Figure 51 shows the following interception flow:
1. The target object returns a normal reply.
2. The ORB calls send_reply on server interceptor C.
3. Interceptor C’s send_reply interception point throws exception foo_x,
which the ORB delivers to interceptor B’s send_exception.
4. Interceptor B’s send_exception changes exception foo_x to exception
foo_y, which the ORB delivers to interceptor A’s send_exception.
5. Interceptor A’s send_exception returns exception foo_y to the client.
706
Writing Server Interceptors
Client
A B C
send_exception send_reply
send_exception
throws exception throws exception
foo_y foo_x
foo_y foo_x
object returns
normal reply
Server
707
CHAPTER 21 | Portable Interceptors
ServerRequestInfo
Each server interception point gets a single ServerRequestInfo argument,
which provides the necessary hooks to access and modify server request
data:
CORBA::Policy
get_server_policy(in CORBA::PolicyType type);
void
set_slot(
in SlotId id,
in any data
) raises (InvalidSlot);
boolean
target_is_a(in CORBA::RepositoryId id);
void
add_reply_service_context(
in IOP::ServiceContext service_context,
in boolean replease
);
};
708
Writing Server Interceptors
ServerRequestInfo: r_req_
serv_cxts r_req s_reply s_excep s_other
request_id y y y y y
operation y y y y y
argumentsa y y y
exceptions y y y y
contexts y y y y
operation_context y y
result y
response_expected y y y y y
sync_scope y y y y y
reply_status y y y
forward_reference y
get_slot y y y y y
get_request_service_context y y y y y
get_reply_service_context y y y
sending_exception y
object_id y
adapter_id y
target_most_derived_interface y
get_server_policy y y y y y
709
CHAPTER 21 | Portable Interceptors
ServerRequestInfo: r_req_
serv_cxts r_req s_reply s_excep s_other
set_slot y y y y y
target_is_a y
add_reply_service_context y y y y y
a. When a ServerRequestInfo is passed to receive_request(), the arguments list contains an entry for all
arguments, but only in and inout arguments are available.
710
Writing Server Interceptors
void
ACL_ServerInterceptorImpl::receive_request_service_contexts(
PortableInterceptor::ServerRequestInfo_ptr request
) IT_THROW_DECL((
CORBA::SystemException,
PortableInterceptor::ForwardRequest
))
{
// Determine whether password protection is required.
AccessControl::PasswordPolicy_var password_policy =
get_password_policy(request);
// ...
711
CHAPTER 21 | Portable Interceptors
AccessControl::PasswordPolicy_ptr
ACL_ServerInterceptorImpl::get_password_policy(
PortableInterceptor::ServerRequestInfo_ptr request
) IT_THROW_DECL((CORBA::SystemException))
{
try {
CORBA::Policy_var policy = request->get_server_policy(
AccessControl::PASSWORD_POLICY_ID);
return AccessControl::PasswordPolicy::_narrow(policy);
}
catch (const CORBA::INV_POLICY&) {
// Policy not specified
}
return AccessControl::PasswordPolicy::_nil();
}
// ...
Get service contexts After receive_request_server_contexts gets the server’s password policy,
it needs to compare it to the client password that accompanies the request.
The password is encoded as a service context, which is accessed through its
identifier PASSWORD_SERVICE_ID:
Example 128:
// ...
if (!CORBA::is_nil(password_policy) &&
password_policy->requires_password())
{
CORBA::String_var server_password =
password_policy->password();
if (!check_password(request, server_password))
{
throw CORBA::NO_PERMISSION(0xDEADBEEF);
}
}
// ...
712
Writing Server Interceptors
Example 128:
CORBA::Boolean
ACL_ServerInterceptorImpl::check_password(
PortableInterceptor::ServerRequestInfo_ptr request,
const char* expected_password
) IT_THROW_DECL((CORBA::SystemException))
{
try {
// Get the password service context...
1 IOP::ServiceContext_var password_service_context =
request->get_request_service_context(
AccessControlService::PASSWORD_SERVICE_ID
);
4 CORBA::Any_var password_as_any =
m_codec->decode_value(octets, CORBA::_tc_string);
const char* password;
password_as_any >>= password;
713
CHAPTER 21 | Portable Interceptors
714
Registering Portable Interceptors
715
CHAPTER 21 | Portable Interceptors
Obtain PICurrent In the sample application, the client application and client interceptor use
PICurrent to exchange password data:
• The client thread places the password in the specified PICurrent slot.
• The client interceptor accesses the slot to obtain the client password
and add it to outgoing requests.
In the sample application, pre_init() calls the following operations on
ORBInitInfo:
1. allocate_slot_id() allocates a slot and returns the slot’s identifer.
2. resolve_initial_references("PICurrent") returns PICurrent.
void
ACL_ORBInitializerImpl::pre_init(
PortableInterceptor::ORBInitInfo_ptr info
) IT_THROW_DECL((CORBA::SystemException))
{
// Reserve a slot for the password current
1 PortableInterceptor::SlotId password_slot =
info->allocate_slot_id();
PortableInterceptor::Current_var pi_current;
// get PICurrent
try {
716
Registering Portable Interceptors
2 CORBA::Object_var init_ref =
info->resolve_initial_references("PICurrent");
pi_current =
PortableInterceptor::Current::_narrow(init_ref);
} catch
(const PortableInterceptor::ORBInitInfo::InvalidName&) {
throw CORBA::INITIALIZE();
}
// ...
}
Register an initial reference After the ORB initializer obtains PICurrent and a password slot, it must
make this information available to the client thread. To do so, it instantiates
an AccessControl::Current object. This object encapsulates:
• PICurrent and its password slot
• Operations that access slot data
The AccessControl::Current object has the following IDL definition:
module AccessControl {
// ...
local interface Current : CORBA::Current {
attribute string password;
};
};
#include <omg/PortableInterceptor.hh>
#include <orbix/corba.hh>
#include "access_control.hh"
class ACL_CurrentImpl :
public AccessControl::Current,
public IT_CORBA::RefCountedLocalObject
717
CHAPTER 21 | Portable Interceptors
{
public:
ACL_CurrentImpl(
PortableInterceptor::Current_ptr pi_current,
PortableInterceptor::SlotId password_slot
) IT_THROW_DECL(());
char*
password() IT_THROW_DECL((CORBA::SystemException));
void
password(const char* the_password
) IT_THROW_DECL((CORBA::SystemException));
// ...
}
try {
1 AccessControl::Current_var current =
new ACL_CurrentImpl(pi_current, password_slot);
2 info->register_initial_reference(
"AccessControlCurrent", current);
}
catch (const
PortableInterceptor::ORBInitInfo::DuplicateName&)
{
throw CORBA::INITIALIZE();
}
718
Registering Portable Interceptors
Create and register policy The sample application’s IDL defines the following password policy to
factories provide password protection for the server’s POAs.
module AccessControl {
const CORBA::PolicyType PASSWORD_POLICY_ID = 0xBEEF;
struct PasswordPolicyValue {
boolean requires_password;
string password;
};
PortableInterceptor::PolicyFactory_var passwd_policy_factory =
new ACL_PasswordPolicyFactoryImpl();
info->register_policy_factory(
AccessControl::PASSWORD_POLICY_ID,
passwd_policy_factory
);
719
CHAPTER 21 | Portable Interceptors
Create Codec objects Each portable interceptor in the sample application requires a
PortableInterceptor::Codec in order to encode and decode octet data for
service contexts or tagged components. The ORB initializer obtains a Codec
factory by calling ORBInitInfo::codec_factory, then creates a Codec:
Note: The order in which the ORB initializer registers interceptors has no
effect on their runtime ordering. The order in which portable initializers are
called is determined by their order in the client and server binding lists
(see “Setting Up Orbix to Use Portable Interceptors” on page 723)
720
Registering Portable Interceptors
721
CHAPTER 21 | Portable Interceptors
Registering an ORBInitializer
An application registers an ORB initializer by calling
register_orb_initializer, which is defined in the PortableInterceptor
name space as follows:
namespace PortableInterceptor {
static void register_orb_initializer(
PortableInterceptor::ORBInitializer_ptr init);
};
722
Setting Up Orbix to Use Portable Interceptors
Note: The binding lists determine the order in which interceptors are
called during request processing.
For more information about Orbix configuration, see the Application Server
Platform Administrator’s Guide.
723
CHAPTER 21 | Portable Interceptors
724
CHAPTER 22
Bidirectional GIOP
The usual GIOP connection semantics allow request messages
to be sent in only one direction over a connection-oriented
transport protocol. Recent changes to the GIOP standard allow
this restriction to be relaxed in certain circumstances, making
it possible to use connections in a bidirectional mode.
725
CHAPTER 22 | Bidirectional GIOP
Bidirectional GIOP draft At the time of writing, a draft specification for bidirectional GIOP is
specification described in the OMG firewall submission:
http://www.omg.org/docs/orbos/01-08-03.pdf
Configuration versus There are essentially two alternative approaches you can take to enabling
programming approach bidirectional GIOP in your Orbix applications, as follows:
• Configuration approach.
• Programming approach.
726
Introduction to Bidirectional GIOP
Configuration approach The configuration approach to enabling bidirectional GIOP has the
advantage of being relatively easy to do, because it does not require an
application re-build.
On the other hand, this approach has the disadvantage that it is coarse
grained: that is, the relevant bidirectional policies are applied to all of the
CORBA objects, object references and POA instances.
For details of this approach, see the Orbix Administrator’s Guide.
Programming approach The programming approach to enabling bidirectional GIOP has the
advantage that you can apply it at any level of granularity: ORB, POA,
thread or object. In general, it is better to apply a fine-grained approach—
that is, enabling bidirectional GIOP only for those objects that really need it.
Bidirectional GIOP incurs a small performance penalty, due to the following
overheads: extra component added to IORs, extra service context added to
request messages, checking for bidirectional policy compatibility. By
enabling bidirectional GIOP only where it is needed, you can minimize this
performance penalty.
727
CHAPTER 22 | Bidirectional GIOP
IDL for standard policies The OMG draft specification for bidirectional GIOP defines three
bidirectional policies. These policies are defined in the BiDirPolicy IDL
module as shown in Example 136.
// IDL
module BiDirPolicy
{
typedef unsigned short BidirectionalPolicyValue;
728
Bidirectional GIOP Policies
729
CHAPTER 22 | Bidirectional GIOP
IDL for proprietary policies Orbix defines some proprietary bidirectional GIOP policies, in addition to the
policies defined by the OMG draft specification. These policies are defined in
the IT_BiDirPolicy IDL module as shown in Example 137.
// IDL
...
module IT_BiDirPolicy
{
const CORBA::PolicyType BI_DIR_ID_GENERATION_POLICY_ID =
IT_PolicyBase::IONA_POLICY_ID + 62;
730
Bidirectional GIOP Policies
};
731
CHAPTER 22 | Bidirectional GIOP
Policy granularity As usual for CORBA policies, these bidirectional policies can be defined at
different levels of granularity. The different levels of granularity for which you
can define each policy are summarized in Table 30.
BiDirPolicy::BidirectionalExportPolicy ORB
POA
BiDirPolicy::BidirectionalOfferPolicy ORB
Thread
Object reference
BiDirPolicy::BidirectionalAcceptPolicy ORB
Thread
Object reference
IT_BiDirPolicy::BiDirIdGenerationPolicy ORB
POA
732
Bidirectional GIOP Policies
IT_BiDirPolicy::BidirectionalGen3AcceptPolicy ORB
Thread
Object reference
733
CHAPTER 22 | Bidirectional GIOP
Configuration Prerequisites
Overview This subsection describes the basic configuration prerequisites for using
bidirectional GIOP in an Orbix 6.x domain.
plugins:giop:message_server_binding_list=
["BiDir_GIOP","GIOP" ];
Note: If your server needs to interoperate with Orbix 3 legacy clients, the
binding:client_binding_list should also include a "BiDir_Gen3" entry.
See “Interoperability with Orbix Generation 3” on page 750.
734
Basic BiDir Scenario
735
CHAPTER 22 | Bidirectional GIOP
Demonstration code The stock feed demonstration code is located in the following directory:
OrbixInstallDir/asp/Version/demos/corba/orb/bidir_giop
IDL for stock feed scenario Example 138 shows the IDL for the stock feed demonstration, which
consists of two IDL interfaces: StockInfoCB and RegStockInfo. These IDL
interfaces are identical to the ones used by the corresponding demonstration
in the Orbix Generation 3 product.
// IDL
interface StockInfoCB
{
oneway void NotifyPriceChange(
in string stock_name,
in float new_price
);
};
interface RegStockInfo
{
void Register(in StockInfoCB callback);
void Deregister(in StockInfoCB callback);
void Notify(in float new_price);
};
736
Basic BiDir Scenario
Stock feed scenario Figure 52 gives you an overview of the stock feed demonstration, where a
number of clients register their interest in receiving callbacks from the stock
feed server.
737
CHAPTER 22 | Bidirectional GIOP
Steps to establish a callback Figure 52 shows the steps that occur to establish a stock feed callback, as
follows:
Stage Description
738
Basic BiDir Scenario
Stage Description
739
CHAPTER 22 | Bidirectional GIOP
C++ example Example 139 is a C++ example that shows how to create a POA instance
with the BidirectionalExportPolicy policy enabled. This POA instance is
used on the client side to activate client callback objects.
Call the CORBA::ORB::create_policy() operation to create a
BidirectionalExportPolicy object and then include this policy in the list of
policies passed to the PortableServer::POA::create_POA() operation.
// C++
// create callback POA with the effective
// BidirectionalExportPolicy set to ALLOW in order to allow an
// appropriate BiDirId be published in the callback reference
//
...
PortableServer::POA_var poa;
CORBA::PolicyList poa_policies(1);
poa_policies.length(1);
CORBA::Any bi_dir_value;
bi_dir_value <<= BiDirPolicy::ALLOW;
poa_policies[0] = orb->create_policy(
BiDirPolicy::BI_DIR_EXPORT_POLICY_TYPE,
bi_dir_value
);
740
Basic BiDir Scenario
741
CHAPTER 22 | Bidirectional GIOP
C++ example Example 140 is a C++ example that shows how to create a RegStockInfo
object reference with the BidirectionalOfferPolicy policy enabled. This
RegStockInfo object reference is used on the client side to connect to a
RegStockInfo CORBA object on the server side.
Call the CORBA::ORB::create_policy() operation to create a
BidirectionalOfferPolicy object and then include this policy in the list of
policies passed to the CORBA::Object::_set_policy_overrides()
operation.
// C++
// destringify RegStockInfo IOR and override the effective
// policies with the BidirectionalOfferPolicy set to ALLOW in
// order to allow a birectional offer be made with invocations on
742
Basic BiDir Scenario
if (CORBA::is_nil(objref))
{
return 1;
}
CORBA::Any value;
value <<= BiDirPolicy::ALLOW;
CORBA::PolicyList policies(1);
policies.length(1);
policies[0] = orb->create_policy(
BiDirPolicy::BI_DIR_OFFER_POLICY_TYPE,
value
);
CORBA::Object_var registry_ref =
objref->_set_policy_overrides(policies,
CORBA::ADD_OVERRIDE);
RegStockInfo_var reg_stock_info =
RegStockInfo::_narrow(registry_ref);
if (CORBA::is_nil(reg_stock_info))
{
cerr << "Could not _narrow object to type RegStockInfo"
<< endl;
return 1;
}
743
CHAPTER 22 | Bidirectional GIOP
C++ example Example 141 is a C++ example that shows how to create a StockInfoCB
callback object reference with the BidirectionalAcceptPolicy policy
enabled. This StockInfoCB callback object reference is used on the server
side to connect to a StockInfoCB callback object on the client side.
// C++
void
RegStockInfoImpl::Register (
StockInfoCB_ptr obj
) throw (CORBA::SystemException)
{
cout << "RegStockInfoImpl::Register( StockInfoCB_ptr )
called"
744
Basic BiDir Scenario
<< endl;
cout << "Registering client for stockname "
<< m_stockname << endl;
1 policies[0] = global_orb->create_policy(
BiDirPolicy::BI_DIR_ACCEPT_POLICY_TYPE,
value
);
2 policies[1] = global_orb->create_policy(
IT_BiDirPolicy::BI_DIR_GEN3_ACCEPT_POLICY_ID,
value
);
3 CORBA::Object_ptr new_obj =
obj->_set_policy_overrides(policies,
CORBA::ADD_OVERRIDE);
StockInfoCB_ptr bidir_callback =
StockInfoCB::_narrow(new_obj);
{
CallbackListEntry value;
value.m_original_ref = StockInfoCB::_duplicate(obj);
value.m_bidir_ref = bidir_callback;
IT_Locker<IT_Mutex> lock(m_mutex);
4 m_clientlist.push_back(value);
}
}
745
CHAPTER 22 | Bidirectional GIOP
746
Advanced BiDir Scenario
747
CHAPTER 22 | Bidirectional GIOP
Multiple endpoints The main difference between this advanced bidirectional scenario,
Figure 53, and the basic bidirectional scenario, Figure 52 on page 737, is
that the advanced scenario features multiple endpoints, as follows:
• Server-side endpoints—POA_J and POA_K. The POA_J endpoint has its
policies set so that it accepts insecure connections from clients. The
POA_K endpoint has its policies set so that it requires secure
connections from clients.
• Client-side endpoints—POA_A, POA_B and POA_C, of which only POA_B
and POA_C can accept callbacks (their BidirectionalExportPolicy is
set to ALLOW). POA_B is configured to accept only insecure callbacks.
POA_C is configured to accept only secure callbacks.
Multiple connections Because of the different security policies required by POA_J and POA_K in
Figure 53, it is possible for one client application to establish multiple
connections to the same server. For example, the client might establish an
insecure connection to object J1 in POA_J, and a secure connection to object
K1 in POA_K.
Bidirectional offer phase The offer phase occurs whenever the client opens a connection to the server.
In Figure 53, two offers are made:
• Connection to the object, J1—an insecure connection is made to the
POA_J endpoint, which activates object J1. In the first request message
over this connection, the client includes a GIOP::BI_DIR_GIOP_OFFER
service context containing a list of the client endpoints that support
insecure callbacks: that is, BiDirId_B.
• Connection to the object, K1—a secure connection is made to the
POA_K endpoint, which activates object K1. Similarly to the first
connection, the client includes a GIOP::BI_DIR_GIOP_OFFER service
context containing a list of the client endpoints that support secure
callbacks: that is, BiDirId_C.
748
Advanced BiDir Scenario
Exporting a callback object In Example 53 on page 747, the client exports a callback reference, B1, to
the server. Because POA_B has its BiDirExportPolicy set to ALLOW, the IOR
for B1 includes a GIOP::TAG_BI_DIR_GIOP IOR component, which embeds
the BiDirId_B bidirectional ID.
The presence of the TAG_BI_DIR_GIOP IOR component indicates to the
server that the object, B1, supports bidirectional GIOP and the ID,
BiDirId_B, identifies the associated endpoint on the client side.
Bidirectional accept phase The accept phase occurs when the first operation invocation is made on the
object reference, B1, on the server side. When the first operation is invoked
on B1, the ORB recognizes that B1 can use bidirectional GIOP, because the
following conditions hold:
• The BiDirAcceptPolicy is set to ALLOW on the B1 object reference, and
• The IOR for B1 includes a TAG_BI_DIR_GIOP IOR component.
The ORB then extracts the BiDirId_B ID from B1’s IOR and compares this
bidirectional ID with the offers from existing client connections. Because the
insecure connection offers bidirectional GIOP for the BiDirId_B endpoint,
the B1 object reference attempts to re-use this connection for the callback.
At this point, Orbix automatically compares the callback invocation policies
with the attributes of the offered connection. Only if the policies are
compatible will Orbix re-use the existing insecure connection for
bidirectional GIOP.
749
CHAPTER 22 | Bidirectional GIOP
Figure 54: Orbix 3 Client Receiving a Callback from an Orbix 6.1 Server
Configuring an Orbix 6.1 server for To configure an Orbix 6.1 server to interoperate bidirectionally with Orbix
Gen 3 interoperability Generation 3 clients, you must include the appropriate BiDir_Gen3 entry in
the server's configured binding:client_binding_list. For example,
Setting the BiDir Gen 3 accept To enable an Orbix 3 callback object reference to re-use an existing
policy incoming connection on the server side, you must set the
IT_BiDirPolicy::BidirectionalGen3AcceptPolicy on the callback object
reference.
For C++ example code, see Example 141 on page 744.
750
Interoperability with Orbix Generation 3
Asymmetry of Gen 3 bidirectional Orbix 6.1 support for Orbix 3 bidirectional GIOP is asymmetric. An Orbix
support 6.1 server can invoke on a Orbix 3 callback reference using bidirectional
GIOP. However, an Orbix 6.1 client can not produce a callback reference
that an Orbix 3 server could invoke on using bidirectional GIOP.
Limitations of Gen 3 bidirectional Orbix 3 bidirectional GIOP is also subject to the following limitations:
GIOP • An Orbix 3 callback reference must be passed as a request parameter
over the actual connection to be used for bidirectional invocations;
whereas an Orbix 6.x bidirectional-enabled callback reference can be
passed in any way to the server (for example, through the naming
service or by stringifying to a shared file).
• The bidirectional offer implicit in an Orbix 3 callback reference is
limited to the lifetime of the connection over which the callback
reference is received by the server. Hence, further bidirectional
invocations could not be made if, for example, the connection is
reaped by the Orbix automatic connection management (ACM) and
then re-established.
751
CHAPTER 22 | Bidirectional GIOP
752
CHAPTER 23
Locating Objects
with corbaloc
Corbaloc URLs enable you to specify the location of a CORBA
service in a relatively simple format. Before using a corbaloc
URL on the client side, you would normally register a simplified
key for the CORBA object. Key registration can be done either
using the itadmin named_key or by programming.
753
CHAPTER 23 | Locating Objects with corbaloc
Converting a corbaloc URL to an In C++, you can convert a corbaloc URL into an object reference using the
object reference CORBA::ORB::string_to_object() function, which has the following
signature:
// C++
CORBA::Object_ptr string_to_object(const char *);
For code examples, see “Using the corbaloc URL in a Client” on page 765.
corbaloc URL formats The following corbaloc URL formats are described here:
• Basic corbaloc URL.
• Multiple-address corbaloc URL.
• Secure corbaloc URL.
754
corbaloc URL Format
Basic corbaloc URL A basic corbaloc URL has the following format:
corbaloc:[iiop]:[Version@]Host[:Port][/ObjectKey]
755
CHAPTER 23 | Locating Objects with corbaloc
Multiple-address corbaloc URL The multiple-address corbaloc URL has the following format:
corbaloc:[CommaSeparatedAddressList][/ObjectKey]
With this form of corbaloc URL, you can locate a service that runs on more
than one host and port (or is available through multiple protocols).
Each address in the list has the same format as the middle part of the basic
corbaloc URL. For example, given that the FooService object is available
both on HostX and HostY, you could specify a multiple-address corbaloc
URL for the service as follows:
corbaloc:iiop:1.2@HostX:3075,iiop:1.2@HostY:3075/FooService
This form of URL is useful for specifying backup services; Orbix tries each of
the addresses in the order in which they appear until it makes a successful
connection.
Secure corbaloc URL A secure corbaloc URL has the following format:
corbaloc:it_iiops:[Version@]Host[:Port][/ObjectKey]
This differs from the basic corbaloc URL only in that the transport protocol
is it_iiops, which selects the IIOP/TLS protocol instead of IIOP. The
it_iiops protocol specifier is Orbix-specific.
Note: Some earlier versions of Orbix (C++ only) used iiops to specify
the IIOP/TLS protocol. If you need to support interoperability with older
versions of Orbix, you could use a multiple-address corbaloc URL to
support both types of protocol specifier, it_iiops and iiops.
For example, to connect securely to the FooService object:
corbaloc:it_iiops:1.2@FooHost:3075,iiops:1.2@FooHost:3075/FooSer
vice
756
corbaloc URL Format
Object keys The object key appearing in a corbaloc URL can have one of the following
values:
• Object key from an IOR—the CORBA specification defines a corbaloc
object key to be the same as the object key embedded in an IOR,
except that non-printable characters are substituted by URL escape
sequences. Unfortunately, this form of object key is unwieldy, because
object keys from IORs are usually defined in a binary format.
• Named key—a named key is a human-readable key that is registered
with the locator service. The named key enables you to construct a
human-readable corbaloc URL for indirect persistent servers.
• Plain text key—a plain text key is a human-readable key that is
registered with the plain_text_key plug-in. The plain text key enables
you to construct a human-readable corbaloc URL for direct persistent
servers.
The named key and the plain text key are conceptually similar; they are both
mechanisms for substituting a human-readable key in a corbaloc URL.
757
CHAPTER 23 | Locating Objects with corbaloc
758
Indirect Persistence Case
The CORBA objects activated by this POA have the following qualities:
• Persistence—implies that the object reference for this object remains
valid even after the server is stopped and restarted.
• Indirect persistence—implies that clients establish contact with the
server through the locator. In practice, the POA embeds the locator’s
address in the object references it generates. This forces clients to
contact the locator before connecting to the server.
Figure 55 shows an overview of how Orbix resolves a corbaloc URL with
the help of the locator service in the indirect persistent case.
Figure 55: Using corbaloc with the Locator-Based Named Key Registry
759
CHAPTER 23 | Locating Objects with corbaloc
Stages in registering and finding a The stages involved in registering a named key and resolving a corbaloc
named key URL constructed with that named key, as shown in Figure 55 on page 759,
can be described as follows:
Stage Description
4 The locator looks up the named key registry to find the object
reference corresponding to the FooService key. The Foo object
reference is then sent back to the client in a reply message
(either a GIOP LocateReply message or a GIOP Reply message
with LOCATION_FORWARD reply type).
5 Using the object reference data received from the locator, the
client can now open a connection directly to the Foo server.
760
Indirect Persistence Case
The itadmin named_key The itadmin named_key command supports a variety of subcommands for
command managing named keys in the implementation repository, as follows:
For full details of these commands, see the Orbix Administrator’s Guide.
761
CHAPTER 23 | Locating Objects with corbaloc
Creating a named key using To create a named key using the itadmin named_key create command,
itadmin named_key create perform the following steps:
Step Action
1 Obtain a stringified IOR for the CORBA object that you want to
register. You could obtain the IOR in one of the following ways:
• If the server dumps the stringified IOR to a file or to the
console window, you can copy it from there (the
CORBA::ORB::object_to_string() function generates
stringified IORs).
• If the object is already registered in the CORBA naming
service, you can obtain the stringified IOR using the
itadmin ns resolve Name command.
762
Indirect Persistence Case
Prerequisites To use the IT_Location and IT_NamedKey modules in C++, you must link
your application with the it_location library (it_location.lib in
Windows).
Server example in C++ Example 142 shows how a server can register a named key, FooService,
that identifies a given object reference, FooObjectRef (the object reference
must have been generated from a CORBA object belonging to an indirect
persistent POA).
// C++
...
// Get the Locator
CORBA::Object_var objref =
1 orb->resolve_initial_references("IT_Locator");
IT_Location::Locator_var locator =
IT_Location::Locator::_narrow(objref);
763
CHAPTER 23 | Locating Objects with corbaloc
764
Indirect Persistence Case
corbaloc:iiop:1.2@LocatorHost:LocatorPort/NamedKey
Because the server is indirect persistent, the URL embeds the locator’s
address, LocatorHost:LocatorPort, not the server’s own address.
For example, given that the Orbix locator is running on host, LocatorHost,
and port, 3075, and the server registers a Foo object under the named key,
FooService, you could access the Foo object with the following URL:
corbaloc:iiop:1.2@LocatorHost:3075/FooService
Client example in C++ Example 143 shows how to resolve a corbaloc URL for an object of Foo
type, using the CORBA::ORB::string_to_object() function.
// C++
const char * corbalocURL =
"corbaloc:iiop:1.2@LocatorHost:3075/FooService";
765
CHAPTER 23 | Locating Objects with corbaloc
766
Direct Persistence Case
The CORBA objects activated by this POA have the following qualities:
• Persistence—implies that the object reference for this object remains
valid even after the server is stopped and restarted.
• Direct persistence—implies that clients establish contact with the
server directly, bypassing the locator. Hence, the POA embeds the
server’s own address in the object references it generates.
Figure 56 shows an overview of how Orbix resolves a corbaloc URL using
the plain_text_key plug-in in the direct persistent case.
767
CHAPTER 23 | Locating Objects with corbaloc
Stages in registering and finding a The stages involved in registering a plain text key and resolving a corbaloc
plain text key URL constructed with that plain text key, as shown in Figure 56 on
page 767, can be described as follows:
Stage Description
768
Direct Persistence Case
Prerequisites Because the plain_text_key plug-in is already included in the core it_art
library, there are no special prerequisites for using plain text keys on the
server side.
Server example in C++ Example 144 shows how a server registers a plain text key, FooService,
that identifies a given object reference, FooObjectRef (the object reference
must have been generated from a CORBA object belonging to a direct
persistent POA).
// C++
CORBA::Object_var objref = the_orb->resolve_initial_references(
1 "IT_PlainTextKeyForwarder"
);
IT_PlainTextKey::Forwarder_var forwarder =
IT_PlainTextKey::Forwarder::_narrow(objref);
2 forwarder->add_plain_text_key(
"FooService",
FooObjectRef
);
769
CHAPTER 23 | Locating Objects with corbaloc
corbaloc:iiop:1.2@ServerHost:ServerPort/PlainTextKey
Because the server is direct persistent, the URL embeds the server’s own
address, ServerHost:ServerPort.
For example, given that the server is running on host, FooHost, and port,
4321, and the server registers a Foo object under the plain text key,
FooService, you could access the Foo object with the following URL:
corbaloc:iiop:1.2@FooHost:4321/FooService
Client example in C++ Example 145 shows how to resolve a corbaloc URL for an object of Foo
type, using the CORBA::ORB::string_to_object() function.
// C++
const char * corbalocURL =
"corbaloc:iiop:1.2@FooHost:4321/FooService";
770
CHAPTER 24
Configuring and
Logging
Orbix has built-in configuration and logging mechanisms,
which are used internally by the Orbix product. You have the
option of using these configuration and logging mechanisms
in your own applications.
771
CHAPTER 24 | Configuring and Logging
module IT_Config
{
typedef sequence<string> ConfigList;
...
exception TargetNotFound {};
772
The Configuration Interface
# Orbix Configuration
my_list_item = ["first", "second", "third"];
Reference For more details of the Configuration interface and the IT_Config module,
see the IT_Config sections of the CORBA Programmer’s Reference.
773
CHAPTER 24 | Configuring and Logging
Configuring
Overview Orbix has a flexible configuration system which enables an application to
retrieve configuration data without needing to know anything about the
actual source of the data. This section briefly describes Orbix configuration,
covering the following topics:
• Generating configuration domains.
• Configuration sources.
• Sample configuration.
• C++ accessing configuration settings.
• References.
Generating configuration domains Configuration domains are generated by running the itconfigure tool.
Configuration sources Orbix configuration data can come from one of the following sources:
• Configuration file—this is a file, DomainName.cfg, that stores
configuration settings in a format that is easily readable and editable.
• Configuration repository (CFR) service—this is a service that stores
configuration settings in a central database and is remotely accessible
to CORBA applications. Note, that a minimal configuration handler file,
DomainName.cfg, is also needed on hosts that use the CFR service in
order to contact the CFR initially.
774
Configuring
Sample configuration Example 147 shows some sample configuration settings, of various types,
that might be used to configure a hello_world plug-in.
C++ accessing configuration Example 148 shows C++ code that obtains an initial reference to an
settings IT_Config::Configuration object, by passing the IT_Configuration initial
object ID string to the resolve_initial_references() operation.
// C++
...
#include <orbix/configuration.hh>
HelloWorld_ORBState::HelloWorld_ORBState(
CORBA::IT_ORB_ptr orb
) : m_orb(CORBA::IT_ORB::_duplicate(orb))
{
assert(!CORBA::is_nil(m_orb));
m_orb->it_add_internal_ref();
CORBA::Object_var initial_reference;
775
CHAPTER 24 | Configuring and Logging
load_config();
...
}
The next step is to read configuration data using the operations defined on
the IT_Config::Configuration interface. Example 149 shows how to read
the sample configuration settings from Example 147 on page 775.
// C++
CORBA::Boolean b;
CORBA::String_var s;
CORBA::Long l;
CORBA::Double d;
IT_Config::ConfigList_var cfg_list;
m_config->get_boolean("plugin:hello_world:boolean_item", b);
m_config->get_string("plugin:hello_world:string_item", s);
m_config->get_long("plugin:hello_world:long_item", l);
m_config->get_double("plugin:hello_world:double_item", d);
m_config->get_list("plugin:hello_world:list_item", cfg_list);
References The following references can provide you with more information about Orbix
configuration:
• The documentation of the IT_Config::Configuration interface in the
CORBA Programmer’s Reference.
776
Logging
Logging
Overview Logging provides administrators and system operators with information
about a production system, reporting information such as significant system
events, warnings of anomalous conditions, and detailed information about
error conditions. Its primary goal is to provide administrators with the
information needed to detect diagnose and resolve problems in a production
system.
777
CHAPTER 24 | Configuring and Logging
Event priority Every event that is generated must have a priority assigned to it.
In C++, you can use one of the following constants (of
IT_Logging::EventPriority type) to assign priority to an event:
IT_Logging::LOG_INFO
IT_Logging::LOG_WARNING
IT_Logging::LOG_ERROR
IT_Logging::LOG_FATAL_ERROR
Message A log message is a string, which might include some embedded parameters.
Local log stream The local log stream reports events either to a local file or to standard error.
You can enable the local log stream by including local_log_stream in your
list of orb_plugins, as follows:
For more details about how to configure a local log stream, see the CORBA
Administrator’s Guide.
System log stream The system log stream reports events to the host’s system log. You can
enable the system log stream by including system_log_stream in your list of
orb_plugins, as follows:
For more details about how to configure a system log stream, see the
CORBA Administrator’s Guide.
778
Logging
Defining a subsystem ID and Before you can use logging with your plug-in, you must define a logging
event IDs subsystem ID and a set of event IDs in IDL.
For example, the IDL in Example 150 shows the subsystem ID and event
IDs defined for the lease plug-in.
#include <orbix/logging.idl>
module IT_Lease_Logging
{
const IT_Logging::SubsystemId SUBSYSTEM = "IT_LEASE";
// Errors (1+)
//
const IT_Logging::EventId NAMING_SERVICE_UNREACHABLE = 1;
const IT_Logging::EventId REAPER_THREAD_FAILURE = 2;
const IT_Logging::EventId RENEWAL_THREAD_FAILURE = 3;
const IT_Logging::EventId CALLBACK_FAILURE = 4;
const IT_Logging::EventId INVALID_LEASE_AGENT_REFERENCE = 5;
const IT_Logging::EventId LEASE_AGENT_NOT_FOUND = 6;
const IT_Logging::EventId LEASE_ACQUISITION_FAILURE = 7;
// Warnings (100+)
//
const IT_Logging::EventId CLIENT_LEASE_RELEASE_FAILURE = 100;
const IT_Logging::EventId SERVER_LEASE_WITHDRAW_FAILURE= 101;
const IT_Logging::EventId DEFAULT_REAP_TIME_USED = 102;
const IT_Logging::EventId DEFAULT_PING_TIME_USED = 103;
const IT_Logging::EventId PING_TIME_ALTERED = 104;
const IT_Logging::EventId LEASE_EXPIRED_PREMATURELY = 105;
779
CHAPTER 24 | Configuring and Logging
C++ logging messages Example 151 shows an extract from the lease plug-in code, which shows
how to obtain a reference to an event log and send messages to the event
log.
// C++
...
#include <orbix/logging_support.h>
#include "lease_events.hh"
IT_Lease_ORBState::IT_Lease_ORBState(
CORBA::IT_ORB_ptr orb
) : m_orb(CORBA::IT_ORB::_duplicate(orb)),
m_connected_to_naming_service(IT_FALSE)
{
assert(!CORBA::is_nil(m_orb));
m_orb->it_add_internal_ref();
780
Logging
// C++
// Log the lease renewal failure and exit the thread.
//
IT_LOG_MESSAGE_2(
m_event_log,
IT_Lease_Logging::SUBSYSTEM,
IT_Lease_Logging::RENEWAL_THREAD_FAILURE,
IT_Logging::LOG_ERROR,
IT_LEASE_RENEWAL_THREAD_FAILURE_MSG,
m_lease_id, // 1st Parameter
m_server_id // 2nd Parameter
);
781
CHAPTER 24 | Configuring and Logging
// C++
const char* IT_LEASE_RENEWAL_THREAD_FAILURE_MSG =
"lease renewal thread failure - lease %0 for server %1 may not be
automatically renewed";
The first parameter value replaces %0 and the second replaces %1. You can
use either strings or integer types as parameters.
References The following resources are available on the subject of Orbix logging:
• The C++ logging macros are defined in
OrbixInstallDir/asp/Version/include/orbix/logging_support.h.
• The documentation of the IT_Logging module in the CORBA
Programmer’s Reference.
782
CHAPTER 25
Orbix Compression
Plug-in
This chapter explains how to program the Orbix ZIOP
compression plug-in. This can enable significant performance
improvements on low bandwidth networks.
783
CHAPTER 25 | Orbix Compression Plug-in
Client Object
ZIOP Compression
IIOP Message
784
Introduction to the ZIOP Plug-In
Implementation Compression can be configured per-ORB and also per-binding (using Orbix
ORB policies). The compression is performed using a configurable
compression library. The compression plug-in (ziop) supports the following
compression algorithms:
• gzip
• pkzip
• bzip2
Orbix ZIOP compression has been implemented in both C++ and Java and
is available on all platforms.
Additional components The following Orbix components have also been updated for ZIOP
compression:
• The giop_snoop plug-in has been updated to detect ZIOP compressed
messages.
• The iordump tool has been updated to parse the new IOR profiles for
ZIOP compression.
785
CHAPTER 25 | Orbix Compression Plug-in
Configuration Prerequisites
Overview Before you can program compression policies, the Orbix configuration must
satisfy prerequisites to ensure that the ZIOP plug-in is loaded and enabled.
Orbix uses symbolic names to configure plug-ins and then associates them
with a Java or a C++ implementation. The compression/decompression
plug-in is named ziop. This is implemented in Java by the
com.iona.corba.ziop.ZIOPPlugIn class, and in C++ by the it_ziop
shared library.
The ziop plug-in requires the following basic configuration settings:
• Configuring the ziop plug-in.
• Configuring the binding lists.
Note: Both the client and the server must be configured appropriately to
enable compression.
Configuring the ziop plug-in To configure the ziop plug-in, perform the following steps:
1. Ensure that the following entries are present in your Orbix configuration
file:
plugins:ziop:shlib_name = "it_ziop";
plugins:ziop:ClassName = "com.iona.corba.ziop.ZIOPPlugIn";
For example:
786
Configuration Prerequisites
Configuring the binding lists To enable compression/decompression for CORBA IIOP communication,
ensure that your binding lists contain the following entries.
For clients:
binding:client_binding_list = ["GIOP+ZIOP+IIOP"];
For servers:
plugins:giop:message_server_binding_list = ["ZIOP+GIOP"];
The client or server binding lists can be much more complicated than these
simple examples, although these are adequate for compressed GIOP/IIOP
communication. Here is an example of more complex binding lists:
binding:client_binding_list = ["OTS+GIOP+ZIOP+IIOP_TLS",
"CSI+GIOP+ZIOP+IIOP_TLS", "GIOP+ZIOP+IIOP_TLS",
"CSI+GIOP+ZIOP+ZIOP+IIOP", "GIOP+ZIOP+IIOP"];
plugins:giop:message_server_binding_list = [ "BiDir_GIOP",
"ZIOP+GIOP", "GIOP"];
787
CHAPTER 25 | Orbix Compression Plug-in
Compression Policies
Overview This section describes those compression policies that are defined in IDL
and can be set programmatically. Not all compression policies can be set
programmatically—see the Administrator’s Guide for details of all the
policies that can be set by configuration.
• CompressionEnablingPolicy.
• CompressorIdPolicy.
IDL for the compression policies Example 152 shows the part of the IT_ZIOP module that defines two
compression policies, CompressionEnablingPolicy and
CompressorIdPolicy. This IDL is extracted from the orbix_pdk/ziop.idl
file.
// IDL
// File: <OrbixInstallDir>/asp/<Version>/idl/orbix_pdk/ziop.idl
...
module IT_ZIOP {
...
typedef unsigned long CompressorId;
788
Compression Policies
789
CHAPTER 25 | Orbix Compression Plug-in
C++ enable/disable compression Example 153 shows how to enable compression at the POA level in a C++
on the server side server. This example creates a compression enabling policy with the value
true and uses this policy to initialize a POA object, child_poa. The
programmed policy value overrides the
policies:ziop:compession_enabled setting from the Orbix configuration.
Because this example does not program a value for the compressor ID
policy, the choice of compression algorithm is implicitly determined by the
policies:ziop:compressor_id setting in the Orbix configuration.
// C++
#include <omg/orb.hh>
#include <orbix_pdk/ziop.hh>
// ...
790
Programming Compression Policies
C++ enable/disable compression Example 154 shows how to disable compression at the proxy object level in
on the client side a C++ client. This example creates a compression enabling policy with the
value false and uses this policy to create a copy of a proxy object, objref2.
The programmed policy value overrides the
policies:ziop:compession_enabled setting from the Orbix configuration.
// C++
#include <omg/orb.hh>
#include <orbix_pdk/ziop.hh>
// ...
C++ select compression Example 155 shows how to select the compression algorithm by setting the
algorithm on the server side compressor ID at the POA level in a C++ server. This example creates a
compressor ID policy with the value 3 (for bzip2) and uses this policy to
initialize a POA object, child_poa. The programmed policy value overrides
the policies:ziop:compressor_id setting from the Orbix configuration.
// C++
#include <omg/orb.hh>
#include <orbix_pdk/ziop.hh>
// ...
791
CHAPTER 25 | Orbix Compression Plug-in
policies[1] =
orb->create_policy(IT_ZIOP::COMPRESSOR_ID_POLICY_ID, any);
PortableServer::POA_var child_poa = root_poa->create_POA(
"child_poa",
root_poa->the_POAManager(),
policies
);
792
Implementing Custom Compression
793
CHAPTER 25 | Orbix Compression Plug-in
Example For a detailed example of how to use the IT_Buffer programming interface,
see the ZIOP compression demonstration in the following directory:
OrbixInstallDir/asp/Version/demos/corba/orb/ziop_compression
Buffer IDL interface Example 156 shows the Buffer IDL interface, which is defined in the
IT_Buffer module.
// IDL
...
module IT_Buffer {
...
local interface Buffer
{
794
Implementing Custom Compression
void rewind();
Segment next_segment();
void grow(
in unsigned long increment,
in TimeBase::UtcT expiry
);
void trim(
in unsigned long from,
in unsigned long to
);
void eclipse(in long delta);
void recycle();
void prepend(in Buffer head);
void append(in Buffer tail);
Buffer extract(
in unsigned long from,
in unsigned long to
);
void copy_octets(
in unsigned long buffer_offset,
inout CORBA::OctetSeq dest,
in unsigned long dest_offset,
in unsigned long length
);
};
...
};
Buffer attributes The following attributes are defined in the IT_Buffer::Buffer interface:
• length—the number of bytes within the buffer currently available for
use.
• original_length—the number of bytes originally allocated to the
buffer.
• storage_size—the allocation unit size of the buffer’s underlying
storage implementation.
• segment_count—the number of segments currently available for use.
795
CHAPTER 25 | Orbix Compression Plug-in
Buffer operations The following operations are defined in the IT_Buffer::Buffer interface:
• rewind()—ensures that a subsequent call to next_segment() returns
the first segment of the buffer or NULL, if the length is zero.
• next_segment()—returns a reference to the next segment in the buffer
or NULL, if the buffer contains no additional segments.
• grow()—attempt to grow the length of the buffer by at least increment
bytes. The expiry parameter specifies the maximum amount of time to
wait for this operation to complete.
• trim()—reduce the length of the buffer and rewind. The reduced
buffer is defined by the subrange [from, to). That is, the parameters
are interpreted as follows:
♦ from—the index of the first byte to be included in the trimmed
buffer.
♦ to—the index after the last byte to be included in the trimmed
buffer.
• extract()—extract the specified range of bytes from this buffer,
returning the result as a new Buffer. The reduced buffer is defined by
the subrange [from, to). That is, the parameters are interpreted as
follows:
♦ from—the index of the first byte to be included in the trimmed
buffer.
♦ to—the index after the last byte to be included in the trimmed
buffer.
• recycle()—release the buffer’s memory, unreferencing any Storage
instances it contains.
• prepend()—add another buffer, head, to the front of this buffer.
• append()—add another buffer, tail, to the end of this buffer.
796
Implementing Custom Compression
Segment IDL interface Example 157 shows the Segment IDL interface, which is defined in the
IT_Buffer module.
// IDL
...
module IT_Buffer {
native RawData;
Segment attributes The following attributes are defined in the IT_Buffer::Segment interface:
• data—a pointer to the block of raw memory where this segment is
stored. In C++, the native RawData type maps to CORBA::Octet*.
• offset—an offset into the data block that marks the start of the bytes
belonging to this segment. In other words, the first byte belonging to
the segment is given by Segment::data + Segment::offset.
• length—the number of bytes in data that belong to this segment. The
value of length is always greater than zero.
For example, the index after the last byte in the segment is given by
Segment::data + Segment::offset + Segment::length.
• underlying_storage—returns the underlying storage as an
IT_Buffer::Storage object.
797
CHAPTER 25 | Orbix Compression Plug-in
Implementing a Compressor
Overview This section describes how to implement an IT_ZIOP::Compressor object,
which is responsible for performing compression and decompression of
GIOP messages. By implementing this IDL interface, you can define new
compression algorithms for the ZIOP plug-in.
Two operations are defined in the Compressor interface: compress() and
decompress(). Each of these operations takes a source buffer as input and
returns a transformed target buffer as output. The buffers are passed in the
form of IT_Buffer::Buffer objects.
Compressor IDL interface Example 158 shows the Compressor IDL interface, which is defined in the
IT_ZIOP module.
// IDL
#include <omg/orb.idl>
#include <orbix_pdk/buffer.idl>
...
module IT_ZIOP {
...
exception CompressionException { string reason; };
void compress(
in IT_Buffer::Buffer source,
in IT_Buffer::Buffer target
) raises (CompressionException);
void decompress(
in IT_Buffer::Buffer source,
in IT_Buffer::Buffer target
) raises (CompressionException);
798
Implementing Custom Compression
};
...
};
C++ header for Compressor Example 159 shows a sample header file for the Compressor class.
// C++
#include <orbix/corba.hh>
#include <orbix_pdk/buffer.hh>
#include <orbix_pdk/ziop.hh>
class DemoCompressorImpl :
1 public IT_ZIOP::Compressor,
public IT_CORBA::RefCountedLocalObject
{
public:
DemoCompressorImpl(
IT_ZIOP::CompressorFactory_var factory,
CORBA::Long compression_level
);
~DemoCompressorImpl();
void
compress(
IT_Buffer::Buffer_ptr source_buffer,
IT_Buffer::Buffer_ptr target_buffer
799
CHAPTER 25 | Orbix Compression Plug-in
);
void
decompress(
IT_Buffer::Buffer_ptr source_buffer,
IT_Buffer::Buffer_ptr target_buffer
);
IT_ZIOP::CompressorFactory_ptr compressor_factory();
CORBA::Long compression_level();
private:
2 IT_ZIOP::CompressorFactory_var m_factory;
CORBA::Long m_compression_level;
};
C++ implementation of Example 160 shows a sample implementation of the Compressor class.
Compressor
// C++
#include <orbix_sys/default_ts_error_handler.h>
#include <orbix/timebase.h>
800
Implementing Custom Compression
#include "demo_compressor_impl.h"
1 DemoCompressorImpl::DemoCompressorImpl(
IT_ZIOP::CompressorFactory_var factory,
CORBA::Long compression_level
):
m_factory(factory),
m_compression_level(compression_level)
{
}
DemoCompressorImpl::~DemoCompressorImpl() { }
void
2 DemoCompressorImpl::compress(
IT_Buffer::Buffer_ptr source_buffer,
IT_Buffer::Buffer_ptr target_buffer
)
{
3 source_buffer->rewind();
target_buffer->rewind();
...
}
void
4 DemoCompressorImpl::decompress(
IT_Buffer::Buffer_ptr source_buffer,
IT_Buffer::Buffer_ptr target_buffer
)
{
5 source_buffer->rewind();
target_buffer->rewind();
...
}
IT_ZIOP::CompressorFactory_ptr
6 DemoCompressorImpl::compressor_factory()
{
return m_factory;
}
CORBA::Long
7 DemoCompressorImpl::compression_level()
{
return m_compression_level;
801
CHAPTER 25 | Orbix Compression Plug-in
802
Implementing Custom Compression
803
CHAPTER 25 | Orbix Compression Plug-in
CompressorFactory IDL interface Example 161 shows the CompressorFactory IDL interface, which is defined
in the IT_ZIOP module.
// IDL
...
module IT_ZIOP {
...
typedef unsigned long CompressorId;
...
local interface CompressorFactory
{
readonly attribute CompressorId compressor_id;
readonly attribute unsigned long long compressed_bytes;
readonly attribute unsigned long long uncompressed_bytes;
readonly attribute double average_compression;
void add_sample(
in unsigned long long compressed_bytes,
in unsigned long long uncompressed_bytes
);
};
...
};
804
Implementing Custom Compression
C++ header for Example 162 shows a sample header file for the CompressorFactory class.
CompressorFactory
// C++
#include <omg/orb.hh>
#include <orbix_pdk/ziop.hh>
class DemoCompressorFactory :
1 public IT_ZIOP::CompressorFactory,
public IT_CORBA::RefCountedLocalObject
{
public:
DemoCompressorFactory(
IT_ZIOP::CompressorId compressor_id
);
virtual ~DemoCompressorFactory();
IT_ZIOP::CompressorId compressor_id();
CORBA::ULongLong compressed_bytes();
CORBA::ULongLong uncompressed_bytes();
CORBA::Double average_compression();
IT_ZIOP::Compressor_ptr
get_compressor(
CORBA::Long compression_level
);
void
add_sample(
CORBA::ULongLong compressed_bytes,
805
CHAPTER 25 | Orbix Compression Plug-in
CORBA::ULongLong uncompressed_bytes
);
private:
2 IT_ZIOP::CompressorId m_compressor_id;
CORBA::ULongLong m_compressed_bytes;
CORBA::ULongLong m_uncompressed_bytes;
IT_ZIOP::Compressor_var m_compressor;
};
// C++
#include <orbix_sys/default_ts_error_handler.h>
#include "demo_compressor_factory.h"
#include "demo_compressor_impl.h"
1 DemoCompressorFactory::DemoCompressorFactory(
806
Implementing Custom Compression
IT_ZIOP::CompressorId compressor_id
):
m_compressor_id(compressor_id),
m_compressed_bytes(0L),
m_uncompressed_bytes(0L),
m_compressor(0)
{
}
DemoCompressorFactory::~DemoCompressorFactory() { }
IT_ZIOP::CompressorId
DemoCompressorFactory::compressor_id()
{
return m_compressor_id;
}
CORBA::ULongLong
DemoCompressorFactory::compressed_bytes()
{
return m_compressed_bytes;
}
CORBA::ULongLong
DemoCompressorFactory::uncompressed_bytes()
{
return m_uncompressed_bytes;
}
CORBA::Double
2 DemoCompressorFactory::average_compression()
{
if(m_uncompressed_bytes == 0)
{
return (CORBA::Double)1.0;
}
CORBA::Double dbl_compressed_bytes =
(CORBA::Long)m_compressed_bytes;
CORBA::Double dbl_uncompressed_bytes =
(CORBA::Long)m_uncompressed_bytes;
return dbl_compressed_bytes / dbl_uncompressed_bytes;
}
IT_ZIOP::Compressor_ptr
3 DemoCompressorFactory::get_compressor(
807
CHAPTER 25 | Orbix Compression Plug-in
CORBA::Long compression_level
)
{
if(CORBA::is_nil(m_compressor))
{
m_compressor = new DemoCompressorImpl(this,
compression_level);
}
return m_compressor;
}
void
4 DemoCompressorFactory::add_sample(
CORBA::ULongLong compressed_bytes,
CORBA::ULongLong uncompressed_bytes
)
{
m_compressed_bytes += compressed_bytes;
m_uncompressed_bytes += uncompressed_bytes;
}
808
Implementing Custom Compression
The CompressionManager Example 164 shows the CompressionManager IDL interface, which is
interface defined in the IT_ZIOP module.
// IDL
...
module IT_ZIOP {
...
exception FactoryAlreadyRegistered { };
exception UnknownCompressorId { };
...
typedef sequence<CompressorFactory> CompressorFactorySeq;
void unregister_factory(
in CompressorId compressor_id
) raises (UnknownCompressorId);
CompressorFactory get_factory(
in CompressorId compressor_id
) raises (UnknownCompressorId);
Compressor get_compressor(
in CompressorId compressor_id,
in long compression_level
) raises (UnknownCompressorId);
CompressorFactorySeq get_factories();
809
CHAPTER 25 | Orbix Compression Plug-in
};
...
};
C++ registering a Example 165 shows how to register a custom CompressorFactory, which
CompressorFactory makes a custom compression algorithm available to the application. This
segment of code should be called when the application starts up.
// C++
1 CORBA::Object_var objref =
orb->resolve_initial_references("IT_CompressionManager");
if (CORBA::is_nil(objref))
{
cerr << "Could not resolve reference" << endl;
return 1;
}
2 compression_manager =
IT_ZIOP::CompressionManager::_narrow(objref);
810
Implementing Custom Compression
if (CORBA::is_nil(compression_manager))
{
cerr << "Could not _narrow object to type
IT_ZIOP::CompressionManager" << endl;
return 1;
}
811
CHAPTER 25 | Orbix Compression Plug-in
812
APPENDIX A
Orbix IDL
Compiler Options
This appendix describes the syntax of the IDL compiler
command, along with the relevant options and switches.
813
APPENDIX A | Orbix IDL Compiler Options
Note: You must specify at least one plug-in switch, such as -poa or
-base, unless you modify the IDL configuration file to set IsDefault for
one or more plug-ins to Yes. (see page 821). As distributed, the
configuration file sets IsDefault for all plug-ins to No.
General switches You can qualify the idl command with one or more of the following
switches. Multiple switches are colon-delimited.
Switch Description
814
Command Line Switches
Switch Description
815
APPENDIX A | Orbix IDL Compiler Options
Modifiers for all C++ plug-in Table 31 describes modifiers that can be used with all C++ plug-in
switches switches.
Modifier Description
-d[decl-spec] Creates NT declspecs for dllexport and dllimport. If you omit decl-spec,
idl uses the stripped IDL module’s name.
For example, the following command:
idl -dIT_ART_API foo.idl
yields this code:
#if !defined(IT_ART_API)
#if defined(IT_ART_API_EXPORT)
#define IT_ART_API IT_DECLSPEC_EXPORT
#else
#define IT_ART_API IT_DECLSPEC_IMPORT
#endif
#endif
If you compile and link a DLL with the idl-generated code within it,
IT_ART_API_EXPORT must be a defined preprocessor symbol so that
IT_ART_API is set to dllexport. All methods and variables in the generated
code can be exported from the DLL and used by other applications. If
IT_ART_API_EXPORT is not defined as a preprocessor symbol, IT_ART_API is
set to dllimport; methods and variables that are defined in the generated
code are imported from a DLL.
816
Plug-in Switch Modifiers
Modifier Description
-Ocpath Sets the output directory for client stub (.cxx) files.
-xAMICallbacks Generates stub code that enables asynchronous method invocations (AMI).
817
APPENDIX A | Orbix IDL Compiler Options
Modifiers for -base, -psdl, and Table 32 describes the modifiers for -base, -psdl, and -pss_r.
-pss_r switches
Table 32: Modifier for -base, -psdl, and -pss_r plug-in switches
Modifier Description
-c[suffix.]ext Specifies the format for stub file names. The default name is idl-name.cxx.
For example, the following command:
idl -base:-cc foo.idl
yields a server skeleton file with this name:
foo.c
If the argument embeds a period (.), the string to the left of the period is
appended to the IDL file name; the string to the right of the period specifies
the file extension. For example, the following command:
idl -base:-c_client.c foo.idl
yields the following stub file name:
foo_client.c
Modifiers for -jbase and -jpoa Table 33 describes the modifiers for -jbase and -jpoa.
switches
Modifier Description
-Ppackage Use package as the root scope to package all unspecified modules. By
default, all Java output is packaged in the IDL module names.
-Pmodule=package Use package as the root scope for the specified module.
-Odir Output all java code to dir. The default is the current directory.
-Mreflect Specifies the POA dispatch model to use either reflection or cascading
-Mcascade if-then-else statements. The default is reflect.
818
Plug-in Switch Modifiers
Modifier Description
-ETRUE Initialize the string fields of structures and exceptions to the empty string.
-EFALSE The default is FALSE, meaning that string fields are initialized to null.
-TTRUE Generate toString() overrides for the type stubs. Default is FALSE.
-TFALSE
Modifiers for -poa switch Table 34 describes the modifiers for -poa.
Modifier Description
-s[suffix.]ext Specifies the skeleton file name. The default name is idl-nameS.cxx for
skeleton files.
For example, the following command:
idl -poa:-sc foo.idl
yields a server skeleton file with this name:
fooS.c
If the argument embeds a period (.), the string to the left of the period is
appended to the IDL file name; the string to the right of the period specifies
the file extension. For example, the following command:
idl -poa:-s_server.h foo.idl
yields the following skeleton file name:
foo_server.c
819
APPENDIX A | Orbix IDL Compiler Options
Modifier Description
-b[suffix.]ext Specifies the format of the header file names in generated #include
statements. Use this modifier if you also use the -h modifier with the -base
plug-in switch.
For example, if you specify a .h extension for -base-generated header files,
specify the same extension in -poa-generated #include statements, as in the
following commands:
idl -base:-hh foo.idl
idl -poa:-bh foo.idl
These commands generate header file foo.h, and include in skeleton file
fooS.cxx a header file of the same name:
#include "foo.h"
If the argument embeds a period (.), the string to the left of the period is
appended to the IDL file name; the string to the right of the period specifies
the file extension. For example, the following command:
idl -poa:-b_client.h foo.idl
yields in the generated skeleton file the following #include statement:
#include "foo_client.h"
-mincl-mask #include statements with file names that match mask are ignored in the
generated skeleton header file. This lets the code generator ignore files that it
does not need. For example, the following switch:
-momg/orb
directs the idl compiler to ignore this #include statement in the IDL/PSDL:
#include <omg/orb.idl>
-pmultiple Sets the dispatch table to be 2 to the power of multiple. The default value of
multiple is 1. Larger dispatch tables can facilitate operation dispatching, but
also increase code size and memory usage.
820
IDL Configuration File
plugin-type
{
Switch = switch-name;
ShlibName = path;
ShlibMajorVersion = version
ISDefault = "{ YES | NO }";
PresetOptions = "-plugin-modifier[, -plugin-modifier]..."
# plugin-specific settings...
# ...
}
821
APPENDIX A | Orbix IDL Compiler Options
Cplusplus
{
Switch = "base";
ShlibName = "it_cxx_ibe";
ShlibMajorVersion = "1";
IsDefault = "NO";
PresetOptions = "-t";
822
IDL Configuration File
POACxx
{
Switch = "poa";
ShlibName = "it_poa_cxx_ibe";
ShlibMajorVersion = "1";
IsDefault = "NO";
PresetOptions = "-t";
IFR
{
Switch = "R";
ShlibName = "it_ifr_ibe";
ShlibMajorVersion = "1";
IsDefault = "NO";
PresetOptions = "";
};
PSSDLCxx
{
Switch = "psdl";
ShlibName = "it_pss_cxx_ibe";
ShlibMajorVersion = "1";
IsDefault = "NO";
PresetOptions = "-t";
UsePSSDLGrammar = "YES";
823
APPENDIX A | Orbix IDL Compiler Options
PSSRCxx
{
Switch = "pss_r";
ShlibName = "it_pss_r_cxx_ibe";
ShlibMajorVersion = "1";
IsDefault = "NO";
PresetOptions = "-t";
UsePSSDLGrammar = "YES";
POAJava
{
Switch = "jpoa";
ShlibName = "jpoa";
ShlibMajorVersion = "1";
IsDefault = "NO";
};
Given this configuration, you can issue the following idl commands on the
IDL file foo.idl:
idl -base foo.idl Generates client stub and header code.
idl -poa foo.idl Generates POA code.
idl -base -poa foo.idl Generates code for both client stub and header
code and POA code.
824
APPENDIX B
IONA Foundation
Classes Library
For each platform, IONA distributes several variants of its IONA foundation
classes (IFC) shared library, which provides a number of proprietary
features, such as a threading abstraction. For each IFC library, IONA
provides checked and unchecked variants:
• Checked variants are suitable for development and testing: extra
checking is built into the code—for example, it throws an exception
when a thread attempts to lock a mutex that it has already locked.
• Unchecked variants are suitable for deployed applications, which have
been tested for thread safety.
Each UNIX distribution provides IFC libraries that support the POSIX thread
package. The following platforms have multiple IFC libraries, which support
different thread packages:
825
APPENDIX B | IONA Foundation Classes Library
Unix:
Unchecked $IT_PRODUCT_DIR/shlib/native-thread-pkg/libit_ifc_compiler-spec
Checked $IT_PRODUCT_DIR/shlib/native-thread-pkg/checked/libit_ifc_compiler-spec
Windows:
Unchecked %IT_PRODUCT_DIR%\bin\windows\it_ifc3_vc60.dll
Checked %IT_PRODUCT_DIR%\bin\windows\checked\it_ifc3_vc60.dll
UNIX:
$IT_PRODUCT_DIR/shlib/default/ifc-lib-sym-link
Windows:
%IT_PRODUCT_DIR%\bin\it_ifc3_vc60.dll
826
Selecting an IFC Library
Unix On UNIX systems, you can set a program’s IFC library in two ways:
• (Recommended) When linking the program, use the linker’s run path
feature, and set it to the desired IFC library directory. For example, set
the -R option with the Sun compiler.
• Set the program’s environment variable (LD_LIBRARY_PATH or
SHLIB_PATH). Keep in mind that other services such as the Locator also
might use this environment and can be affected by this setting.
827
APPENDIX B | IONA Foundation Classes Library
828
APPENDIX C
Orbix C++
Libraries
Library Name Function
829
APPENDIX C | Orbix C++ Libraries
830
Library Name Function
831
APPENDIX C | Orbix C++ Libraries
832
APPENDIX D
Orbix Policies
Orbix supports a number of proprietary policies in addition to
the OMG policies. To create a policy of the proper type you
must know the policy’s tag.
833
APPENDIX D | Orbix Policies
Data Values
A client’s BindingEstablishmentPolicy is determined by the members of
its BindingEstablishmentPolicyValue, which is defined as follows:
struct BindingEstablishmentPolicyValue
{
TimeBase::TimeT relative_expiry;
unsigned short max_binding_iterations;
unsigned short max_forwards;
TimeBase::TimeT initial_iteration_delay;
float backoff_ratio;
};
See Also
“BindingEstablishmentPolicy” on page 253
RelativeBindingExclusiveRoundtripTimeoutPolicy
Policy Tag
IT_CORBA::RELATIVE_BINDING_EXCLUSIVE_ROUNDTRIP_TIMEOUT_POLICY_ID
Data Values
This policy’s value is set in 100-nanosecond units.
See Also
“RelativeBindingExclusiveRoundtripTimeoutPolicy” on page 256
834
Client Side Policies
RelativeBindingExclusiveRequestTimeoutPolicy
Policy Tag
IT_CORBA::RELATIVE_BINDING_EXCLUSIVE_REQUEST_TIMEOUT_POLICY_ID
Data Values
This policy’s value is set in 100-nanosecond units.
See Also
“RelativeBindingExclusiveRequestTimeoutPolicy” on page 256
RelativeConnectionCreationTimeoutPolicy
Policy Tag
IT_CORBA::RELATIVE_CONNECTION_CREATION_TIMEOUT_POLICY_ID
Data Values
The policy’s value is set in 100-nanosecond units.
See Also
“RelativeConnectionCreationTimeoutPolicy” on page 256
InvocationRetryPolicy
Policy Tag
IT_CORBA::INVOCATION_RETRY_POLICY_ID
Data Values
A client’s InvocationRetryPolicy is determined by the members of its
InvocationRetryPolicyValue, which is defined as follows:
struct InvocationRetryPolicyValue
{
unsigned short max_retries;
unsigned short max_rebinds;
unsigned short max_forwards;
TimeBase::TimeT initial_retry_delay;
float backoff_ratio;
};
835
APPENDIX D | Orbix Policies
See Also
“InvocationRetryPolicy” on page 256
836
POA Policies
POA Policies
ObjectDeactivationPolicy
Policy Tag
IT_PortableServer::OBJECT_DEACTIVATION_POLICY_ID
Data Values
Three settings are valid for this policy:
DELIVER(default)The object deactivates only after processing all pending
requests, including any requests that arrive while the
object is deactivating.
DISCARD The POA rejects incoming requests with an exception of
TRANSIENT. Clients should be able to reissue discarded
requests.
HOLD Requests block until the object deactivates. A POA with a
HOLD policy maintains all requests until the object
reactivates. However, this policy can cause deadlock if
the object calls back into itself.
See Also
“Setting deactivation policies” on page 347
PersistentModePolicy
Policy Tag
IT_PortableServer::PERSISTENCE_MODE_POLICY_ID
Data Values
The only valid value for this policy is
IT_PortableServer::DIRECT_PERSISTENCE.
See Also
“Direct persistence” on page 312
837
APPENDIX D | Orbix Policies
WellKnownAddressingPolicy
Policy Tag
IT_CORBA::WELL_KNOWN_ADDRESSING_POLICY_ID
Data Values
This policy takes a string that maps to the prefix of the configuration variable
listing the well known address.
See Also
“Direct persistence” on page 312
WorkQueuePolicy
Policy Tag
IT_WorkQueue::WORK_QUEUE_POLICY_ID
Data Values
This policy takes a WorkQueue object.
See Also
“Creating the WorkQueue” on page 334
838
Security Policies
Security Policies
For more detailed information on the following policies see the CORBA
SSL/TLS Guide.
SessionCachingPolicy
Policy Tag
IT_TLS_API::TLS_SESSION_CACHING_POLICY
Data Values
The following settings are valid for this policy:
CACHE_NONE(default) The ORB does not cache session data.
CACHE_CLIENT The ORB will cache session data for client side
of a connection.
CACHE_SERVER The ORB will cache session data for server
side of a connection.
CACHE_SERVER_AND_CLIENT The ORB stores session information for both
the client and server side of a connection.
MaxChainLengthPolicy
Policy Tag
IT_TLS_API::TLS_MAX_CHAIN_LENGTH_POLICY
Data Values
This policy takes an integer.
CertContraintsPolicy
Policy Tag
IT_TLS_API::TLS_CERT_CONSTRAINTS_POLICY
Data Values
This policy takes an IT_TLS_API::CertConstraints object.
839
APPENDIX D | Orbix Policies
CertValidatorPolicy
Policy Tag
IT_TLS_API::TLS_CERT_VALIDATOR_POLICY
Data Values
This policy takes a IT_TLS::CertValidator object.
840
Firewall Proxy Policies
InterdictionPolicy
Policy Tag
IT_FPS::INTERDICTION_POLICY_ID
Data Values
PROCEED(default)This is the default behavior of the firewall proxy service
plug-in. A POA with its INTERDICTION policy set to
PROCEED will be proxified.
PREVENT This setting tells the firewall proxy service plug-in to not
proxify the POA. POAs with their INTERDICTION policy set
to PREVENT will not use the firewall proxy service and
requests made on objects under its control will come
directly from the requesting clients.
841
APPENDIX D | Orbix Policies
842
APPENDIX E
DLL Name
Mappings
This appendix provides a list of link-time files (DLL side-decks)
that are supplied with Orbix Mainframe in the orbixhlq.LKED
data set. In each case, it provides the z/OS member name of
the file, its equivalent HFS root name (if applicable), and a
brief description of its purpose
Points to note • On z/OS, the DLL member name equates to the side-deck name,
except that a version number is appended to the end of the DLL name.
• All names begin with a prefix of ORX.
• An HFS root name of it_art, for example, equates to a library
filename of libit_art.so on Solaris.
843
APPENDIX E | DLL Name Mappings
844
Table 35: DLL Name Mappings (Sheet 3 of 4)
845
APPENDIX E | DLL Name Mappings
846
Index
Symbols Char 415
802 Octet 415
string 418
WChar 415
A wstring 418
Abstract storage home extraction operators 412
defined 560 inserting user-defined types 410
defining 563 inserting values 409
factory operation 566 alias 420
forward declaration 567 array 416
inheritance 567 Boolean 415
keys 564 bounded string alias 420
operations 566 Char 415
Abstract storage type Octet 415
defined 560 string 417
defining 561 WChar 415
definition syntax 561 wstring 417
forward declaration 562 insertion operators 409
inheritance 561 memory management 410, 412
from storage object 562 querying type code 422
operations 562 append() operation 796
state members 561 Application
activate() running 54
calling on POAManager 99, 325 arguments() 461
activate_object() 96, 272, 317, 319 Arithmetic operators 146
activate_object_with_id() 272, 317, 319 ArrayDef 476
Active object map 300 Array type
disabling 309 _forany 416
enabling 309 Association
using with servant activator 341 constructors 579
add_ior_component() 684 operations 580
addMember() 541 Asynchronous method invocations 361–371
add_plain_text_key() operation 769 client implementation 369
_add_ref() 287 implied IDL 364
add_sample() operation 805, 808 reply handlers 365
AliasDef 475 Attribute
allocate_slot_id() 716 client-side C++ mapping for 224
Any type 407–447 genie-generated 77
extracting user-defined types 413 in IDL 117
extracting values from 412 readonly 66
alias 421 average_compression attribute 805, 808
array 416
Boolean 415
bounded string alias 420
847
INDEX
848
INDEX
849
INDEX
850
INDEX
851
INDEX
852
INDEX
H policy 317
hash() 211 Implied IDL 364
has_no_active_member() 443 attribute mapping 364
Hello World! example 49 operation mapping 364
high availability sendc_get operation 364
replication 597 sendc_ operation 364
hold_requests() indirect persistence
calling on POAManager 325 and corbaloc URL 758
Inheritance
implementing by 76
I in abstract storage home 567
IDL 107 in interfaces 120
attribute in 66 in servant classes 290, 291
attributes in 117 storage home 570
compiling 70 Initial naming context
constant expressions in 146 obtaining 509
empty interfaces 119 Initial reference
exceptions 373–392 registering 717
exceptions in 118 initial reference IDs
interface definition 111 IT_Configuration 775
interface repository definitions 467 IT_EventLog 781
object types 471 IT_Locator 764
module definition 109 IT_PlainTextKeyForwarder 769
name scoping 109 inout parameters 115
one-way operations in 115 in parameters 115
operation in 66, 114 Integer
parameters in 114 constant in IDL 143
pragma directives 496 Interception points 672
precedence of operators 146 client flow 691
prefix pragma 497 client interceptors 688, 690, 696
user-defined types 142 client-side data 673, 695
version pragma 497 IOR data 673
IDL compiler 70 IOR interceptors 682
generated files 70 request data 673, 685
generating implied IDL 364 server flow 704
options server interceptors 703, 709
-base 70 server-side data 673, 708
-flags 70 timeout constraints 686
-poa 70 Interceptor interface 672
output 70 Interceptors, see Portable interceptors
populating interface repository 468 Interface
idlgen utility 68 client proxy for 202
iiops protocol specifier components 113
corbaloc 756 defined in IDL 111
implementation repository dynamic generation 449
and named keys 760, 761 empty 119
IMPLICIT_ACTIVATION policy 317, 320 forward declaration of 124
Implicit object activation 271, 320 inheritance 120
overriding default POA 323 inheritance from Object interface 122
853
INDEX
854
INDEX
855
INDEX
856
INDEX
857
INDEX
858
INDEX
859
INDEX
860
INDEX
861
INDEX
862
INDEX
863
INDEX
V
validate_connections() 197
value() 461
ValueBoxDef 475
ValueDef 475
_var object reference type 204, 215–218
assignment operator 216
class members 215
constructors 215
conversion operator 216
default constructor 215
destructor 216
explicit conversion operator 216
in() 216
indirection operator 216
inout() 216
narrowing 216
out() 216
widening 216
Version pragma 497
W
WellKnownAddressingPolicy 306
Wide character
constant in IDL 144
Widening
_ptr 212
assignment 212
_var 216
Wide string
constant in IDL 144
864