0% found this document useful (0 votes)
185 views

Developing A CORBA Server

The document describes developing a CORBA server which involves 5 steps: 1. Create an IDL file to specify the public interface of the servant class. 2. Compile the IDL file to generate usage files needed to implement the servant. 3. Add declarations to the servant implementation header file. 4. Complete the servant implementation file by adding code to implement the business logic. 5. Create the server main file with code for initialization and creating servant objects.

Uploaded by

satheesh4590
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
185 views

Developing A CORBA Server

The document describes developing a CORBA server which involves 5 steps: 1. Create an IDL file to specify the public interface of the servant class. 2. Compile the IDL file to generate usage files needed to implement the servant. 3. Add declarations to the servant implementation header file. 4. Complete the servant implementation file by adding code to implement the business logic. 5. Create the server main file with code for initialization and creating servant objects.

Uploaded by

satheesh4590
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 59

Developing a CORBA server

Use this task to develop a CORBA server to service requests for business functions used in the implementation of client
objects. The instructions and code extracts provided in this task are based on the development of the WSLoggerServer
sample, for which files are included with WebSphere in the following directory:
WAS_HOME/Enterprise/samples/sampcppsdk.
Developing a CORBA server involves developing a server implementation class (known as a servant) and a server, as
described in the following steps:

1. Create and edit an IDL file, servant.idl, to specify the public interface to the servant object class; where
servant is the name of the server implementation class.
For more information about creating and editing an IDL file for the servant object class, see Defining the
interface for a servant implementation (servant.idl).
This steps results in a fully-specified servant.idl file.

2. Compile the servant IDL file,servant.idl, to produce the usage binding files needed to implement and use the
servant object within a particular programming language.
For more information about compiling an IDL file, see Compiling the servant IDL (using idlc).
This step results in the set of usage binding files required for the servant.idl file.
3. Add declarations for class variables, constructors, and destructors to the servant implementation header
(servant.ih).
For more information about adding declarations to an implementation header, see Adding declarations to a
CORBA servant implementation header (servant.ih).
This step results in the servant implementation header file, servant.idl, that contains all the declarations for
class variables, constructors, and destructors needed by the implementation.

4. Complete the servant implementation servant_I.cpp, to add the code that is to implement the servant
business logic.
For more information about completing the servant implementation, see Adding code to a CORBA servant
implementation (servant_I.cpp).
This step results in the server implementation file, servant_I.cpp, that contains the code needed by the
implementation to support the business logic.

5. Create the server main, server.cpp, to write the code for the methods that the server implements (for
example, to perform initialization tasks and create servant objects).
For more information about completing the servant implementation, see Creating the server main code
(server.cpp).
This step results in the server main source file, server.cpp, that contains the main method and associated
code needed to implement the server.
Build the server code, as described in Building a C++ CORBA server. Adding declarations to a CORBA
servant class definition (servant.ih)
Use this task to add declarations for class variables, constructors, and destructors for a CORBA servant class to its
skeleton implementation header file, servant.ih. This defines any private variables for the implementation code in the
associated servant_I.cpp file.
This task follows on from the task to compile the servant.idl file that defines the public interface for the server
implementation class. For more information about compiling the IDL file, which creates the servant.ih file, see Compiling
the servant IDL (using idlc).
To add declarations for class variables, constructors, and destructors to an implementation header file,servant.ih,
complete the following steps:

1. At a command line change to the directory that contains the servant.ih file, where servant is the name of the
servant class.
2. Edit the implementation header file, servant.ih, to add appropriate declarations for class variables,
constructors, and destructors. For more information about the types of declarations that you can add to an
implementation header file, see IDL type declarations.
For example, the idlc command idlc -ehh:ih:ic:uc:sc -mdllname=WSLogger WSLogger.idl
converted the following interface declaration to the class declaration in the WSLogger.ih file. The
WSLogger.ih file was then edited to add the extra declarations shown in bold.
Implementation header in WSLogger.ih
Example: WSLogger interface and declarations
added to the skeleton implementation header
Interface declaration in WSLogger.idl

interface WSLogger { class WSLogger_Impl : public virtual

void setFileName(in string newFileName); ::WSLogger_Skeleton {

string getFileName(); public:

void setMethodName( in string newMethodName ); ::CORBA::Void setFileName (const char*

string getMethodName(); newFileName);

short openLogFile(); char* getFileName ();

short closeLogFile(); ::CORBA::Void setMethodName (const char*

short writeLogMessage(in string newMessage, in short newSeverity); newMethodName);

const short DMY_DATE_FORMAT = 1; char* getMethodName ();

const short MDY_DATE_FORMAT = 2; ::CORBA::Short openLogFile ();

void setDateFormat(in unsigned short newDateFormat); ::CORBA::Short closeLogFile ();

unsigned short getDateFormat(); ::CORBA::Short writeLogMessage (const char*

}; newMessage, ::CORBA::Short newSeverity);

::CORBA::Void setDateFormat (::CORBA::UShort

newDateFormat);

::CORBA::UShort getDateFormat ();

private:

char * fileName;

char * methodName;

::CORBA::UShort dateFormat;

ofstream logFile;

::CORBA::UShort logFileOpen;

public:

WSLogger_Impl( char * newFileName );

virtual ~WSLogger_Impl();

};

You can next add code to skeleton implementation definition, servant_I.cpp, to implement the business logic, as
described in Completing the server implementation (server_I.cpp).
Adding code to a CORBA servant implementation (servant_I.cpp)
Use this task to add code for a CORBA server implementation class to its skeleton implementation file, servant_I.cpp.
The code defines the methods that implement the business logic for the server implementation class, servant.
This task follows on from the task to add declarations for class variables, constructors, and destructors to the servant
implementation header file,servant.ih. For more information about adding declarations to an implementation header, see
Adding declarations to a CORBA servant implementation header (servant.ih).
To add code to an implementation file,servant_I.cpp, to add the business logic that the servant is to provide, complete
the following steps:

1. At a command line change to the directory that contains the servant_I.cpp file, where servant is the name of
the server implementation class.
2. Edit the implementation file, servant_I.cpp, to add appropriate code to implement the business logic methods.
For example, the idlc command idlc -ehh:ih:ic:uc:sc -mdllname=WSLogger WSLogger.idl
converted the following interface declaration to the skeleton methods in the implementation file,
WSLogger_I.cpp. The WSLogger_I.cpp file was then edited to add the code to implement the methods, with
the code added for the WSLogger_Impl::writeLogMessage method shown in bold.
::CORBA::Void WSLogger_Impl::setFileName (const char* newFileName)

char* WSLogger_Impl::getFileName ()
{

::CORBA::Void WSLogger_Impl::setMethodName (const char* newMethodName)

char* WSLogger_Impl::getMethodName ()

::CORBA::Short WSLogger_Impl::openLogFile ()

::CORBA::Short WSLogger_Impl::closeLogFile ()

// This method writes one line of message text to the log file. The line

// prefaced with the current date and time in the currently specified
// format, the current method name (if any), the severity level, and

// the message text.

::CORBA::Short WSLogger_Impl::writeLogMessage (const char* newMessage, ::CORBA::Short newSeverity)

::CORBA::String_var timeString;

if ( logFileOpen == FALSE )

return( -1 );

// Get the date and time string.

time_t tp;

time_t tp2;

if ( ( tp = time(&tp2) ) != -1 )

struct tm *x = gmtime( &tp2 );

timeString = ::CORBA::string_dup( ctime( &tp2 ) );

// Determine the day and month.

::CORBA::String_var day = ::CORBA::string_alloc( 3 );

::CORBA::String_var month = ::CORBA::string_alloc( 4 );

day[0] = timeString[8];

day[1] = timeString[9];

day[2] = 0;

month[0] = timeString[4];

month[1] = timeString[5];

month[2] = timeString[6];

month[3] = 0;

// Copy the time and year.

::CORBA::String_var time = ::CORBA::string_alloc( 14 );

strncpy( time, (const char *) &timeString[11], 13 );

time[13] = 0;

// Output the time of the log message.

if ( dateFormat == DMY_DATE_FORMAT )

logFile << day << " " << month;


else if ( dateFormat == MDY_DATE_FORMAT )

logFile << month << " " << day;

logFile << " " << time << ", ";

if ( getMethodName() != NULL )

logFile << getMethodName() << ", ";

logFile << "severity " << newSeverity << ": ";

// Output the log message.

logFile << newMessage << endl;

return 0;

::CORBA::Void WSLogger_Impl::setDateFormat (::CORBA::UShort newDateFormat)

::CORBA::UShort WSLogger_Impl::getDateFormat ()

{
}

You can next create the server main code (server.cpp), to implement the server, as described in Creating a CORBA
server main code (server.cpp).
1.
Creating the CORBA server main code (server.cpp)
Use this task to create a CORBA server that hosts a servant object. The server performs the following tasks
1. Validating user input
2. Initializing the server environment
3. Accessing naming contexts
4. Naming, creating, and binding a servant object
5. Creating a server shutdown object
6. Going into a wait loop
7. Servicing requests
This task follows on from adding code for the business logic methods in the servant implementation file,servant_I.cpp.
For more information about adding code to a servant implementation file, see Completing the servant implementation
(servant_I.cpp).
To create the main code for a CORBA server, complete the following steps:

1. Create a source file,servantServer.cpp, where servant is the name of the implementation class for which the
server is to service requests.
2. Edit the server source file, servantServer.cpp, to add appropriate code to implement the server methods. To
do this, complete the following steps:
1. Add include statements and global declarations needed, as described in Creating CORBA server
main code (server.cpp), adding include statements and global declarations.
2. Add the main method, in the form:
3. void main( int argc, char *argv[] )

4. {

5. ::CORBA::Object_ptr objPtr;

6. ::CORBA::Status stat;

7. int rc = 0;

8. }

Edit the server source file, servantServer.cpp, to add appropriate code to check the input parameters provided on the
command used to start the server, as described in Creating CORBA server main code (server.cpp), adding
code to check input parameters.
Edit the server source file, servantServer.cpp, to add appropriate code to initialize the server environment, as
described in Creating CORBA server main code (server.cpp), adding code to initialize the server
environment.
Edit the server source file, servantServer.cpp, to add appropriate code to access naming contexts, as described in
Creating CORBA server main code (server.cpp), adding code to access naming contexts.
At this point initialization has been accomplished and a naming context created for servant objects.
Edit the server source file, servantServer.cpp, to add appropriate code to name, create, and bind servant objects, as
described in Creating CORBA server main code (server.cpp), adding code to name, create, and bind servant
objects.
Edit the server source file, servantServer.cpp, to add code to create a server shutdown object, as described in
Creating CORBA server main code (server.cpp), adding code to create a server shutdown object.
Edit the server source file, servantServer.cpp, to add code to put the server into an infinite loop (to service any ORB
requests received), as described in Creating CORBA server main code (server.cpp), adding code to put the
server into an infinite loop.
Edit the server source file, servantServer.cpp, to add code to shutdown the server and release resources used, as
described in Creating CORBA server main code (server.cpp), adding code to shutdown the server and
release resources used.
Building a C++ CORBA server
This topic provides an overview of the task to build the code for a C++ CORBA server. The actual steps that you
complete depend on the development environment that you use.
For example, if you are using the Microsoft C++ 6.0 compiler on Windows NT to build a C++ CORBA server, you can
use the following commands:

1. Compile the server.cpp, servant_I.cpp, and servant_S.cpp files.


At a command line, type the following command for each file:
-DSOMCBNOLOCALINCLUDES -D_MS_INC_DIR=msvc60_install\include

-D_USE_NAMESPACE -DNO_STRICT -Imsvc60_install\include -Imsvc60_install\include

-Iwasee_install\include -I. filename

Where

msvc60_install
is directory in which the Microsoft C++ 6.0 compiler is installed; for example, d:\msvc60\vc98.

wasee_install
is the directory into which WebSphere Application Server enterprise services is installed.

filename
is the name of the file to be compiled (server.cpp, servant_I.cpp, or servant_S.cpp).
2. link /nologo /DEBUG /OUT:WSLoggerServer.exe

3. /DEFAULTLIB:\WebSphere\AppServer\Enterprise\lib\somosa1m.lib

4. \WebSphere\AppServer\Enterprise\lib\somororm.lib \WebSphere\AppServer\Enterprise\lib\somsrvsm.lib

5. servantServer.obj servant_I.obj servant.obj

This task is one step of the parent task, Developing a CORBA client.
For more examples of building C++ CORBA server code (on several platforms) for WebSphere Application Server, see
the samples article "Tutorial: Creating a user-defined C++ server and client" at
WAS_HOME/Enterprise/samples/sampeex/sampcppsdk/wsBuildLogger.htm.
Related tasks...

Parent: Developing a
CORBA server

Related concepts...

The CORBA server


programming model

The CORBA programming


model
A Simple C++
Client/Server in
CORBA
By Carlos Jiménez
de Parga | 27 Sep
2009
An introduction to
the Visual C++
CORBA
development
Top of Form
/w EPDw UKMTAy

Download source
files - 29.22 KB
Contents
Introduction
1.1 Interface
Definition
Language (IDL)
1.2 Mapping IDL
to a Programming
Language
1.3 IDL Compilers
1.4 Making a
Remote Call
1.5 CORBA
Services
Developing a
Simple Client-
server Application
2.1 The Business
Logic Domain
2.2 Writing the
IDL
2.3 Generating
the Skeleton and
the Stub
2.4 Implementing
the Servant Class
2.5 Creating the
Server
2.6 Implementing
the Client
2.7 Running the
Client-server
Application
CORBA Benefits
and Drawbacks
Further Reading
Acknowledgemen
ts
1 Introduction
The Object
Management
Group is a not-for-
profit
organization that
promotes the use
of object-oriented
technologies.
Among other
things, it defines
the UML and
CORBA standards.
CORBA is an
acronym for
Common ORB
Architecture. The
phrase common
architecture
means a technical
standard, so
CORBA is simply a
technical
standard for
something called
an ORB.
In turn, ORB is an
acronym for
object request
broker, which is
an object-oriented
version of an
older technology
called remote
procedure call
(RPC). An ORB or
RPC is a
mechanism for
invoking
operations on an
object (or calling
a procedure) in a
different
(“remote”)
process that may
be running on the
same, or a
different,
computer. At a
programming
level, these
“remote” calls
look similar to
“local” calls. In
fact, CORBA
makes it possible
for a client
application
written in one
programming
language, say,
Java, to make a
remote call to a
server
implemented in a
different
programming
language, say, C+
+. This language
independence
arises because
the public
interfaces of a
server application
are defined in an
IDL file and
CORBA defines
mappings from
IDL to many
programming
languages,
including C, C++,
Java, COBOL, Ada,
SmallTalk and
Python.
1.1 Interface
Definition
Language (IDL)
An IDL file defines
the public
application
programming
interface (API)
that is exposed
by objects in a
server
application. The
type of a CORBA
object is called an
interface, which is
similar in concept
to a C++ class or
a Java interface.
An example IDL
file is shown
below.
Collapse | Copy
Code
module Finance {
typedef
sequence<string
> StringSeq;
struct
AccountDetails {
string name;
StringSeq
address;
long
account_number;
double
current_balance;
};
exception
insufficientFunds
{ };
interface
Account {
void deposit(in
double amount);
void
withdraw(in
double amount)
raises(insufficient
Funds);
readonly
attribute
AccountDetails
details;
};
};
As the above
example shows,
IDL types may be
grouped into a
module. This
construct serves
a purpose similar
to a C++
namespace or a
Java package: it
places a prefix on
the names of
types to prevent
namespace
pollution. The
scoping operator
in IDL is “::”. For
example,
Finance::Account
is the fully-scoped
name of the
Account type
defined in the
Finance module.
An IDL interface
may contain
operations and
attributes (and, if
you want, also
nested types).
Many people
mistakenly
assume that an
attribute is similar
in concept to an
instance variable
in C++ (a field in
Java). This is
wrong. An
attribute is simply
syntactic sugar
for a pair of
get- and set-style
operations. An
attribute can be
readonly, in which
case it maps to
just a get-style
operation.
The parameters
of an operation
have a specified
direction, which
can be in
(meaning that the
parameter is
passed from the
client to the
server), out (the
parameter is
passed from the
server back to the
client) or inout
(the parameter is
passed in both
directions).
Operations can
also have a return
value. An
operation can
raise (throw) an
exception if
something goes
wrong. There are
over 30
predefined
exception types,
called system
exceptions, that
all operations can
throw, although in
practice system
exceptions are
raised by the
CORBA runtime
system much
more frequently
than by
application code.
In addition to the
pre-defined
system
exceptions, new
exception types
can be defined in
an IDL file. These
are called user-
defined
exceptions. A
raises clause on
the signature of
an operation
specifies the user-
defined
exceptions that it
might throw.
Parameters to an
operation (and
the return value)
can be one of the
built-in types—for
example, string,
boolean or long—
or a “user-
defined” type that
is declared in an
IDL file. User-
defined types can
be any of the
following:
A struct. This is
similar to a C/C+
+ struct or a Java
class that
contains only
public fields.
A sequence. This
is a collection
type. It is like a
one-dimensional
array that can
grow or shrink.
The IDL-to-C++
mapping predates
the C++ standard
template library
so, unfortunately,
an IDL sequence
does not map to a
std::vector.
Instead, the IDL-
to-C++ mapping
defines its own
vector-like data
type.
An array. The
dimensions of an
IDL array are
specified in the
IDL file, so an
array is of fixed
size, that is, it
cannot grow or
shrink at runtime.
Arrays are rarely
used in IDL. The
sequence type is
more flexible and
so is more
commonly used.
A typedef. This
defines a new
name for an
existing type. For
example, the
statement below
defines age that
is represented as
a short:
Collapse | Copy
Code
typedef short
age;
A common, and
very important,
use of typedef is
to associate a
name with a
sequence or array
declaration. For
example:
Collapse | Copy
Code
typedef
sequence<string
> StringSeq;
A union. This type
can hold one of
several values at
runtime, for
example:
Collapse | Copy
Code
union Foo
switch(short) {
case 1: boolean
boolVal;
case 2: long
longVal;
case 3: string
stringVal;
};
An instance of
Foo could hold a
boolean, a long or
a string. The case
label indicates
which value is
currently active.
An enum is
conceptually
similar to a
collection of
constant integer
declarations. For
example:
Collapse | Copy
Code
enum color {red,
green, blue};
Internally, CORBA
uses integer
values to
represent
different enum
values.
1.2 Mapping IDL
to a Programming
Language
As already
mentioned, IDL is
used to define the
public API that is
exposed by
objects in a
server
application. IDL
defines this API in
a way that is
independent of
any particular
programming
language.
However, for
CORBA to be
useful, there must
be a mapping
from IDL to a
particular
programming
language. For
example, the IDL-
to-C++ mapping
enables people to
develop CORBA
applications in C+
+, while the IDL-
to-Java mapping
enables people to
develop CORBA
applications in
Java.
The CORBA
standard
currently defines
mappings from
IDL to the
following
programming
languages: C, C+
+, Java, Ada,
Smalltalk, COBOL,
PL/I, LISP, Python
and IDLScript.
These officially-
endorsed
language
mappings provide
source-code
portability of
applications
across different
CORBA products.
There are
unofficial
mappings for a
few other
languages, such
as Eiffel, Tcl and
Perl.
1.3 IDL Compilers
An IDL compiler
translates IDL
definitions (for
example, struct,
union, sequence
and so on) into
similar definitions
in a programming
language, such as
C++, Java, Ada or
Cobol. In addition,
for each IDL
interface, the IDL
compiler
generates both
stub code—also
called proxy types
—and skeleton
code. These
terms often cause
confusion, so I
explain them
below:
A dictionary
definition of stub
is “the short end
remaining after
something bigger
has been used
up, for example, a
pencil stub or a
cigarette stub”. In
traditional (non-
distributed)
programming, a
stub procedure is
a dummy
implementation
of a procedure
that is used to
prevent
“undefined label”
errors at link
time. In a
distributed
middleware
system like
CORBA, remote
calls are
implemented by
the client making
a local call upon a
stub
procedure/object.
The stub uses an
inter-process
communication
mechanism (such
as TCP/IP sockets)
to transmit the
request to a
server process
and receive back
the reply.
The term proxy is
often used
instead of stub. A
dictionary
definition of proxy
is “a person
authorized to act
for another”. A
CORBA proxy is
simply a client-
side object that
acts on behalf of
the “real” object
in a server
process. When
the client
application
invokes an
operation on a
proxy, the proxy
uses an inter-
process
communication
mechanism to
transmit the
request to the
“real” object in a
server process;
then the proxy
waits to receive
the reply and
passes back this
reply to the
application-level
code in the client.
The term
skeleton code
refers to the
server-side code
for reading
incoming
requests and
dispatching them
to application-
level objects. The
term skeleton
may seem like a
strange choice.
However, use of
the word skeleton
is not limited to
discussions about
bones; more
generally, it
means a
“supporting
infrastructure”.
Skeleton code is
so called because
it provides
supporting
infrastructure that
is required to
implement server
applications.
A CORBA product
must provide an
IDL compiler, but
the CORBA
specification does
not state what is
the name of the
compiler or what
command-line
options it accepts.
These details vary
from one CORBA
product to
another.
1.4 Making a
Remote Call
CORBA uses the
term servant to
refer to an object
written in a
programming
language (for
example, C++ or
Java) that
implements an
IDL interface. The
diagram below
shows what
happens when a
client application
invokes an
operation on an
object/servant in
a server process.
The application
code in the client
makes an
invocation upon a
local proxy object
(recall that the
proxy class is
generated by the
IDL compiler). The
proxy marshals
information about
the request—such
as the name of
the operation
being invoked,
and its in and
inout parameters
—into a binary
buffer. The proxy
object then
passes this binary
buffer to the ORB
library (provided
with the CORBA
product), which
transmits the
request message
across the
network to the
server process.
The ORB in the
client process
waits to read a
reply message
from the server
process. The ORB
returns the reply
buffer back to the
proxy object,
which unmarshals
inout and out
parameters and
the return value
(or a raised
exception), and
returns these to
the client
application code.
On the server
side, a thread
inside the ORB
sits in an event
loop, waiting for
incoming
requests. When a
request arrives,
the ORB reads the
request’s binary
buffer and passes
this to some code
that unmarshals
the parameters
and dispatches
the request to the
target servant.
The code that
performs the
unmarshalling
and dispatching is
spread over two
components. One
component is
called a POA, and
I will discuss that
later in this
article. The other
component is the
skeleton code
that was
generated by the
IDL compiler. The
generated
skeleton code is
not explicitly
shown in the
diagram because
it takes the form
of a base class
that is inherited
by the servant
class. When the
operation in the
servant returns,
the skeleton code
marshals the
inout and out
parameters (or a
raised exception)
into a binary
buffer and this is
returned via the
POA to the server-
side ORB, which
transmits the
reply message
across the
network to the
client process.
1.5 CORBA
Services
Many
programming
languages are
equipped with a
standardized
library of
functions and/or
classes that
complement the
core language.
These
standardized
libraries usually
provide collection
data-types (for
example, linked
lists, sets, hash
tables and so on),
file input-output
and other
functionality that
is useful for the
development of a
wide variety of
applications. If
you asked a
developer to write
an application in,
say, Java, C or C+
+ but without
making use of
that language’s
standard library,
then the
developer would
find it very
difficult.
A similar situation
exists for CORBA.
The core part of
CORBA (an
object-oriented
RPC mechanism
built with IDL and
common on-the-
wire protocols) is
of limited use by
itself—in the
same way that a
programming
language stripped
of its
standardized
library is of
limited use. What
greatly enhances
the power of
CORBA is a
standardized
collection of
services—called
CORBA Services—
that provide
functionality
useful for the
development of a
wide variety of
distributed
applications. The
CORBA Services
have APIs that are
defined in IDL. In
effect, you can
think of the
CORBA Services
as being like a
standardized
class library.
However, one
point to note is
that most CORBA
Services are
provided as
prebuilt server
applications
rather than as
libraries that are
linked into your
own application.
Because of this,
the CORBA
Services are
really a
distributed,
standardized
class library.
Some of the
commonly-used
CORBA Services
include the
following:
The Naming
Service and
Trading Service
allow a server
application to
advertise its
objects, thereby
making it easy for
client applications
to find the
objects.
Most CORBA
applications use
synchronous,
one-to-one
communication.
However, some
applications
require many-to-
many,
asynchronous
communication,
or what many
people call
publish and
subscribe
communication.
Various CORBA
Services have
been defined to
support this type
of
communication.
In a distributed
system, it is
sometimes
desirable for a
transaction to
span several
databases so that
when a
transaction is
committed, it is
guaranteed that
either all the
databases are
updated or none
are updated. The
Object
Transaction
Service (OTS)
provides this
capability.
2 Developing a
Simple Client-
server Application
I have used the
Orbacus
implementation
of COBRA to
develop a sample
client-server
application.
Orbacus is
available for both
Java and C++.
2.1 The Business
Logic Domain
My demonstration
server application
provides
encrypt() and
decrypt()
operations. The
client application
invokes encrypt()
on an object in
the server to
encrypt some
plain text. Later
the client invokes
decrypt() to
decrypt the text
again.
The
encryption/decryp
tion algorithm is
based on the
Caesar cipher
which is one of
the simplest
encryption
techniques. It is a
substitution
cipher in which
each letter in the
plain text is
replaced by a
letter that is a
fixed number of
positions down
the alphabet. I
added an XOR
operation with an
encryption key
after the shift
operation to
obscure the
relationship. You
can see the
substitution
technique in the
diagram below:

2.2 Writing the


IDL
The first step in
implementing the
client-server
application is to
write an IDL file
that defines the
public API of the
server. This
consists of one
interface that
provides
encrypt() and
decrypt()
operations. The
interface also
contains a
shutdown()
operation so we
can gracefully ask
the server to
terminate.
The IDL for the
demonstration
application is
shown below:
Collapse | Copy
Code
// Crypt.idl
interface
CaesarAlgorithm
{
typedef
sequence<char>
charsequence;
charsequence
encrypt(in string
info,in unsigned
long k,in unsigned
long shift);
string
decrypt(in
charsequence
info,in unsigned
long k,in unsigned
long shift);
boolean
shutdown();
};
2.3 Generating
the Skeleton and
the Stub
After writing the
IDL file, we
convert the IDL
definitions into
corresponding C+
+ definitions by
running the IDL
compiler supplied
with Orbacus.
Collapse | Copy
Code
idl crypt.idl
Running that
command
generates the
following files:
crypt.h and
crypt.cpp: These
files define and
implement the
C++ types
corresponding to
IDL types (such as
structs, unions,
sequences and so
on) defined in the
IDL file. These
files also
implement the
client-side proxy
classes
corresponding to
IDL interfaces.
crypt_skel.h and
crypt_skel.cpp:
These files define
and implement
the server-side
functionality
required to read
incoming
requests and
dispatch them to
servants (the C+
+ objects that
represent the
CORBA objects).
Note that the
CORBA
specification has
not standardized
the name of the
IDL compiler, and
different CORBA
implementations
may use different
names. For
example, the IDL
compilers in
Orbacus, Orbix
and omniORB are
called idl, while
TAO calls its IDL
compiler tao_idl,
and both
VisiBroker and
Orbit call theirs
idl2cpp.
2.4 Implementing
the Servant Class
The next step is
to implement the
servant class,
that is, a C++
class that
implements an
IDL interface. To
do this, we create
a class
(CryptographicIm
pl) that inherits
from the skeleton
class
(POA_CaesarAlgor
ithm) generated
by the IDL
compiler. You can
see this below.
Collapse | Copy
Code
#include
<iostream>
#include
<string>

#include
"OB/CORBA.h"
#include
"crypt_skel.h"

class
CryptographicImp
l : virtual
public
::POA_CaesarAlgo
rithm,

virtual
public
PortableServer::R
efCountServantBa
se

CORBA::ORB_var
orb; // Reference
to CORBA ORB

public:
CryptographicImp
l(CORBA::ORB_var
orb)
{

this->orb =
orb;
}

//
Caesar text
encryption
algorithm

virtual
::CaesarAlgorithm
::charsequence*

encrypt(const
char*
info,::CORBA::ULo
ng
k,::CORBA::ULong
shift)

throw(::CORBA::S
ystemException)
{

std::string
msg = info;

int len =
msg.length();

::CaesarAlg
orithm::charsequ
ence* outseq =

new
::CaesarAlgorithm
::charsequence;
outseq-
>length(len + 1);

std::string::iterato
r i = msg.begin();

std::string::iterato
r end =
msg.end();

int j = 0;

while (i !=
end)

*i+= shift;

*i
^= k;

(*outseq)[j++] =
*i++;

(*outseq)
[len] = '\0';

return
outseq;
}

//
Caesar text
decryption
algorithm

virtual
char*
decrypt(const
::CaesarAlgorithm
::charsequence&

info,::CORBA::ULo
ng
k,::CORBA::ULong
shift)

throw(::CORBA::S
ystemException)
{

char* r =
CORBA::string_all
oc(info.length());

for (int i =
0;i < info.length()
- 1;i++)

r[i]
= info[i];

r[i]
^= k;

r[i]
-= shift;

r[info.length() - 1]
= '\0';

return r;
}

//
Terminate CORBA
message

virtual
::CORBA::Boolean
shutdown()
throw(::CORBA::S
ystemException)
{

orb-
>shutdown(false)
;

return true;
}
};
The servant
implements the
operations
defined in the IDL
interface. When a
client makes a
remote call, the
dispatch code
(implemented in
the inherited
POA_CaesarAlgori
thm class)
unmarshals the
parameters of the
incoming request
and invokes the
operation.
2.5 Creating the
Server
Having
implemented the
servant class, we
must now
implement the
server mainline.
This performs
some CORBA
initialization
steps, creates the
servant,
advertises its
object reference
in the naming
service and then
enters an event
loop to wait for
incoming
requests. Before
showing the code,
there are two
more CORBA
concepts that I
need to explain:
POA and POA
Manager.
From reading this
article so far, you
might have the
impression that a
client invokes
operations on
remote objects,
and those remote
objects live in a
server process.
That is almost
correct. In reality,
the objects live in
a container called
a POA (the term
stands for
Portable Object
Adapter, but the
origin of the
name is not
particularly
interesting), and
the POAs, in turn,
live in a server
process. The
motivation for
this is that
different POAs
can provide
different qualities
of service (or
policies in CORBA
terminology). For
example, one
POA might be
single-threaded
while another
POA might be
multi-threaded.
The quality of
service provided
by a POA is
applied to the
servants within
that POA. So by
putting some
servants in one
POA and other
servants in
another POA, a
single server
process can host
objects with a
variety of
different qualities
of service: some
objects might be
single-threaded,
while others are
multi-threaded;
some objects
might be
transient
(meaning
temporary), while
other objects
might be
persistent
(meaning that a
client application
can continue
communicating
with the object
even if the server
processes crashes
and is restarted).
CORBA provides
an API that
enables server
developers to
create multiple
POAs, each with a
potentially
different quality
of service. The
CORBA runtime
system in a
server pre-
creates a
“rootPOA” that is
multi-threaded
and transient.
Since that quality
of service is
suitable for the
needs of our
demonstration
server, we do not
need to explicitly
create a POA.
A water tap (or
faucet as the
Americans call it)
is used for turning
on and off the
flow of water. A
POA manager is
similar to a water
tap except that it
controls the flow
of incoming
requests. When a
CORBA server
starts, the POA
manager
associated with
the root POA is in
a holding state,
which means if
any requests from
clients arrive then
they will be
queued up. This
holding state
enables a server
process to
complete its
initialization
without having to
worry about
processing
incoming
requests. When
the server has
completed its
initialization, it
puts its POA
manager(s) into
the active state,
so that incoming
requests can be
dispatched, via
POAs, to servants.
Collapse | Copy
Code
#include
<iostream>
#include
"OB/CORBA.h"
#include
<OB/Cosnaming.h
>
#include
"crypt.h"
#include
"cryptimpl.h"

using namespace
std;

int main(int argc,


char** argv)
{
// Declare
ORB and servant
object

CORBA::ORB_var
orb;

CryptographicImp
l* CrypImpl =
NULL;
try {
//
Initialize the ORB.
1 orb
=
CORBA::ORB_init(
argc, argv);
//
Get a reference to
the root POA
2

CORBA::Object_va
r rootPOAObj =

orb-
>resolve_initial_r
eferences("RootP
OA");
//
Narrow it to the
correct type

PortableServer::P
OA_var rootPOA =

PortableServer::P
OA::_narrow(rootP
OAObj.in());

//
Create POA
policies

CORBA::PolicyList
policies;

policies.length(1);

policies[0]
=
rootPOA-
>create_thread_p
olicy

(PortableServer::S
INGLE_THREAD_M
ODEL);

//
Get the POA
manager object

PortableServer::P
OAManager_var
manager =
rootPOA-
>the_POAManage
r();

3 //
Create a new POA
with specified
policies

PortableServer::P
OA_var myPOA =
rootPOA-
>create_POA

("myPOA",
manager,
policies);

//
Free policies

CORBA::ULong
len =
policies.length();
for
(CORBA::ULong i
= 0; i < len; i++)

policies[i]-
>destroy();

//
Get a reference to
the Naming
Service
root_context
4

CORBA::Object_va
r rootContextObj
=

orb-
>resolve_initial_r
eferences("Name
Service");
//
Narrow to the
correct type

CosNaming::Nami
ngContext_var nc
=

CosNaming::Nami
ngContext::_narro
w(rootContextObj.
in());

//
Create a
reference to the
servant
5
CrypImpl =
new
CryptographicImp
l(orb);
//
Activate object
PortableServer::O
bjectId_var
myObjID =

myPOA-
>activate_object(
CrypImpl);
//
Get a CORBA
reference with
the POA through
the servant
6

CORBA::Object_va
r o = myPOA-
>servant_to_refer
ence(CrypImpl);
//
The reference is
converted to a
character string

CORBA::String_va
r s = orb-
>object_to_string(
o);
7 cout
<< "The IOR of
the object is: "
<< s.in() <<
endl;

CosNaming::Nam
e name;

name.length(1);

name[0].id
= (const char *)
"CryptographicSe
rvice";

name[0].kind =
(const char *) "";
//
Bind the object
into the name
service
8 nc-
>rebind(name,o);

//
Activate the POA

manager-
>activate();
cout
<< "The server is
ready.

Awaiting
for incoming
requests..." <<
endl;
//
Start the ORB
9 orb-
>run();

}
catch(const
CORBA::Exception
& e) {
//
Handles CORBA
exceptions
cerr
<< e << endl;
}

// Decrement
reference count
if (CrypImpl)
10
CrypImpl-
>_remove_ref();

// End CORBA
if (!
CORBA::is_nil(orb)
){
try{
11 orb-
>destroy();
cout
<< "Ending
CORBA..." <<
endl;
} catch
(const
CORBA::Exception
& e)
{

cout << "orb-


>destroy()
failed:" << e <<
endl;

return 1;
}
}
return 0;
}
Initializes the
ORB.
Get the root POA.
Later on, we will
create a servant
object and
activate (that is,
insert) it into this
POA.
Access the root
POA’s POA
manager. Initially
this POA manager
is in the holding
state, so any
incoming
requests are
queued up. When
the server’s
initialization is
complete, we will
put this POA
manager into the
active state so
that incoming
requests can be
dispatched.
Obtain a
reference for the
naming service so
that after we
create a servant
we can export its
object reference
to the naming
service.
A
CryptographicImp
l servant object is
dynamically
created.
Calling
servant_to_refere
nce() provides us
with a object
reference for the
servant.
The reference is
converted to a
character string
in order to display
it.
The rebind()
operation is used
to register the
servant’s object
reference in the
naming service.
The ORB enters
an event loop, so
it can await
incoming
requests.
Servants are
reference
counted. When a
servant is
created, its
reference count is
initialized to 1.
Now that we are
finished with the
servant, we
decrement its
reference count
so the CORBA
runtime system
knows it can be
deleted safely.
Destroy the ORB.
2.6 Implementing
the Client
The client
application
performs some
CORBA
initialization
steps, retrieves
the server’s
object reference
from the naming
service and then
goes into a loop,
invoking encrypt()
and decrypt() on
it. When the
client is finished,
it invokes
shutdown() to ask
the server to
terminate.
Collapse | Copy
Code
#include
<iostream>
#include
<string>
#include
"OB/CORBA.h"
#include
"OB/Cosnaming.h
"
#include
"crypt.h"

using namespace
std;

int main(int argc,


char** argv)
{

// Declare
ORB

CORBA::ORB_var
orb;

try {

//
Initialize the ORB
1 orb
=
CORBA::ORB_init(
argc, argv);

//
Get a reference to
the Naming
Service
2

CORBA::Object_va
r rootContextObj
=

orb-
>resolve_initial_r
eferences("Name
Service");

CosNaming::Nami
ngContext_var nc
=
CosNaming::Nami
ngContext::_narro
w(rootContextObj.
in());

CosNaming::Nam
e name;

name.length(1);

name[0].id
= (const char *)
"CryptographicSe
rvice";

name[0].kind =
(const char *) "";
//
Invoke the root
context to
retrieve the
object reference
3

CORBA::Object_va
r managerObj =
nc-
>resolve(name);
//
Narrow the
previous object to
obtain the correct
type
::Cae
sarAlgorithm_var
manager =
4
::CaesarAlg
orithm::_narrow(
managerObj.in());

string
info_in,exit,dumm
y;

CORBA::String_va
r info_out;
::Cae
sarAlgorithm::cha
rsequence_var
inseq;

unsigned
long key,shift;

try{

do{

cout
<<
"\nCryptographic
service client" <<
endl;

cout
<<
"------------------------
----" << endl;

do{ // Get
the cryptographic
key

if (cin.fail())

cin.clear();

cin
>> dummy;

cout <<
"Enter encryption
key: ";

cin >> key;

}
while (cin.fail());

do{ // Get
the shift

if (cin.fail())

cin.clear();

cin
>> dummy;

cout <<
"Enter a shift: ";

cin >>
shift;
}
while (cin.fail());

//
Used for debug
pourposes

//key
= 9876453;

//shif
t = 938372;

getline(cin,dumm
y); // Get the text
to encrypt

cout
<< "Enter a plain
text to encrypt: ";

getline(cin,info_in
);

//
Invoke first
remote method
5

inseq =
manager-
>encrypt

(info_in.c_str(),ke
y,shift);

cout
<<
"------------------------
------------------"

<< endl;

cout
<< "Encrypted
text is: "

<< inseq-
>get_buffer() <<
endl;

//
Invoke second
remote method
6

info_out =
manager-
>decrypt(inseq.in
(),key,shift);

cout
<< "Decrypted
text is: "

<<
info_out.in() <<
endl;

cout
<<
"------------------------
-------------------"

<< endl;

cout
<< "Exit? (y/n): ";

cin
>> exit;
} while
(exit!="y");

//
Shutdown server
message
7
manager-
>shutdown();

}
catch(const
std::exception&
std_e){

cerr <<
std_e.what() <<
endl;
}
}catch(con
st
CORBA::Exception
& e) {
//
Handles CORBA
exceptions
cerr
<< e << endl;
}
// End CORBA
if (!
CORBA::is_nil(orb)
){
try{

orb-
>destroy();

cout <<
"Ending CORBA..."
<< endl;
}
catch(const
CORBA::Exception
& e)
{

cout <<
"orb->destroy
failed:" << e <<
endl;

return 1;
}
}
return 0;
}
Initialize the ORB.
Obtain a
reference for the
naming service.
Call resolve()
(which means
lookup) on the
naming service to
retrieve the
server’s object
reference.
The narrow()
operation is, in
essence, a
typecast. We
have to narrow()
the object
reference
received from the
naming service
into the correct
subtype so we
can invoke
operations on it.
Make a remote
invocation of the
encrypt()
operation.
Make a remote
invocation of the
decrypt()
operation.
Make a remote
invocation of the
shutdown()
operation.
2.7 Running the
Client-server
Application
Once we have
implemented the
client and the
server, it’s time
to connect them.
Because our
demonstration
client and server
exchange object
references via the
naming service,
we must ensure
that the naming
service (which is
called nameserv
in Orbacus) is
running. We use
some command-
line options to tell
the naming
service the host
and port on which
it should listen.
Collapse | Copy
Code
nameserv
-OAhost localhost
-OAport 8140
After this, we can
start the server
with a command-
line option to tell
it how to contact
the naming
service.
Collapse | Copy
Code
server
-ORBInitRef
NameService=cor
baloc:iiop:localho
st:8140/NameSer
vice

Finally we can
start the client,
again with a
command-line
option to tell it
how to contact
the naming
service.
Collapse | Copy
Code
client -ORBInitRef
NameService=cor
baloc:iiop:localho
st:8140/NameSer
vice

3 CORBA Benefits
and Drawbacks
CORBA offers
several benefits.
First,
implementations
of CORBA are
available for
many
programming
languages (such
as C, C++, Java,
COBOL, Ada,
SmallTalk and
Python, Tcl and
Perl) and many
operating
systems
(including
Windows, UNIX,
mainframes and
embedded
devices). Not all
client-server
technologies can
claim this. For
example,
Microsoft
technologies
(COM, DCOM
and .NET) have
traditionally been
available only on
Windows. Java
RMI works across
multiple
operating
systems, but it
can be used only
in Java-based
applications.
Second, when
making a remote
call, CORBA
marshals
parameters into a
compact binary
format for
transmission
across the
network. This
compact format
saves on network
bandwidth. In
addition, the CPU
overhead
required to
marshal
parameters into
the binary format
is also quite low.
In contrast, some
newer client-
server
technologies,
such as SOAP,
marshal
parameters into
XML format when
making remote
calls. XML is very
verbose so it uses
a lot of
bandwidth;
usually at least
ten times more
bandwidth than
an equivalent
CORBA call. In
addition, when a
SOAP-based
server receives a
request, it must
parse the XML to
extract the
parameter data.
Doing this
requires a lot of
CPU time. In
contrast,
parameters can
be marshaled in
and out of
CORBA’s binary
message format
much more
quickly.
Third, CORBA has
been around for
well over a
decade, which
means that its
technology is
quite stable and
feature rich.
Of course, CORBA
is not perfect. Its
main drawback is
that the power
and flexibility of
CORBA comes
with a relatively
steep learning
curve. An open-
source library
called the CORBA
Utilities eases
some, but not all,
of the learning
curve for C++
and Java
developers.
Bottom of Form

You might also like