(Ebook-Pdf) - Corba Programming Unleashed
(Ebook-Pdf) - Corba Programming Unleashed
Introduction
Appendix A
Appendix B
Appendix C
Index
Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
Table of Contents
-----------
Introduction
This book, in many ways, represents everything that I have used from CORBA
implementations in my work as a consultant. I started writing the book in the
firm confidence that CORBA is a known quantity, which could hold no
surprises. To my amazement, I discovered that CORBA held many delightful
challenges that still required me to shake off the sense of weariness that had
grown on me over years of work with CORBA, both professionally and
personally.
The most challenging aspect of CORBA is its promise of interoperability. In
my capacity as consultant, I have had to rely on IIOP a number of times.
Nowadays, I have come to take it for granted. The other interesting aspect of
CORBA was the introduction I have had to dealing with the many interesting
ways in which the vendors of CORBA have extended the specification. Some
of you might think that such a situation is not an ideal one—especially when it
comes to choosing an ORB for your application. But you will find that the
CORBA vendors have managed to extend CORBA without breaking
compliance to the core specification. Surely, that is a giant step toward
heterogeneous interoperability of off-the-shelf CORBA implementations.
This book is a thorough exposition of the current implementation of the
CORBA standard. It covers the many areas of CORBA that troubled me as a
consultant and led to many a mistake that sometimes proved costly. I hope,
through this volume, you will know more than I did when I started. CORBA
Programming Unleashed provides information about the following topics.
They may be read in any order you like, and they bear no real
interdependencies.
• Chapter 1, “CORBA 2.0: An Architecture for Interoperability”
• Chapter 2, “IIOP and E-Commerce”
These two chapters are really the heart of the book. They demonstrate the twin
promise of CORBA interoperability and the potential of IIOP as a protocol for
e-commerce.
• Chapter 3, “The Portable Object Adapter”
The Portable Object Adapter is a new addition to the CORBA standard. It
promises to bring a new level of abstraction to CORBA.
• Chapter 4, “CORBA Object References and Smart Pointers”
• Chapter 6, “CORBA C++ Memory Management”
These two chapters deal with the arcane issues of using C++ as a language for
CORBA implementations. One of the topics that caused me the most grief
when I began using CORBA was how to use C++ effectively.
• Chapter 5, “Java/IDL Mapping”
Java is fast becoming a standard language and platform for implementing
CORBA applications. This chapter deals extensively with how to use Java
with CORBA/IDL.
• Chapter 7, “The Naming Service”
• Chapter 8, “The Trader Service”
• Chapter 9, “The Event Service”
These are what I consider to be the core services that CORBA has to offer.
These chapters cover the API and how to effectively use these services in your
applications.
• Chapter 10, “The Transaction Service”
With the explosive emergence of the World Wide Web and the potential for
exciting opportunities for e-commerce–related businesses, the CORBA
Transaction Service is sure to become the cornerstone of many innovative
Web applications. This chapter teaches you how to effectively and correctly
use CORBA Transactions.
• Chapter 11, “The Security Service”
In a connected world, security is sure to be the one critical component that can
make or break an application. The CORBA security specification is one of the
most in-depth and complete specifications on distributed security this side of
DCE.
• Chapter 24, “CORBA Interface Repository”
• Chapter 25, “CORBA Dynamic Invocation Interfaces”
As applications become more complex while offering new challenges to
integration, the CORBA Interface Repository and Dynamic Invocation
Interfaces are going to be key components that will enable “the web of
objects” to expand the way the Internet did.
• Chapter 12, “CORBA Server Activation Modes”
• Chapter 14, “CORBA and Threads”
• Chapter 16, “Distributed Callbacks”
These three chapters deal with the important issue of server-side scalability.
CORBA enables very fine-grained control over scalability issues. These
chapters introduce the mechanisms of this control.
• Chapter 13, “Orbix Filters”
• Chapter 15, “Orbix Dynamic Loaders”
These two chapters deal with how IONA has extended CORBA. These two
services can widen your options in terms of how CORBA can be further
integrated into existing architectures and requirements.
• Chapter 17, “VisiBroker Caffeine”
• Chapter 18, “VisiBroker SmartStubs”
• Chapter 19, “Handling Distributed Events”
These chapters deal with VisiBroker’s contribution to adding commercial
depth to the CORBA standard. In all, I am sure that many of the interesting
mechanisms that the commercial vendors have introduced into CORBA will be
incorporated into the CORBA specification. Orbix Dynamic Loaders and
VisiBroker SmartStubs are sure to be two that will eventually be added to the
CORBA specifications.
• Chapter 21, “CORBA and Java Servlets”
• Chapter 22, “CORBA and Mobile Agents”
• Chapter 26, “Developing CORBABean Wrappers”
These three chapters deal with issues that enable you to integrate the power of
CORBA with the flexibility of Java. If an effective distributed-object solution
of the Web needs to be constructed with the simplicity of HTML, Java servlets
and CORBA will provide the way. Mobile agents, on the other hand, are
probably the next evolutionary step in client/server technology; Chapter 22
explores how the technologies can be integrated.
• Chapter 23, “CORBA and Design Patterns”
Over the last couple of years, I have realized that designing distributed systems
has many fundamental differences from building desktop applications.
Building distributed systems is not just about having objects interacting over
networks. Nevertheless, it was heartening to see that the “Gang Of Four”
patterns and others still hold true for distributed systems.
• Appendix A, “Annotated ORB Interface API”
• Appendix B, “TIE and BOA”
• Appendix C, “Orbix and MFC”
Appendix A provides you with a reference to the ORB interface API, and
Appendix B is a guide to using TIE and the Basic Object Adapter. Appendix C
tells you how to use MFC effectively with CORBA.
So, there you have it. Pretty much everything I want to know about
CORBA—until the next incarnation of the standard, that is. I wish you luck.
Good hunting.
Suhail M. Ahmed
Amsterdam, 1998
Table of Contents
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
Table of Contents
-----------
Note:
If Windows 95 is installed on your computer and you have the AutoPlay
feature enabled, the START.EXE program starts automatically whenever
you insert the disc into your CD-ROM drive.
where [options] are the options for your version of UNIX and
<mountpoint> is the typical mountpoint for your machine.
3. View the Readme file in the root directory of the CD-ROM for
installation instructions for the evaluation software products included.
Note:
The mountpoint must exist before you try the mount command.
Note:
This CD-ROM uses long and mixed-case filenames requiring the use of a
protected-mode CD-ROM driver.
Use of this software is subject to the Binary Code License terms and
conditions on pages 575-578. Read the license carefully. By opening this
package, you are agreeing to be bound by the terms and conditions of this
license from Sun Microsystems, Inc.
Table of Contents
Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
Table of Contents
-----------
Contributing Authors
Larry Martinez is a Senior Technical Specialist with BSI Consulting in
Houston, Texas, where he develops distributed client/server systems using
Java, C++, and CORBA. He has developed systems under both UNIX and
Windows NT systems for a variety of industries, including NASA’s space
shuttle program, mutual funds, oil exploration, and natural gas. Larry holds a
bachelor’s degree in Mechanical Engineering from the University of Texas at
Austin, and is an active member of the Houston Java User’s Group. He
occasionally heads to the Colorado Rockies where he enjoys backpacking,
snowboarding, and fishing.
Charles Pace has over 14 years of experience in all kinds of progressive,
cutting-edge software development. He has written a vast array of computer
programs, from interactive educational games to large enterprise systems.
Charles looks forward to enabling developers to deliver applications utilizing
the next generation of programming technology.
Dedication
For Aaron
May your future be as kind to you as your past has been to your father.
For Hilde
For being there.
For My Parents
For your faith in me.
Acknowledgments
Innumerable people have influenced this book. Most of the things I learned
and came to know are from countless hours of discussions with the people I
have had the privilege of working with as a consultant and teacher. But this
book would not have been possible without the few good people who
somehow crossed my life at the right moment in time. I owe a great deal to
Tracy Dunkelberger and Katie Purdum for giving me the chance to write this
book. Craig Read was kind enough, in his own way, to make time available for
me from my busy schedule. His command of English and history came in
handy when he offered his criticisms. I would also like to extend my sincere
thanks to Wendy Devolder, who has been a kind friend and brought this
opportunity to my attention. Finally, I want to thank Christophe Bon, who read
every page, and no matter how scattered the content was at times, he always
told me that it was great! Thank you all.
Table of Contents
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Part I
The Core Architecture
In This Part
• CORBA 2.0: An Architecture for Interoperability
• IIOP and E-Commerce
• The Portable Object Adapter
Chapter 1
CORBA 2.0: An Architecture for
Interoperability
In This Chapter
• More Than One ORB?
• Architecture and Protocols
• Anatomy of an IOR
• The OrbixIORTuner Implementation
CORBA 2.0 specifies a technical framework that allows applications that use
different protocols to interoperate. In addition to this, CORBA 2.0 also
specifies the means by which protocols such as DCE RPC can interoperate
with native CORBA protocols. Although this chapter concentrates on the
details of how to achieve interoperability between different CORBA 2.0
implementations, it is instructive to understand the overall architecture for
interoperability as defined by CORBA 2.0.
CORBA 2.0 differs from the earlier version in the nature of what the
specification defined as interoperability. CORBA 1.0 allowed ORBs from the
same vendor to interoperate across multiple platforms. This meant that if you
wanted to run CORBA over multiple platforms, you selected an ORB vendor
that supported those platforms.
With CORBA 2.0, you were given the freedom to choose any vendor that
supported CORBA on the platform of your choice. The specifications
guaranteed that applications running on multiple platforms would work
together.
The specification defines four broad areas of interoperability. At its highest
level, the standard defines an overall architecture for interoperability. The
architecture defines the ideas used to describe interoperability—the
vocabulary, so to speak. Within the context of this architecture, CORBA 2.0
specifies a mechanism known as bridging to allow multiple ORBs to
communicate and collaborate with each other.
There are a number of reasons why OMG decreed interoperability in the first
place. Here are some of the more important reasons:
• Large organizations have a tendency to define applications and
services in terms of domains.
• Division in terms of domains results in complex and heterogeneous
environments with many different types of platforms and software.
• Diversity of solutions leads to solutions that are optimal for a given
problem, but it also introduces tight constraints on how different
solutions could coexist.
• Organizations need to have full freedom to choose whatever they like
for their domains, but also have the freedom to select solutions that
would have the least impact on their existing infrastructure. This path
leads to having different ORBs in different domains—hence the need for
interoperability.
CORBA 2.0 accepts this reality and tries to define an architecture that enables
you to define these domains in terms of the roles they play. In a typical
organization, you could differentiate them broadly into business domains and
technical domains. The technical domains are usually generic enough to be
reused by any business domain. Such services include transactions, security,
component repositories, and so on.
The GIOP specifies a set of standard messages that can be used to ensure
interoperability between ORBs. As such, GIOP is the core of the ORB
interoperability architecture.
The GIOP is a simple protocol that facilitates interoperability between
different ORBS. It is a general protocol that can work over any minimalist
connection-oriented transport protocol. It is a very efficient protocol and
designed to be as compact as possible. GIOP messages enable object
references to change location during runtime. The protocol is designed so that
if a change of location does occur, it does so quite transparently for the client.
GIOP-based protocols make a number of assumptions, the most important of
which is that GIOP will use a connection-oriented transport mechanism. On
top of this, GIOP assumes that this transport will be reliable. Reliability
ensures that if a connection is lost due to whatever reasons, the underlying
transport will offer some mechanism for notifying that some fault has
occurred.
With the CORBA 1.0 implementation, each ORB vendor had its own
proprietary protocol to encapsulate invocations across clients and servers.
Within the new standard, OMG has made GIOP implementations mandatory.
More specifically, OMG specifies the Internet Inter-ORB Protocol (IIOP) as
the means to allow an interoperable heterogeneous ORB environment. IIOP is
a TCP-based implementation of GIOP. This means that IIOP uses TCP as its
transport protocol. It is possible to implement GIOP over other transport
protocols such as IPX/SPX, NetBEUI as well.
Previous Table of Contents Next
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Environment-Specific Inter-ORB Protocol
Anatomy of an IOR
You can create IORs from object references. Such references are known as
stringified object references. These references can be passed to a client that
requires the services of a server component. At the client side, these stringified
object references can be converted back to object references, and operations
can be invoked on them.
The Application Programming Interface (API) required for these operations is
found in the ORB class. Later on, you’ll have a detailed look at the protocols
offered by the ORB class. For the current discussions, two operations,
ORB::object_to_string and ORB::string_to_object are the
necessary APIs needed to accomplish the conversion of an object reference to
an IOR and an IOR back to an object reference, respectively.
Interoperability in this context is rather simple. Object references running on
different ORBs can be converted into stringified references and passed
between each other. We will be using this mechanism to illustrate
interoperability.
Before we can do that we have to solve the chicken-and-egg problem!
Warning:
Ensure that the ORB you are considering in your architecture is 100 percent
CORBA 2.0–compliant. The level of interoperability varies from vendor to
vendor. Although most CORBA manuals talk about “out-of-the-box”
interoperability, some vendors stretch that definition a little too thin.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Before you specify the interfaces for IORR, you also have to think about how you are going to allow a
heterogeneous CORBA environment to use this service. One primary requirement is that a component
anywhere should be able to access the IORR’s IOR. The trick is to use some unglamorous way to
accomplish this. Consider the following possibilities:
• You could stream the IOR to some persistent state. This solution is a good one at first sight.
Perhaps you could use something like a small object-oriented database, such as Objectstore PSE, to
achieve this. Even traditional persistent stores could be used. The benefit of this is that the search
capability of the database could be used to search for IORs.
• You could stream the IOR to a networked file system. This is a simple solution. It is also
efficient, because there is very little overhead in streaming to the file system and then retrieving the
IOR. Of course, the trade-off you have to make is that you would have to engineer all the search
and sort algorithms. Because you live in the age of reuse, however, there are plenty of freely
available libraries that do these efficiently. You could use the C++ Standard Template Libraries or
the Java Collections that are part of Java Development Kit 1.2, which is also available to JDK 1.1.
You have to consider that the IORs should be on some type of networked file systems or at least an
environment that could provide you with an open means of networked distribution.
• If interoperability is to be achieved on a single machine, a simple Clipboard-based transfer is
sufficient. This is an obtuse way of saying, “use cut and paste”!
We are going to try something novel. As a consultant who works with every manner of distributed
middleware, I have to keep my ears to the ground, so to speak, for new ways of distributing information.
One of the technologies I came across recently is called iBus.
iBus will easily allow us to transmit the IORRegistry’s IOR over IP multicast. With Java, the solution is
trivial, because the requisite network libraries for UDP-based protocols are built into the JDK. Because
iBus is a pure Java implementation, we could easily integrate this solution with Java-based CORBA
applications. On the other hand, with C++ it is a little more involved. Trying to implement IP multicast
using C++ would be unnecessarily complex, so we shall make a radical design compromise.
With these choices in mind, let’s now define the IDL for the IORegistry:
IORR.idl
module iorr{
interface registry {
exception ObjectAlreadyRegisted { string reason; };
exception ObjectNotRegisterd { string reason; };
exception GenralException { string reason; };
struct entry {
string object_name;
string server_name;
string ior;
};
typedef entry registryEntry;
typedef sequence<entry> directory;
void register(in registryEntry t_entry) raises
(ObjectAlreadyRegisted);
void deregister(in registryEntry t_entry) raises
(ObjectNotRegisterd);
void fetch(inout registryEntry t_entry) raise
(ObjectNotRegisterd);
directory fetch_all() raises (GenralException);
};
};
The struct entry is the object that will make up the entries in the registry. These entries, of course,
have to be held in an array, which is what the following line does:
The Java Generic Library can be downloaded from www.odi.com. iBus can be obtained from
http://www.softwired.ch.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Compiling the IDL
Run the VisiBroker IDL pre-compiler on the ior.idl with the following line:
package iorr;
import com.objectspace.jgl.*;
import java.util.Enumeration;
//A Java generic library Hashset to hold all the Registry entries
private HashSet iortable = null;
Enumeration en = iortable.elements();
boolean found_it = false;
while(en.hasMoreElements()){
x_entry = (iorr.registryPackage.entry) en.nextElement();
if(x_entry.server_name.equals(y_entry.server_name) &&
x_entry.object_name.equals(y_entry.object_name) )
found_it = true;
}
return found_it;
}
public _example_registry() {
super(“IORR”);
iortable = new HashSet(false);
}
try{
iortable.add(t_entry);
System.out.println(“IOR From the Client: “ + t_entry.ior);
}
catch(NullPointerException e){
throw new iorr.registryPackage.ObjectAlreadyRegisted(e.toString());
}
}
iorr.registryPackage.entryHolder _entry =
(iorr.registryPackage.entryHolder) e.nextElement();
if(_entry.value.object_name == t_entry.value.object_name
&& _entry.value.server_name == t_entry.value.server_name){
t_entry.value.ior = _entry.value.ior;
found = true;
}
}
if(!found)
throw new iorr.registryPackage.ObjectNotRegisterd(“Object not
found”);
}
Enumeration e = iortable.elements();
iorr.registryPackage.entry[] table =
new iorr.registryPackage.entry[count];
for(int i = 0; i < count; i++){
table[i] = (iorr.registryPackage.entry)e.nextElement();
}
return table;
}
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
These are the details of the IORRegistry interface’s implementation:
• The Registry entries coming in from clients are to be held in the Java Generic Library (JGL) Hashset.
The JGL Hashset stores elements by their hash code by invoking the hashcode() on them. The JGL
is an extremely efficient set of Java container classes based on the C++ Standard Template Library.
• The client can invoke register() on the IORRegistry by passing an instance of the following:
iorr.registryPackage.entry t_entry
On receipt of the entry instance, the implementation adds the entry into the Hashset.
• The following method verifies whether the entry exists in the Registry:
iorr.registryPackage.entryHolder _entry =
(iorr.registryPackage.entryHolder) e.nextElement();
if(_entry.value.object_name == t_entry.value.object_name
&& _entry.value.server_name == t_entry.value.server_name){
t_entry.value.ior = _entry.value.ior;
found = true;
}
}
if(!found)
throw new iorr.registryPackage.ObjectNotRegisterd(“Object not
found”);
}
The object reference’s name has to be unique within a server. If this constraint is violated, the exception
iorr.registryPackage.ObjectAlreadyRegisted
The parameter passed into the server was tagged as inout. This means that the parameter is passed
from the server, and the server can operate on it and return it. In Java, an inout parameter maps to a
holder class. The implementation is rather simple, because it involves iterating over an enumeration of
the elements of the Hashset until a match is found and then setting the requested IOR in the holder class.
• As it should be clear from the interface for the iorr.registryPackage.entry, a client is
expected to know the name of the server and object before it can request an IOR. In case these are not
known, the client can request all the entries. This means that the client needs to figure out which IOR it
wants.
We now have to design a solution to allow potential clients to discover the register’s IOR so that it can use its
services.
The design of the IORRegistry is such that any client that needs to use IOR for interoperability needs to find
only the initial IOR for the registry. A good solution that solves this problem would be to decouple the location
of where this IOR was stored from the potential client. Otherwise, if the location were to change, the client
would have to be reinitialized with the new settings.
With Java, such changes could easily be handled with property files. Java property files can be read in by an
application, and these files could contain all the necessary parameters to initialize the connection to the
IORRegistry. If such a solution could be found, the system has a good chance to be flexible and scalable. One
such technology is iBus. In fact, it would be a good solution in some cases to have all servers multicast their
IORs over predefined channels. This would result in an architecture that has no need for a centralized naming
service.
Using iBus means that any client that has implemented the required callback interface could “listen” to the
IOR for the registry being transmitted over the network. Our implementation of iBus uses IP multicast as the
transmission protocol. (Information about iBus can be otained at the Softwired Web site.) This does mean the
quality of service is rather poor, because the packets have no guaranteed delivery. But this is more efficient in
terms of conserving your network’s bandwidth.
The SOAR.infrastructure.transmitter defines two classes and an interface. The class
iortransmitter.java defines and implements the requirements for an iBus channel. The
implementation is rather simple, because the only thing this class needs to do is transmit the IORRegistry
server. The IORReceiver.java contains the implementation for the iBus channel receiver functionality.
Any client that needs to “tune” into the IORRegistry server’s channel can use this class to do so. The only
constraint is that such a client implements the receiver interface defined in receiver.java. Listing 1.2
shows the implementation of the IORRegistry IOR transmitter.
Listing 1.2 The IORRegistry Transmitter
package SOAR.infrastructure.transmitter;
import iBus.exception.*;
import iBus.*;
import java.util.*;
import java.net.*;
ior_to_transmit = t_ior;
try{
t_url = new iBusURL (“ibus://226.0.0.1/soar/iorrt”);
}
catch(java.net.MalformedURLException e){
System.out.println(e.toString());
}
for(;;){
try{
t_stack.push(t_url, t_posting);
System.out.println(“tick..”);
Thread.currentThread().sleep(10000);
}
catch(Exception e){
System.out.println(e.toString());
}
}
}
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
This implementation is a very simple adaptation of the iBus “hello world” channel application. As
stated earlier, we are going to use IP Multicast to “push” our IOR around the network.
The IORTransmitter class can be used by any CORBA object that needs to transmit its IOR
around. Any class that wishes to do so could launch the IORTransmitter on a thread so that it
can work in the background and not interfere in the normal workings of the CORBA
implementation:
• When instantiating the IORTransmitter class, the IOR of the source object can be
passed into the class. This is the IOR that is transmitted.
• With iBus, you can use extended URL objects known as iBus URLs to construct a channel.
You do so at
Depending on your IP address, you would have to change the address that you put into this
URL.
• You initialize the protocol stack for IP Multicast. iBus offers a number of protocols,
including Reliable and even Reliable IP Multicast. Let’s choose IPMCAST to conserve the
network bandwidth.
• You initialize the posting object, which is to contain the IOR string object. In fact, iBus
posting objects can contain any valid Java object (!). Here is the relevant code:
t_posting.setObject(0, ior_to_transmit);
• After you have done all this, you register the talker for the chosen iBus URL. The talker is
the object that contains the logic that transmits the information over the channel. This is
accomplished in the following line of code:
t_stack.registerTalker(t_url);
• Finally, a client class on invoking start() on the IORTransmitter launches an
infinite for loop that transmits the posting every ten seconds.
Now that we have the implementation of the IORRegistry and the iBus transmitter for pushing the
IOR for the registry server, we need to implement a Visigenic server to host the IORRegistry
object, as shown in Listing 1.3.
Listing 1.3 Implementing a Server Host
package iorr;
import SOAR.transmt.*;
import org.omg.CORBA.*;
import java.io.*;
try{
File outputFile = new File(“ior.dat”);
FileWriter out = new FileWriter(outputFile);
catch(IOException ioe){
System.out.println(ioe.toString());
}
t_boa.obj_is_ready(t_registry);
System.out.println(t_registry + “ is ready.”);
System.out.println(“IOR is “ + t_registry_ior);
t_boa.impl_is_ready();
orb.disconnect(registry);
}
}
As I said earlier, most CORBA server implementations are relatively standard. In fact, some ORBs
do generate a standard server file that can be used without changes with a wide variety of client
implementations. But our server implementation does contain a few changes relating to the steps
required to set up the IORTransmitter object:
• After instantiating an instance of the registry interface, you invoke the call
object_to_string on the ORB handle. This call returns the IOR, which is a stringified
object reference of the registry.
• After you have returned the IOR to the registry, you can now instantiate an instance of the
IORTransmitter. The constructor of the IORTransmitter requires a string parameter
referring to the IOR. The IOR string is passed into the IORTransmitter to instantiate it.
• After instantiating the transmitter, the start method is called on the object to have the
IORTransmitter run on a separate thread from the main one.
• The main thread blocks, waiting for incoming requests, in this line:
orb.disconnect(registry);
Setting the IORTransmitter on a separate thread now makes a lot more sense. The server
waiting for messages and the transmitter multicasting messages make excellent candidates for
threading.
We have implemented the IORRegistry and the IORTransmitter. You can now run the
application to see whether everything functions as intended. Of course, it won’t seem to do much,
because the only thing that is happening is hidden from your view: On one thread, the blocked BOA
call is waiting for any request, and on the other thread, the IORTransmitter multicasts the
server’s IOR every ten seconds. We now need to build some ears!
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The IORRegistry Client-Side Implementation
There are some important considerations you have to take into account before constructing a
listener. The most important thing is that no matter which client uses a listener, the services that
have been implemented cannot change. This means that no matter what type the client is, the
implementation of the IORRegistry server or the IORTransmitter cannot change. The
other consideration is that we should have the ability to just reuse the listener without change;
after all, this is what object orientation is all about isn’t it? This is a valid consideration,
because any real-world implementation of such a system would consist of any number of
IORReceiver clients running. The iBus listener should not have to know all the clients that
are using its services. It should be a simple matter of any client requiring the services of a
listener to simply “plug in” the receiver and receive transmissions.
We can accomplish this constraint on our design by specifying a generic interface that must be
implemented by any client that wishes to be notified on reception of an IOR. We shall specify
such an interface, which we call the IORReceiver interface:
}
The IORReceiver interface specifies only a single operation: set_IOR(String
t_ior). This method needs to be implemented by any client that requires notification by the
IORReceiver object.
The IORReceiver is the ear that listens to a specific iBus channel for reception of the IOR
string. Most of the constraints that were imposed on the transmitter also apply to the receiver.
For instance, the receiver has to execute on a thread, the receiver has to be shut down when the
Registry IOR has been received, and so on. Listing 1.4 shows the implementation of the
IORReceiver class.
Listing 1.4 The IORReceiver Class
package SOAR.infrastructure.receiver;
import iBus.exception.*;
import iBus.*;
import java.util.*;
import java.net.*;
import iorr.*;
try{
t_url = new iBusURL (“ibus://226.0.0.1/soar/iorrt”);
}
catch(MalformedURLException e){
System.err.println(e.toString());
}
try{
t_stack.subscribe(t_url, t_tuner);
}
catch(iBus.exception.CommException e){
System.err.println(e.toString());
}
catch(iBus.exception.AlreadySubscribed e){
System.err.println(e.toString());
}
System.err.println(Application.getAppName() + “ is ready.”);
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The IORRegistry receiver contains two classes: the IORReceiver and the
IORTuner. Think of the IORReceiver class as the radio and the IORTuner class
as the component that actually receives the signals. Let’s look at the internals of the
two classes:
• The IORReceiver is a subclass of the java.util.Thread class. This
allows you to run the receiver on a thread. By design, any class that wants to
receive IORs over the iBus can use this class by aggregation and simply “start
up” the thread.
• iBus requires that you initialize the application. It is not really necessary, but
if you want to pass Quality of Service information to iBus, you can do that as
parameters to this method.
• In the run method, an instance of the IORTuner is instantiated. The
IORTuner is required to be a concrete implementation of iBus.Receiver.
Let’s look a little closer at this class:
The IORTuner accepts any class that implements the receiver interface. As
mentioned earlier, this interface allows the Tuner to be independent of any
client using its services by simply invoking set_IOR(t_ior).
The following method:
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Listing 1.5 shows the implementation of the OrbixIORTuner.
Listing 1.5 OrbixIORTuner
Package Iorr;
import org.omg.CORBA.*;
import SOAR.receiver.*;
import java.util.*;
public OrbixIORTuner(){
_orb = ORB.init();
System.out.println(“ORB Client initializing..”);
}
while(t_registry_ior == null){
System.out.println(“waiting..”);
try{
Thread.currentThread().sleep(10000);
}
catch(InterruptedException e){
System.out.println(e.toString());
}
}
t_receiver.logout();
t_receiver.stop();
t_receiver = null;
System.out.println(“Registry IOR is “ + t_registry_ior );
this.continueProcess();
}
try{
org.omg.CORBA.Object _temp =_orb.string_to_object(t_registry_ior);
t_registry = registryHelper.narrow(_temp);
}
catch(SystemException e){
System.out.println(e.toString());
}
iorr.registryPackage.entry t_entry =
new iorr.registryPackage.entry();
t_entry.server_name = “VisibrokerServer”;
t_entry.object_name = “SomeObject”;
t_entry.ior = t_registry_ior;
r_entry.value.server_name = t_entry.server_name;
r_entry.value.object_name = t_entry.object_name;
r_entry.value.ior = “”;
try{
t_registry.register(t_entry);
}
catch(iorr.registryPackage.ObjectAlreadyRegisted e){
System.out.println(“ObjectAlreadyRegistered..”);
System.out.println(e.toString());
}
try{
t_registry.fetch(r_entry);
}
catch(iorr.registryPackage.ObjectNotRegisterd e){
System.out.println(“ObjectNotRegisterd..”);
System.out.println(e.toString());
}
try{
org.omg.CORBA.Object _temp =_orb.string_to_object(t_registry_ior);
t_registry = registryHelper.narrow(_temp);
}
catch(SystemException e){
System.out.println(e.toString());
}
try{
t_registry.register(t_entry);
}
catch(iorr.registryPackage.ObjectAlreadyRegisted e){
System.out.println(“ObjectAlreadyRegistered..”);
System.out.println(e.toString());
}
try{
t_allentries = t_registry.fetch_all( );
}
catch(iorr.registryPackage.GenralException e){
System.out.println(“GeneralException..”);
System.out.println(e.toString());
}
try{
t_registry.deregister(t_entry);
}
catch(iorr.registryPackage.ObjectNotRegisterd e){
System.out.println(“ObjectNotRegisterd..”);
System.out.println(e.toString());
}
}
}
The OrbixIORTuner is a thread class. It also implements the iorr.receiver interface. This means that
an instance of this class can be passed into the constructor of the IORTuner class:
• The constructor for OrbixIORTuner initializes the ORB. Because this class is a thread, everything
that needs to be initialized once can be done here.
• A number of things are accomplished in the run method. An instance of the IORReceiver is
instantiated and activated. Notice that in this method:
a reference to the OrbixIORTuner is passed. Remember from the discussion of the IORTuner that
its constructor takes an iorr.receiver as a parameter. Because OrbixIORTuner is an
iorr.receiver, you can pass an instance of it into the constructor.
• In this line, you go into a busy-waiting loop. This is not the most efficient way for a thread to wait for
something, because processor cycles are gobbled up with each loop. Better ways will be demonstrated
with other examples. Nevertheless, the busy-waiting tests on the truth of t_registry_ior. A null
value for t_registry_ior ensures that the block will loop. Also note that the thread sleeps for ten
seconds so that other threads can also have some CPU time.
Keep in mind that the IORReceiver is also a thread. If the class is to be continuously blocked, the
IORTuner will not get an opportunity to set the t_registry_ior to something else.
• When the busy-waiting block breaks out, the code shuts down the IORReceiver and assigns a null
value to the object reference. This is how object references can be marked for garbage collection in
Java.
• The public synchronized void set_IOR(String t_ior) is an implementation of the
required method that has to be implemented from the iorr.receiver interface. This is the method
that is invoked by the IORTuner to notify the client class that the IOR has been received. We could
have used Java’s built-in even class to do this work, but for the purposes at hand this method would
suffice. Chapter 2, “IIOP and E-Commerce,” contains an improved version of the IORTuner and
IORReceiver.
• The private void continueProcess() is where the main test block sits. All the operations
on the iorr.registry interface are tested here. Before that can be done, the appropriate object
reference needs to be constructed for the IOR that was received by the IORReceiver and passed on
to the OrbixIORClient. Let’s take a deeper look at what this method does:
The CORBA.Object contains the required method that would allow the conversion of the stringified
object reference, the IOR to an instance of CORBA.Object. This is what occurs in the method:
Tip:
Always ensure that a remote invocation is made in the context of error handling. Of course, this should be
standard practice with any implementation that has error support. This is necessary in CORBA, because if
something does go wrong remotely, the application must be capable of handling the faulty condition gracefully.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
This completes the implementation of the IOR interoperability component.
Summary
CORBA was designed since version 2 for broad-based interoperability. To this
extent, a clear distinction is made in terms of its underlying protocols. CORBA
2.0 specified two broad-transport, independent protocols: the GIOP or General
Interoperability Protocol, and the ESIOPs or Environment Specific Protocols.
A TCP transport that implements GIOP is known as IIOP, the Internet
Inter-ORB Protocol. This protocol implementation is mandatory for any
CORBA 2.0–compliant ORB.
IIOP allows interoperability between different ORBs. This interoperability can
be achieved through bridges. There are two types of bridges: a half bridge and
a full bridge. CORBA 2.0 also specifies the mechanism that allows the
conversion of an object reference into a string format. This format is known as
the Interoperable Object Reference (IOR). Objects sitting on one ORB can be
converted to an IOR and exchanged with another ORB to achieve
interoperability.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 2
IIOP and E-Commerce
In This Chapter
• CORBA and Java
• Cookies Cannot Cut It for Long
• Analysis and Design of the SOAR System
• What SOAR Should Do
• The SOAR Client
• The SearchClient GUI
• The ReservationClient GUI
• The PaymentClient GUI
• The Invoice GUI
• The SOARClient Applet
Large organizations have a multiplicity of information technology
architectures. Under those circumstances, it is unreasonable to assume a
homogeneous ORB environment. It is likely that an ORB most suited for the
environment would be present. There could be a specialized ORB for the
mainframe, one for the desktop client/server environment, and so on.
If the Internet is taken into consideration, homogeneity should surely be out of
the question. The Internet is the most powerful testbed to prove the promise of
CORBA interoperability. In this chapter, you use IIOP over the
Internet/intranet to connect complex e-commerce solutions over IIOP.
This example will not attempt to build anything as complex as a bridge.
Nevertheless, the application will be rather involved and complex. This is by
choice, because CORBA solutions are not for the fainthearted or the unskilled.
Before we dive into the internals of the problem and the proposed solution,
let’s look at the environments we are going to use.
The application uses VisiBroker 3.3. The solution will be built using Java.
This setup brings to focus a number of CORBA features. This example
demonstrates that CORBA is platform-neutral and language-independent. In
this context, it is desirable to show the synergy between Java and CORBA.
I will also show how you can use a multiplicity of development environments
with CORBA. A number of IDEs will be used to build both client-side and
server-side applications.
Note:
All the Java code will be 100 percent pure so that it will run on any
Javasoft-compatible JVMs. My own test environment runs on OS2 and
Windows NT.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Analysis and Design of the SOAR System
A through and robust analysis is a prerequisite for any software project. CORBA does not
minimize this. As much as I would like to offer an exhaustive analysis of the SOAR domain,
it is best left to another book. But I would like to take the opportunity to emphasize the
importance of object-oriented analysis and design (OOA/D)—especially the design.
Although I have stated that CORBA does not impose any new constraints on the analysis
phase of software development, it does limit your design. It must be pointed out that
CORBA does not specify things such as reliability, fault tolerance, or any other such
quality-of-service criteria. These are essentially up to the ORB vendors.
Even then, such things are not givens, but your design should manifest these requirements.
A badly designed distributed application has the potential to be your worst nightmare,
because, more often than not, you have very little control of things when they do go wrong.
This is typical for any piece of software. Imagine if things go wrong on the other side of the
world, and your piece of architecture depends on it. You can hardly call up people in the
middle of the night and expect support!
ORBs that offer quality-of-service features are coming out, so at least you can look forward
to a reasonable level of fault tolerance. Nevertheless, there is no excuse for shoddy design.
Enough said about OOA/D. Let’s lay down some of the ground rules. SOAR has a number
of requirements as well as constraints. Alas, constraint analysis is beyond the scope of this
book. I will, nevertheless, mention here what I mean by constraints. These are systems
attributes, something that is usually confused with systems functionality. By that I mean, the
user friendliness of the GUI, system response time, and so forth. These are worthy goals in
themselves, but they don’t really have anything to do with SOAR’s goals. So for the time
being, I will ignore all such constraints and describe the system only in terms of what is
required for interoperability and effectively delivering the prototype with the book.
As stated earlier, this chapter is meant to be neither exhaustive nor complete, because the
primary purpose of this chapter is to show the issues relating to a complex application.
The reservation requirements should do essentially two things: enable a customer to reserve
a seat, and, if required, enable the same or some other person to cancel an existing
reservation. The following is such an interface:
#include “factory.idl”
module Reservations{
interface Flight{
enum status{open,full};
struct seat{
string id;
status availability;
};
readonly attribute string name;
readonly attribute float cost;
readonly attribute string flightNumber;
readonly attribute string date;
readonly attribute string time;
readonly attribute short capacity;
readonly attribute string from;
readonly attribute string to;
void reserveSeat(in string id,
in Factory::Object::Person passenger);
void cancelSeat(in string id);
};
};
Most of the Flight interface is just read-only attributes. This means that the IDL
pre-compiler will only generate accessor methods for these attributes. These attributes will
be initialized by some external process; in our case, the server that holds the flight object
will do this setup to initialize the state of the aircraft. After the state is initialized, a customer
cannot change the state of the flight; he or she can only read it. The only mutable attribute in
the preceding interface is the seat. The seat can have an identifier and a flag announcing the
status of the seat.
Further on, the interface defines two operations: one for reserving the seat, the other for
canceling a reserved seat. Beyond that, the flight interface does not do anything. The
interface has to be compiled and implemented. Invoke the following command on the
idl2java pre-compiler:
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The Flight Interface Implementation
The Flight interface implementation should adhere to several design constraints. Because the space on the
airplane is finite, it is natural that this space can run out. The design should accommodate this aspect. A
seat should not be capable of being reserved more than once. This is a requirement that is stated
implicitly. Beyond this, the requirements on the interface are modest. These requirements are
implemented, as shown in Listing 2.1.
Listing 2.1 The Reservation Component
package Reservations;
import Reservations.FlightPackage.*;
import java.util.*;
public Reservations() {
super();
}
this.name = name;
this.flightNumber = flightNumber;
this.cost = cost;
this.date = date;
this.time = time;
this.capacity = capacity;
this.from = from;
this.to = to;
class reservation{
public seat a_seat;
public Factory.ObjectPackage.Person a_passenger;
..
..
seat[] seats = new seat[capacity];
for(short i = 0; i< capacity; i++){
seats[i] = new seat(new Short(i).toString(),status.open);
aircraft.add(seats[i]);
}
..
..
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The seats are initialized in an array. They are initialized to the capacity that an external process
passes as a capacity parameter inside the reservation server’s constructor. When the seats are
allocated, they are added to an instance of a JGL stack object named aircraft. The
aircraft object represents the seats that are available in an aircraft.
The reserveSeat() method is passed two parameters: a String identifier and an instance
of a Passenger (to be specified later). The client invokes this method to reserve a seat, the
identifier being the seat number the client wants to reserve. In terms of the implementation, the
reservation object first validates the existence of such a seat in the aircraft. If an instance is
present, the implementation reserves this seat by moving the seat, after setting the state of the
object, to another stack object called reservations. Over time, the number of seats inside
the aircraft stack should go down, and the number of seats present in the reservations
collection should go up.
I took this approach because it seems easier to remove an object from the reservations
stack if a cancel operation is called on the Reservation interface. Both reserveSeat()
and cancelSeat() generate an event, the ReservationEvent. This enables us to create a
loose coupling between some GUI that we are yet to build and this object.
The Reservation implementation defines a set of event-related classes that is used to couple
the GUI with this class. The Reservation design specifies a listener interface that can be used
by any party to listen in on event when a ReservationEvent takes place. The following is
the interface:
package Reservations;
import java.util.*;
package Reservations;
import java.util.*;
int getID() {
return id;
}
}
When a ReservationEvent takes place—for instance, a reserve event or a cancel event, the
aforementioned methods invoke the following method:
…
…
ReservationEvent event =
new ReservationEvent(this,ReservationEvent.SeatCanceled);
sendMessage(event);
…
…
The object will instantiate a ReservationEvent object and fire the sendMessage()
method. sendMessage invokes the instances that have registered with the object for
notification; in this case, such objects have to implement the ReservationListener
interface. This code is shown here:
Listing 2.2 illustrates a typical implementation of a CORBA server. It uses Java Swing class for
the GUI. Here, you separate the server from the GUI that acts as a front-end to the server. This
can be accomplished if you implement a server class as an object that is capable of running as a
thread inside the GUI object. This enables the GUI to respond to interactions with the user at the
same time that it is providing service to the client. The server code is shown first and then the
GUI code.
Listing 2.2 The Flight Server
package Reservations;
Server(ReservationServer rs_){
gui = rs_;
}
rs[i].addReservationListener(gui);
boa.obj_is_ready(rs[i]);
gui.DisplayMessage(rs[i] + “ is ready.”);
}
try{
SearchEngine.RegisterFlight(rs);
}
catch(org.omg.CORBA.SystemException e){
gui.DisplayMessage(e.toString());
}
boa.impl_is_ready();
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The ReservationServer initializes the reservations objects that implement the Flight
interface. If a more real-world scenario is required, the reservations objects can be initialized
using data that is retrieved from a database. In this case, you just hold the reservations objects in
an array.
org.omg.CORBA.Object obj =
resolv.locate(“http://localhost:15000/timetable.ior”);
SearchEngine = Search.TimeTableHelper.narrow(obj);
Before that can be accomplished, the reservations objects have to be constructed. In this case,
two such objects are constructed. The first reservation object represents a flight from London to
Warsaw and the second, from Los Angles to Rome. Both flights have a capacity of 10 seats:
Java Swing classes are used to construct a user interface that monitors the internal state of the
reservation object references. Primarily, it should display Reservation events as and when they
occur. This code is shown in Listing 2.3.
Listing 2.3 The ReservationServer Interface
package Reservations;
import java.awt.*;
import com.sun.java.swing.*;
import org.omg.CORBA.*;
import java.awt.event.*;
import borland.jbcl.layout.*;
public ReservationServer(){
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
}
void StartButton_mouseClicked(MouseEvent e) {
if(rs == null){
rs = new Server(this);
serverThread = new Thread(rs);
serverThread.start();
}
this.DisplayMessage(“Reservation Server Initialized.”);
}
void StopButton_mouseClicked(MouseEvent e) {
if(rs != null){
rs.stop();
rs = null;
}
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The essential nature of the GUI is that it implements the ReservationListener
interface. It is through this interface that the reservations object communicates with
the user interface. The implementation of this interface is very simple, depending on the
message ID the class invokes the DisplayMessage method to output a string to the
display area. The corresponding implementation is shown in the following lines of code:
rs = new Server(this);
serverThread = new Thread(rs);
serverThread.start();
The stop command terminates the thread by invoking stop() on it.
We have now completed the implementation of the Reservation interface. The
complete implementation consisted of implementing the Reservation interface,
constructing a Server class to host the objects themselves, and providing a GUI to
provide visual cues to the user. The exact manner in which to launch the server will be
explained after we have constructed the entire application. Figure 2.1 shows the server
running.
The SOAR search engine is nothing complicated. On the user end, it should enable a
customer to search for a potential aircraft by some criteria. In the ideal situation, a
customer would search by whatever criteria seems appropriate to the customer. In this
case, you have to keep things simple to not complicate the overall design of SOAR.
You can achieve this simplicity if you narrow down the list of criteria a customer can
search by to the barest minimum. In this case, let’s enable the customer to search only by
the departure point and the destination point. The interface that would enable you to build
such a search service is given in the following code:
#include “reservations.idl”
module Search{
interface TimeTable{
typedef sequence<Reservations::Flight> FlightList;
void RegisterFlight(in FlightList TimeTableEntries);
void RemoveEntry(in string FlightNumber);
FlightList search(in string From,
in string To,
in string Date,
in string Time);
};
};
The Search module specifies an interface that is named TimeTable. This interface
represents the external face of the search engine. The interface itself specifies three
operations. The first is RegisterFlight(). This operation takes in a sequence of
Reservation::Flight objects. This enables the instance of TimeTable objects to
return instances of Flight objects to the client that is using its search facility.
RemoveEntry is another administration operation that enables a registered flight object
to be removed from the TimeTable. Finally, the Search() operation enables a client
to search by a number of parameters, such as destination and departure points, dates, time
of day, or all of these criteria. Let’s look at one possible manner of implementing this
interface.
The TimeTable Implementation
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Listing 2.4 The Search Component
package Search;
import java.util.*;
timetable.push(TimeTableEntries[i]);
The next operation that is implemented is the RemoveEntry() method. This method is passed an
identifier that is used to locate the flight object in the timetable stack. The search is carried out by
constructing an enumeration and checking whether the parameter that is passed to the implementation
matches an attribute of an object in the stack. The following is the associated code:
if(From.equalsIgnoreCase(temp_.from()) &&
To.equalsIgnoreCase(temp_.to()))
The search implementation in this case searches on the departure point and the destination points only. All
the elements in the timetable that match this criteria are added to an array. This array constitutes the result
of the search. This array is passed back to the client. As with the other two methods, the search also
generates a TimeTableEvent to the registered listeners. The next sections explain the classes that help
specify the events that the Schedule class generates.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The TimeTableEvent Class and Interface
The event listener interface needs to be implemented by the classes that are interested in receiving events
from the Schedule object:
package Search;
import java.util.*;
package Search;
import java.util.*;
int getID() {
return id;
}
String toString(){
return msg;
}
}
The preceding event class is slightly different from the ReservationEvent in terms of the internal
data structure. This class contains a String object that encapsulates the actual event message. The
Schedule class goes uses the event class in the following manner:
package Search;
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The stop() method does the reverse of what is done in the run() method. The
TimeTableServer is to be launched inside the GUI object; this class is shown next. The code, as
shown in Listing 2.6, is essentially the same as the Reservation GUI.
Listing 2.6 The SearchServer Interface
package Search;
import java.awt.*;
import org.omg.CORBA.*;
import java.awt.event.*;
import com.sun.java.swing.*;
public SearchServer() {
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
}
The SOAR application requires a number of objects to make it work. In the context of making a
reservation, for example, you require a passenger object. In making a payment, there are a number of
objects that are required. For example, you might require a number of addresses, credit card information,
and so forth. It is possible to design all these in the context of the interfaces that have been specified
until now; the Reservation module could have defined a passenger interface as well. But it might
make things a little clearer if I define these things seperately. The following Factory interface defines
all such concepts:
module Factory{
interface Object{
struct Person{
string name;
short age;
char gender;
string email;
};
struct CreditCard{
string CardType;
string CardNumber;
string ExpiryDate;
};
struct Address{
string addressOne;
string addressTwo;
string city;
string stateOrProvince;
string zipcode;
string country;
};
Person createPassenger(in string name,
in short age,
in char gender,
in string email);
CreditCard creatCreditCard(in string type,
in string number,
in string expiry);
Address createAddress(in string addressOne,
in string addressTwo,
in string city,
in string state,
in string zip,
in string country);
};
};
The Object interface defines three structs; Person, Address, and CreditCard. It further
specifies three operations. Each of these operations creates and returns an appropriate struct. So you
have one operation that creates a person, another for the address, and another to create a credit card.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Implementing the Factory::Object Interface
The implemetation of these operations is fairly simple. All that an implementation has to do is initialize a
struct with the parameter that is passed into these operations and return the requested struct. The
implemention of Factory::Object is done in the Factory class. The implementation does not
maintain any state, so it is very simple, as shown in Listing 2.7.
Listing 2.7 The Factory Component
package Factory;
public class factory extends Factory._ObjectImplBase {
public factory() {
super();
}
public Factory.ObjectPackage.Person createPassenger(
java.lang.String name,
short age,
char gender,
java.lang.String email
) {
The FactoryServer
The FactoryServer, like the servers implemented before consists of two classes: one that implements
the server itself and the other that just builds the GUI to hold the server. The server code implements the
Runnable interface so that the GUI class can launch it on a thread. Let’s first look at the server code and
then the GUI code (see Listing 2.8).
Listing 2.8 The FactoryServer Thread
package Factory;
FactoryServer(FactoryServer rs_){
gui = rs_;
}
public FactoryServer() {
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
}
private void jbInit() throws Exception {
this.setLayout(null);
jScrollPane1.setBounds(new Rectangle(2, 8, 324, 143));
StartButton.setText(“Start”);
StartButton.setBounds(new Rectangle(250, 155, 72, 24));
StartButton.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(MouseEvent e) {
StartButton_mouseClicked(e);
}
});
this.add(jScrollPane1, null);
jScrollPane1.getViewport().add(factoryDisplay, null);
this.add(StartButton, null);
}
void StartButton_mouseClicked(MouseEvent e) {
FactoryServer factoryserver = new FactoryServer(this);
ServerThread = new Thread(factoryserver);
ServerThread.start();
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The final element of the SOAR application on the server side is to construct a Payment server. When a
customer has located a flight he or she wants using a Search server, the customer will then reserve a seat
on the flight. When this is done, a payment needs to be made to confirm the reservation. It is at this point
that the Payment server kicks in.
The Payment server is the last link in the chain that makes up SOAR. After the user has selected a flight,
he or she has to pay for it using a credit card. The Payment server supports this facility. When the
payment is made, the Payment server should return an invoice as proof of payment. An interface that
enables you to do this is given here:
#include “factory.idl”
module Bank{
interface Payment{
struct invoice{
Factory::Object::Person from;
string to;
string invoiceNumber;
float amount;
string date;
};
invoice createPayment(
in Factory::Object::Person from,
in string to,
in Factory::Object::CreditCard plastic,
in Factory::Object::Address billing,
in float amount);
Ideally, the implementation of the Payments interface would use a transaction service to record the
payment in a database. But that is not done here to keep the implementation simple. In essence, the
implementation of the next interface is very much in the spirit of how we implemented the
Factory::Object interface. The interface is implemented in a class named Payments, as shown in
Listing 2.10.
Listing 2.10 The Payments Component
package Bank;
import java.util.*;
public class Payments extends Bank._PaymentImplBase {
public Payments() {
super();
}
class paymentHolder {
Factory.ObjectPackage.Person from;
java.lang.String to;
Factory.ObjectPackage.CreditCard plastic;
Factory.ObjectPackage.Address billing;
float amount;
String invoiceNumber;
paymentHolder(Factory.ObjectPackage.Person from,
java.lang.String to,
Factory.ObjectPackage.CreditCard plastic,
Factory.ObjectPackage.Address billing,
float amount,
String invoiceNumber
) {
this.from = from;
this.to = to;
this.plastic = plastic;
this.billing = billing;
this.amount = amount;
this.invoiceNumber = invoiceNumber;
}
}
The package Bank defines two classes: Payments and PaymentHolder. All the invoices that are
created by the Payments class are stored in a JGL stack. This enables us to insert an invoice into the stack
when it is created and remove it from the stack when the client requests that a payment be undone.
The createPayment() method does two things. First, it creates an instance of a PaymentHolder
and inserts it into the stack. The next thing it does is to create an invoice object and return it to the client.
The following line shows the creation of the invoice object:
Bank.PaymentPackage.invoice t_invoice =
new Bank.PaymentPackage.invoice(from,to,
new Integer(number).toString(),amount,today.toString());
In the undoPayment() method, the client passes an invoice identifier that is used to locate the
PaymentHolder instance from the stack and then remove it. This is accomplished by constructing an
enumeration on the elements of the stack and iterating over it until the desired invoice is located. When it is
located, the instance of PaymentHolder is removed from the stack.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The PaymentServer GUI
Finally, we are now ready to construct a server to host the Payments object reference. Like the three
servers that we built earlier, the GUI for the server is separated from the actual server itself. The server is
constructed on a thread to allow the GUI to function more responsively. First, you look at the
implementation for the server, as shown in Listing 2.11, and then at the implementation for the GUI.
Listing 2.11 The PaymentServer Thread
Package Bank;
Server(PaymentServer ps){
gui = ps;
}
}
catch(Exception e) {
System.out.println(e.toString());
}
try{
boa.impl_is_ready();
}
catch(org.omg.CORBA.SystemException ex){
gui.DisplayMessage(ex.toString());
}
}
}
As with the FactoryServer implementation, the Server class is very typical for CORBA servers.
The two methods that are vital to this implementation are constructing the Payments object and
registering this object reference with the Visigenic URL Naming Service. The first is accomplished in the
following line of code:
resolver.force_register_url(“http://localhost:15000/payment.ior”,
payment);
All that remains is to construct the GUI which enables you to monitor this server, as shown in Listing
2.12.
Listing 2.12 The PaymentServer Interface
package Bank;
import com.sun.java.swing.*;
import java.awt.*;
import java.awt.event.*;
public PaymentServer() {
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Both these scenarios tend to clutter your OS workspace with innumerable windows, and besides, it is
quite cumbersome. I usually provide a Multi Document interface to all the servers that make up an
application. Since the introduction of Swing, this has become possible in Java using
InternalFrames. So let’s use InternalFrames to host all the GUI objects in this chapter. In
our case, we need to host four GUI objects. The code for doing this is given in Listing 2.13.
Listing 2.13 The SOARServerWindow
package Server;
import java.awt.*;
import com.sun.java.swing.*;
JInternalFrame FactoryFrame;
JInternalFrame BankFrame;
JInternalFrame SearchFrame;
JInternalFrame ReservationFrame;
JLayeredPane layers;
public ServerWindow() {
this.setTitle(“Super ORB”);
layers = new JDesktopPane();
setLayeredPane(layers);
FactoryFrame.getContentPane().add(factoryServerWindow,“Center”);
BankFrame.getContentPane().add(paymentServerWindow,“Center”);
SearchFrame.getContentPane().add(searchServerWindow,“Center”);
ReservationFrame.getContentPane().add(
reservationServerWindow,“Center”);
FactoryFrame.pack();
BankFrame.pack();
SearchFrame.pack();
ReservationFrame.pack();
layers.add(FactoryFrame);
layers.add(BankFrame);
layers.add(SearchFrame);
layers.add(ReservationFrame);
this.setVisible(true);
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The SearchClient GUI
The user interface for the search engine should enable a user to input a departure point and an arrival point
and invoke the search() operation on the TimeTable object reference. The result that the
TimeTable object provides should be displayed in a grid object listing all the parameters of the flights
available. The code that enables you to do this is shown in Listing 2.14.
Listing 2.14 The SearchClient Interface
package SoarClient;
import java.awt.*;
import com.sun.java.swing.table.*;
import com.sun.java.swing.border.*;
import com.sun.java.swing.event.TableModelListener;
import com.sun.java.swing.event.TableModelEvent;
import java.util.Vector;
import com.sun.java.swing.*;
import java.awt.event.*;
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
try{
org.omg.CORBA.Object obj = resolver.locate(
“http://localhost:15000/timetable.ior”);
searchEngine = Search.TimeTableHelper.narrow(obj);
}
catch(Exception e) {
System.out.println(e.toString());
}
}
jLabel1.setText(“From”);
jLabel1.setBounds(new Rectangle(8, 14, 41, 15));
jLable2.setText(“To”);
jLable2.setBounds(new Rectangle(173, 14, 41, 15));
ToField.setToolTipText(“Enter Separture Point”);
ToField.setBounds(new Rectangle(173, 31, 149, 19));
jScrollPane1.setBounds(new Rectangle(7, 58, 452, 154));
SearchButton.setText(“Search”);
SearchButton.setBounds(new Rectangle(286, 222, 84, 23));
SearchButton.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(MouseEvent e) {
SearchButton_mouseClicked(e);
}
});
Cancel.setText(“Cancel”);
Cancel.setBounds(new Rectangle(372, 222, 84, 23));
Reserve.setText(“Reserve”);
Reserve.setBounds(new Rectangle(200, 222, 84, 23));
Reserve.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(MouseEvent e) {
Reserve_mouseClicked(e);
}
});
FromField.setToolTipText(“Enter Destination Point”);
FromField.setBounds(new Rectangle(7, 31, 149, 19));
setLayout(null);
add(jLabel1, null);
add(jLable2, null);
add(ToField, null);
add(FromField, null);
add(jScrollPane1, null);
jScrollPane1.getViewport().add(resultTable, null);
add(SearchButton, null);
add(Cancel, null);
add(Reserve, null);
}
void SearchButton_mouseClicked(MouseEvent e) {
Reservations.Flight[] flights;
flights = searchEngine.search(
FromField.getText(),ToField.getText(),“”,“”);
System.out.println(“Results ” +
new Integer(flights.length).toString());
SearchResultModel _model
= (SearchResultModel)resultTable.getModel();
java.util.Vector t_ = new java.util.Vector();
String[] temp_ = new String[5];
for(int j = 0; j < _model.getRowCount(); j++){
_model.removeRow(j);
}
resultTable.setModel(_model);
resultTable.tableChanged(
new TableModelEvent(_model,tablemodel.getRowCount()));
resultTable.repaint();
void Reserve_mouseClicked(MouseEvent e) {
SearchResultModel _model =
(SearchResultModel)resultTable.getModel();
int selectionIndex = resultTable.getSelectedRow();
String[] temp_ = new String[7];
for(int i = 0; i < _model.getColumnCount(); i++){
temp_[i] = (String)_model.getValueAt(selectionIndex,i);
}
temp_[5] = FromField.getText();
temp_[6] = ToField.getText();
client.setParams(temp_);
panel.displayPanel(Soar.RESERVATIONPANEL);
}
}
public SearchResultModel(){
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The SoarClient package implemented in Listing 2.14 specifies two classes. The first
class deals with the user interface itself, and the second class defines a TableModel
that will be used to construct a Jtable object to display the results of the search. This
discussion highlights only the essentials of the preceding two classes—particularly the
code that deals with CORBA.
The constructor is passed five parameters that are required by the search client to do its
job. The parameters are constructed in the applet. The constructor is shown in the
following code:
flights = searchEngine.search
(FromField.getText(), ToField.getText(),“”,“”);
If the searchEngine finds flights that the user is searching for, the flights array will
be initialized. When the array of flights are retrieved from the server, the TableModel
is modified so that the result can be displayed in the table object that the client uses to
display the results. This implementation is shown in the following lines of code:
resultTable.setModel(_model);
resultTable.tableChanged(
new TableModelEvent(_model,tablemodel.getRowCount()));
resultTable.repaint();
With a successful result, the table object will display all the flights that were returned.
The next step in the chain is for the user to select some flight and invoke the reserve
button. At that point, the following code will
execute:client.setParams(temp_);
panel.displayPanel(Soar.RESERVATIONPANEL);
The first line of code initializes the state of the client code. In this case, the client is the
ReservationClient that was passed into the SearchClient by the applet. The
second line of code invokes the displayPanel on the panel object with the
parameter Soar.RESERVATIONPANEL. This causes the panel to invoke the card that
is identified by the final attribute—in this case, the ReservationClient. The first
panel is shown executing in Figure 2.7.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Listing 2.15 The ReservationsClient Interface
package SoarClient;
import java.awt.*;
import com.sun.java.swing.*;
import borland.jbcl.layout.*;
import java.awt.event.*;
import com.sun.java.swing.table.*;
import com.sun.java.swing.event.TableModelListener;
import com.sun.java.swing.event.TableModelEvent;
import java.util.*;
import org.omg.CORBA.*;
AirlineField.setText(params[0]);
FlightNoField.setText(params[1]);
DateField.setText(params[2]);
TimeField.setText(params[3]);
CostField.setText(params[4]);
}
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
try{
org.omg.CORBA.Object obj = resolver.locate(
“http://localhost:15000/factory.ior”);
factory = Factory.ObjectHelper.narrow(obj);
}
catch(Exception e) {
System.out.println(e.toString());
}
}
public ReservationsClient() {
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
}
void AddPassengerButton_mouseClicked(MouseEvent e) {
Factory.ObjectPackage.Person passenger = null;
PassengerListModel _model =
(PassengerListModel)PassengerList.getModel();
passengers = new java.util.Vector();
String[] temp_ = new String[4];
temp_[0] = Name.getText();
temp_[1] = Age.getText();
temp_[2] = (String)Gender.getSelectedItem();
temp_[3] = Email.getText();
String gn = (String)Gender.getSelectedItem();
char Gendr = gn.charAt(0);
Short age_ = new Short(Age.getText());
short age = age_.shortValue();
try{
passenger = factory.createPassenger(
Name.getText(),
age,Gendr,Email.getText());
}
catch(SystemException ex){
System.out.println(ex.toString());
}
passengers.addElement(passenger);
_model.addRow(temp_);
PassengerList.setModel(_model);
PassengerList.tableChanged(
new TableModelEvent(_model,tablemodel.getRowCount()));
PassengerList.repaint();
}
void ConfirmButton_mouseClicked(MouseEvent e) {
PassengerListModel _model = (
PassengerListModel)PassengerList.getModel();
String[] temp_ = new String[5];
Enumeration ex = passengers.elements();
Factory.ObjectPackage.Person passenger = null;
Reservations.Flight flight = null;
short seatsleft = 0;
try{
flight = Reservations.FlightHelper.bind(orb,AirlineField.getText());
seatsleft = flight.capacity();
}
catch(SystemException excp){
System.out.println(excp.toString());
}
if(seatsleft >= passengers.size() ){
while(ex.hasMoreElements()){
passenger = (Factory.ObjectPackage.Person)ex.nextElement();
try{
flight.reserveSeat(new Short(seatsleft).toString(),passenger);
}
catch(SystemException excp){
System.out.println(excp.toString());
}
seatsleft--;
}
}
String[] param = new String[8];
param[0] = params[5];
param[1] = params[6];
param[2] = params[2];
param[3] = params[0];
PassengerListModel model =
(PassengerListModel)PassengerList.getModel();
int selectionIndex = PassengerList.getSelectedRow();
payment.setParam(param,passenger);
panel.displayPanel(Soar.PAYMENTPANEL);
}
void BackButton_mouseClicked(MouseEvent e) {
panel.displayPanel(Soar.SEARCHCLIENTPANEL);
}
}
public PassengerListModel(){
}
public int getColumnCount(){
return columns.length;
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The important aspects of the code are given in the following snippet. The rest of the code essentially deals
with the presentation of information to the user:
passenger = factory.createPassenger(Name.getText(),age,Gendr,Email.
getText());
When the user enters the details for a passenger and clicks the add button, the Factory object reference is
used to construct a passenger object. This information is further used to add to the TableModel so that
the user can see what passengers are added to the reservation request. Finally, the user has to select a user
from all the users that are shown in the table object and hit the Confirm button. At that point the following
code is invoked:
flight = Reservations.FlightHelper.bind(orb,AirlineField.getText());
seatsleft = flight.capacity();
In the first line of code, the bind method is invoked to retrieve the Flight object reference. After the
reference is retrieved, the capacity is checked. The following code is executed only if the capacity of the
flight in terms of seats left is greater than the number of reservations required:
flight.reserveSeat(new Short(seatsleft).toString(),passenger);
All the passengers that the user input into the JTable object are passed into the flight object reference by
invoking the reserveSeat() operation:
payment.setParam(param,passenger);
panel.displayPanel(Soar.PAYMENTPANEL);
Finally, the next object in the chain is initialized to the appropriate state—that is, all the state that is
common between the Reservations server and the Payment server. This is done in the first line of
code. Then the panel object is instructed to display the Soar.PAYMENTPANEL object, which will be used
by the user to make the payment.
The executing client is shown in Figure 2.8.
This is the PaymentClient GUI. The implementation for this class is given in Listing 2.16.
Listing 2.16 The PaymentClient Interface
package SoarClient;
import java.awt.*;
import com.sun.java.swing.*;
import java.awt.event.*;
import org.omg.CORBA.*;
import org.omg.CORBA.ORB.*;
try{
org.omg.CORBA.Object factoryObject =
resolver.locate(“http://localhost:15000/factory.ior”);
factory = Factory.ObjectHelper.narrow(factoryObject);
org.omg.CORBA.Object payementObject =
resolver.locate(“http://localhost:15000/payment.ior”);
payments = Bank.PaymentHelper.narrow(payementObject);
}
catch(Exception e) {
System.out.println(e.toString());
}
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
}
public PaymentClient() {
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
}
Factory.ObjectPackage.Address address =
factory.createAddress(address1,
address2,
city,
state,
zip,
country);
Float cost = new Float(params[5]);
Bank.PaymentPackage.invoice invce =
payments.createPayment(passenger,
Airline.getText(),cc,address,cost.floatValue());
invoice.setInvoice(invce);
panel.displayPanel(Soar.INVOICECLIENTPANEL);
}
catch(SystemException ex){
System.out.println(ex.toString());
}
}
void BackButton_actionPerformed(ActionEvent e) {
panel.displayPanel(Soar.RESERVATIONPANEL);
}
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The payment client uses two object references: the Factory object and the Payment object. Both these
object references are retrieved using the VisiBroker URL service:
org.omg.CORBA.Object factoryObject =
resolver.locate(“http://localhost:15000/factory.ior”);
factory = Factory.ObjectHelper.narrow(factoryObject);
org.omg.CORBA.Object payementObject =
resolver.locate(“http://localhost:15000/payment.ior”);
payments = Bank.PaymentHelper.narrow(payementObject);
The user has to fill in all the fields on the form. After the user has finished filling in all the information, the
user can press the Buy button. Then the following code is executed:
Factory.ObjectPackage.CreditCard cc = factory.creatCreditCard(card,
number,
expiry);
package SoarClient;
import java.awt.*;
import com.sun.java.swing.*;
package SoarClient;
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import com.sun.java.swing.*;
import com.sun.java.swing.UIManager;
import org.omg.CORBA.*;
public Soar() {
try{
orb = org.omg.CORBA.ORB.init(this,null);
resolver = com.visigenic.vbroker.URLNaming.ResolverHelper.narrow(
orb.resolve_initial_references(“URLNamingResolver”));
}
catch(org.omg.CORBA.ORBPackage.InvalidName ivn){
System.out.println(ivn.toString());
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The applet initializes the ORB and the associated resources. This is accomplished in the following three
lines of code:
orb = org.omg.CORBA.ORB.init(this,null);
resolver = com.visigenic.vbroker.URLNaming.ResolverHelper.narrow
(orb.resolve_initial_references(“URLNamingResolver”));
After this is done, the applet code initializes all the SOAR clients in sequence:
Note:
For the previous application, I used the Java WebServer 1.0.3 and HotJava Web browser. You could use
any Web server to hold the client code.
<HTML>
<HEAD>
<META HTTP-EQUIV=“Content-Type” CONTENT=“text/html;
charset=iso-8859-1”>
<TITLE>
Super Orb Airline Reservartion
</TITLE>
</HEAD>
<BODY>
Super Orb Airline Reservations.<BR>
<APPLET
CODEBASE = “.”
CODE = “SoarClient.Soar.class”
NAME = “Soar”
WIDTH = 600
HEIGHT = 600
HSPACE = 0
VSPACE = 0
ALIGN = middle
>
</APPLET>
</BODY>
</HTML>
The two most important parameters in Listing 2.19 are the CODE and CODEBASE tags. The former
identifies the class that has to be loaded into the browser, and the latter indicates where the code is to be
loaded from. CODEBASE specifies “.”, which means that the code will be found where the HTML
page was found in the Web server.
It is necessary to load the applet from a Web server, because the applet is going to make network
connections. Java places the restriction on applets that they cannot connect to servers other than the one
on which they were loaded. Therefore, if the applet was just loaded from the hard drive, the Java Virtual
Machine security manager throws a security exception.
Additionally, the following steps have to be taken to ensure that the applet will execute properly in the
browser:
1. Ensure the Web server is running.
2. Map the Java Class package directory as an alias so that the applet can download the code
from the location that is specified in the CODEBASE tag. For instance, if you store the HTML
page and the code for the SOAR client in a directory C:\Corba, map this directory to an alias in
the Web server—for example, /Corba.
3. Place all the packages in the directory where your page will be downloaded. For the HotJava
Web browser, the following class packages are placed in the page directory:
Unjar the vbjorb.jar archive file to the page directory.
Unjar the swingall.jar archive file to the page directory.
4. Load the SOAR client HTML page from the Web server. The applet executing in the browser
is shown in Figure 2.11.
Summary
CORBA over the Internet has the potential of transforming the nature of applications built over the
Internet. The first impact will probably occur when innovative companies use CORBA-based
application servers to drive second-generation e-commerce systems. Java will play an ever-increasing
role in the drive of what Orfalli and Harkey (Java and CORBA) called the Object Web. The true
potential of the Internet as a medium to transform our lives will then become evident.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 3
The Portable Object Adapter
In This Chapter
• The Rationale Behind POA
• Using POA
• Getting Hold of the POA
• Implementing a POA Application
• Interface Implementation
• The HangarServer Implementation
• The Hangar Deactivation Class
• The Hangar Server GUI
• The HangarClient
A new standard object adapter specification was added to CORBA as of
version 2.2. This new adapter is called the Portable Object Adapter (POA).
This new adapter is meant to replace the Basic Object Adapter (BOA). The
object adapter is a key CORBA architectural component. A server
communicates with an ORB through the object adapter. It serves as a central
gateway functionality that is common for all implementations.
The BOA was meant to be a minimalist adapter. Over time, the BOA has
grown into a multifunctional component. Its primary purpose is to control the
life cycle of an implementation. It is the primary interface into an
implementation repository. Some implementations of the BOA also add basic
security information in the implementation repository, such as granting access
privileges. Since CORBA 1.0, the BOA has grown into a behemoth. The
problem is that every ORB vendor has its own interpretation of what the BOA
is. Consequently, the server-side implementation is not portable between
ORBs. These implementations have been many proprietary extensions. In
itself, this is not a bad development. The only thing OMG mandates for an
implementation is a basic level of interoperability. Various ORBs achieve this
to various degrees.
The POA is designed to achieve portability at the server end. It is a
breakthrough specification for OMG and is a very tight and precise
specification. The overall result should mean that vendors implementing POA
would enable the user to move implementations from one ORB to another with
minimal hassle.
Using POA
POA implementations are only just becoming available. One of the first
implementations of POA is from DAIS J2 from ICL. The version used for the
examples is a DAIS J2 implementation. As such, the beta release is not a
complete implementation of POA. It is nevertheless sufficient to demonstrate
the benefits of POA.
The ORB interface enables an application to obtain a POA object. This object
is known as a root POA, and it is just like the other CORBA services that are
available through the ORB operations. When a POA is created, a specific
policy should be associated with a POA. If none is specified, a default policy
will be used.
The example demonstrates only the root POA. The root POA has the following
default policies:
Thread Policy: ORB_CTRL_MODEL
Lifespan Policy: TRANSIENT
Object ID Uniqueness Policy: UNIQUE_ID
ID Assignment Policy: SYSTEM_ID
Servant Retention Policy: RETAIN
Request Processing Policy: USE_ACTIVE_OBJECT_MAP_ONLY
Implicit Activation Policy: IMPLICIT_ACTIVATION
The POA is an active service such as the Naming Service. When the POA is
created or acquired, the following steps need to be taken for an application to
use the POA:
1. Acquire a servant object.
2. Activate the object.
3. Activate the POAManager.
4. Deactivate the object.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Getting Hold of the POA
The POA is just like any other service that runs on top of the ORB—for instance, the
Naming service or the Trader service. The POA is acquired by requesting the ORB for an
initial_reference by passing a string literal “ROOTPOA”:
org.omg.CORBA.Object poa_ =
orb.resolve_initial_references(“RootPOA”);
org.omg.PortableServer.POA poa =
org.omg.PortableServer.POAHelper.narrow(poa_);
CORE object services uder CORBA can always be retrieved using the resolve_
initial_services() operation. This operation retrieves the object reference. After the
object reference has been retrieved, it has to be narrowed. In order to retrieve the POA, you
use the same mechanism that was shown previously. The default POA is known as the
“RootPOA”. After the POA is acquired, the interface needs to be instantiated. This is
similar to the activation of an object; the code could look something like this:
interface Hangar{
exception HangarException {string reason;};
struct aircraft{
long id;
string name;
};
void CheckInAirCraft(in aircraft
t_aircraft);
void CheckOutAirCraft(in long id);
void kill();
};
The Hangar.idl needs to be compiled using the DAISORB stubgen idl pre-compiler. The
following environment is used to implement the Hangar interface:
Interface Implementation
The interface is implemeted by defining a class, HangarImpl, that is derived from the
HangarOperations class. The code, as shown in Listing 3.1, is very simple and
straightforward.
Listing 3.1 The HangarImpl Implementation
import java.util.*;
import org.omg.CORBA.*;
import org.omg.PortableServer.*;
import java.lang.Thread;
import com.sun.java.swing.*;
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The HangarServer Implementation
The HangarServer is the heart of setting up POA. All the steps listed earlier in the
chapter will be illustrated in the implementation of the server, which is shown in Listing
3.2.
Listing 3.2 Implementing the HangarServer
import com.sun.java.swing.*;
import java.lang.Thread;
orb = org.omg.CORBA.ORB.init(args,null);
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The Hangar Deactivation Class
The POA and the servants need to be cleanly shut down before exiting. The object
references that were registered with the Trader service should be removed from the
Trader context. A Terminator class accomplishes the removal of the object
reference, as shown in Listing 3.3. This class does the reverse of what the server class
does. An added benefit might be that the terminator (and the server) might be
generalized into an abstract base class. This can then be subclassed and extended by the
Server and Terminator classes, thereby saving implementation time and code.
Listing 3.3 The Terminator Class
import com.sun.java.swing.*;
import java.util.*;
Hangar = HangarHelper.narrow(
(results.value[0]).offer_ref );
}
catch ( org.omg.CORBA.UserException e ){
display.append(e.toString());
return;
}
catch ( org.omg.CORBA.SystemException e ){
display.append( e.toString());
return;
}
catch( Exception e){
display.append(e.toString());
}
try{
Hangar.shutDown();
}
catch( org.omg.CORBA.SystemException ex){
display.append(ex.toString());
}
catch( java.lang.Exception exp){
display.append(exp.toSting());
}
Hangar._release();
orb.shutdown(true);
display.append(“Server Shutdown.. \n”);
}
}
The shutDown() method on the Hangar object reference is called. This causes the
shutDown() method to execute on the object, deactivating the object reference with
the POA.The Hangar._release() frees the Hangar object reference that was
acquired from the Trader. Calling the shutdown() on the ORB shuts down the ORB
instance that was acquired by the ORB.init method.
import java.awt.*;
import com.sun.java.swing.*;
import java.awt.event.*;
import java.util.*;
import java.lang.Thread;
void StopBtn_mouseClicked(MouseEvent e) {
String[] args = new String[1];
args[0] = “Hangar”;
Terminator theSweeper = new Terminator(args,
ServerStatus);
Thread terminator = new Thread(theSweeper);
terminator.start();
}
void StartBtn_mouseClicked(MouseEvent e) {
Date today_ = new Date();
String msg = “Started Server: ” +
today_.getHours() + “:” +
today_.getMinutes()+“:”+
today_.getSeconds();
ServerStatus.append(msg + “\n”);
HangarServer temp_ =
new HangarServer(args,HangarStatus,ServerStatus);
Thread HangarThread = new Thread(temp_);
HangarThread.start();
}
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The user clicking on the “stop” button instantiates an instance of the Terminator class. The Terminator,
as mentioned previously, shuts down the POA and associated resources. Here is the relevant piece of code:
void StopBtn_mouseClicked(MouseEvent e) {
String[] args = new String[1];
args[0] = “Hangar”;
Terminator theSweeper = new Terminator(args,
ServerStatus);
Thread terminator = new Thread(theSweeper);
terminator.start();
}
The “start” button starts an instance of the Hangar servant on a new thread. This is the code that handles this
function:
void StartBtn_mouseClicked(MouseEvent e) {
Date today_ = new Date();
String msg = “Started Server: “ +
today_.getHours() + “:” +
today_.getMinutes()+“:”+
today_.getSeconds();
ServerStatus.append(msg + “\n”);
HangarServer temp_ =
new HangarServer(args,HangarStatus,ServerStatus);
Thread HangarThread = new Thread(temp_);
HangarThread.start();
}
The HangarClient
Unlike the server, the client code is very low-tech. It just demonstrates that everything functions as it should.
The body of code is very similar to that of the server. The Trader plays the central role. The client retrieves
the reference to the Trader. From the Trader, the client then resolves the Hangar’s object reference. This is
shown in Listing 3.5.
Listing 3.5 The HangarClient
import org.omg.CORBA.*;
import org.omg.PortableServer.*;
import java.util.*;
public class HangarClient {
Hangar Hangar = null;
HangarPackage.aircraft checkin_aircraft = new
HangarPackage.aircraft();
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Part II
IDL Language Mapping
In This Part
• CORBA Object References and Smart Pointers
• Java/IDL Mapping
• CORBA C++ Memory Management
Chapter 4
CORBA Object References and Smart
Pointers
In This Chapter
• Object Reference Mapping in C++
• Object References and Reference Counts
• Implementing the Object Module
Object references are the workhorse of CORBA. It is on an object reference that the
client invokes an operation. This chapter looks at how these object references can
be managed. Particular attention will be paid to the issue of object reference counts
and how the ORB interface API could be used to elicit information on the services
and objects available on the ORB.
The chapter primarily deals with management of reference counts, which includes
passing object references. It will also look at who ultimately has responsibility for
disposing an object reference.
Object Reference Mapping in C++
Every interface specified in the IDL maps to a C++ class. This class is implemented
as per specification and deployed on some server. This means that the
implementation is hosted in a server. This implementation or component can be
provided with a name, and the ORB can be notified of an object’s initialization.
This is accomplished in the boa.impl_is_ready() method call to the ORB.
The client acquires this object reference in a number of ways. It can acquire an
object reference using a bind() call. It can retrieve an object reference from a
naming service. Where interoperability is an issue, an object reference may be
reconstructed by morphing a stringified object reference to a proper object
reference.
More importantly, object references can be passed around as parameters to
operations. CORBA specifies that an object reference can be mapped to a T_ptr
and a T_var type. The T_ptr is usually a normal C++ pointer type upon which
operations appropriate to a pointer type may be applied. This is generally true
except for pointer arithmetic. Pointer arithmetic cannot be applied to an object
reference pointer, because the arithmetic is local in nature but the pointer that is
being operated on is a pointer to a remote object.
Memory for T_ptrs needs to be allocated. And as with any pointer, memory needs
to be deallocated when the variable is no longer required. In many cases, this leads
to memory leaks where a programmer forgets to deallocate memory. To lessen the
probability of such leaks, CORBA 2.0 specifies the T_var type. The T_var
makes the deallocation a bit painless. This is accomplished by the fact that a
T_var is automatically deallocated when the pointer falls out of scope. However,
the use of the T_var type is optional.
Consider the following IDL (oref.idl):
module objects{
interface object{
readonly attribute long dummyvariable;
};
interface reference{
void setObjectReference(in object anObject);
object getObjectReference();
void getOutObjectReference(out object anobject);
};
};
The intention of this exercise is to show the passing of object references between
clients and servers. The example will particularly focus on the essential aspects of
memory management of object references in CORBA. In the previous interface,
you pass an object reference to and from a client. This demonstrates how object
references can be managed in terms of memory.
The interfaces need to be pre-compiled. The examples will use Orbix 2.3c. The
interface can be compiled with the following command:
prompt$> idl -B -S reference.idl
CORBA does not support distributed memory management. Though the issues
might seem complicated to a novice, the underlying principle is rather simple.
Memory is managed separately for both client and server. This is accomplished
through reference counting. This means that if a copy of an object is made, the
effect is only local. It is conceivable that a server object serves a number of clients.
A consequence of this is that, under CORBA, what a particular client chooses to do
with its object reference has no impact on another client. This eliminates the
overhead of maintaining a global count of the number of object references on the
server. This overhead would conceivably impose a severe strain on the scalability
of the server. The overhead of servicing the kinds of requests made over the
Internet would probably not be possible with an architecture that supports global
reference counts.
The whole framework of CORBA memory management of object references is
based on reference counting. As an architecture that does not support distributed
memory management, this mechanism leads to local management of reference
counts.
There are two interfaces in the previous module: the reference interface and the
object interface. The object interface is operated upon by the reference interface.
Later in the chapter, we shall develop an example implementation that will
demonstrate two things, the first being the rules of passing object references
around. Just like the other IDL types, standard rules apply to memory management
of object references.
Secondly, perhaps more importantly, the example will illustrate the mechanism of
reference counting. The example will have two clients that will interact with the
server in various ways.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Object References as Parameters
An object reference in CORBA maps to a C++ pointer type. Just as with the other
IDL types, care needs to be taken to ensure that the correct semantics of memory
management is used. With object references, there is an added dimension to these
semantics: an incorrect management of object references might lead to a corruption
of object reference count. In theory, this could lead to a premature deletion of the
object reference all together. Subsequent calls on the reference will lead to undefined
behavior. Ultimately, the goal of correct management of object references is to
ensure that the reference count is consistent with the actual reference count.
The IDL pre-compiler for C++ maps interfaces as represented in the following table:
try{
p->bar();
}
..
..
CORBA::release(p);
p = anotherobject_ptr::_duplicate(myinstance);
}
When a new object reference is allocated, the reference count is incremented to one.
This can be accomplished in a number of ways.
On the Server
A server can control the reference count on its end. When a server instantiates a class
to create an object, the reference count on the object is incremented by the number of
instantiations the server has made. Here is the sample code:
CORBA::release(dummyobject);
decrements the reference count by one. If the implementation iterates on T_ptr->_
refCount() and releases all the object references, the object itself will be
deallocated. Code such as the following ensures that the object is correctly disposed
of when no longer needed:
Warning:
The T_ptr is Orbix-specific. Most ORB implementations have such an API—for
example, T_ptr->_ref_count() under VisiBroker. This API is not
CORBA-compliant, so if you use it, isolate the code somewhere so it can be
replaced easily if such an API does become CORBA-compliant.
On the Client
When the client does a connect to an object reference using the naming service to
initialize the object reference, the reference count goes up by one. Here, the client is
using an Orbix bind call to retrieve an object reference. This increases the reference
count by one:
objectRef = objects::object::_bind(“Object:ORefManager”);
Previous Table of Contents Next
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
On the Server and the Client
If an implementation needs to make a copy of an object reference, it calls
T_ptr::_duplicate(t_ptr) on the object reference. Calling the duplicate(t_ptr)
increments the reference count by one. This means that every time _duplicate() is called, the
reference count goes up by one. A common mistake is to assume that no memory leaks occur when the
server exits. This is correct if the reference count is zero for the object, but if the reference count is greater
than zero, the destructor for the object will not be called.
The following example illustrates how _duplicate() can be used:
The primary difference between IONA’s implementation of the T_ptr type and the T_var type is that
the former is just an alias for a pointer type. Here is an example:
namespace objects{
public:
reference_i(const char* marker,object_ptr);
~reference_i();
private:
object_ptr local_object; //Local instance of object
};
};
This header file lists two classes that correspond to the two interfaces specified in oref.idl. As an
extension of the default mappings provided, we made some changes. First, we added a constructor and a
destructor to both the classes. In fact, the constructor in the reference_I class is overloaded to take a
parameter:
objects::object_i::~object_i(){
cout<<”object_i dtor called”<<endl;
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The destructor will not be called if the reference count is more than zero. Over the course of this object’s
execution, the reference count should be more than one. These are released in the reference’s distructor.
When the count is reduced to zero, this method, shown in Listing 4.2, will be invoked.
Listing 4.2 Implementing the Object Module
local_object = objects::object::_duplicate(anObject);
cout<<”setObjectReference count is now: “<<
local_object->_refCount()<<endl;
}
objects::object_var temp_ref =
objects::object::_duplicate(local_object);
cout<<”getObjectReference count is now: “<<
local_object->_refCount()<<endl;
return temp_ref;
}
objects::reference_i::~reference_i(){
The server should have only one instance of the reference object. This means that the moment the release
method is invoked on it, this destructor should be called. The reference’s destructor releases all the
reference count on the objects::object reference, ensuring that the object is correctly destroyed
(see Listing 4.3).
Listing 4.3 The Server Implementation
objects::object_var dummyobject =
new objects::object_i(“Object”);
cout<<”Object initialized..”<<endl;
objects::reference_var referenceobject =
new objects::reference_i(“Reference”,dummyobject);
cout<<”Reference initialized..”<<endl;
try{
CORBA::Orbix.impl_is_ready(“ORefManager”);
}
catch (CORBA::SystemException &sysEx) {
cerr << “Unexpected system exception” << endl;
cerr << &sysEx;
exit(1);
}
catch (…) {
cout << “Unexpected exception” << endl;
exit(1);
}
cout << “server exiting” << endl;
return 0;
}
The two objects are constructed with a marker. The server will shut down after the default timeout under
Orbix when it should become apparent that the appropriate destructor has been called.
objects::object_var objectRef;
objects::reference_var referenceRef;
try {
objectRef = objects::object::_bind(“Object:ORefManager”);
referenceRef =
objects::reference::_bind(“Reference:ORefManager”);
try{
cout<<”Object ref count: “<<objectRef->_refCount()<<endl;
cout<<”Reference ref count: “<<
referenceRef->_refCount()<<endl;
cout<<”Count on the object server: “<<
objectRef->dummyvariable()<<endl;
referenceRef->setObjectReference(objectRef);
objects::object_var tempRef =
referenceRef->getObjectReference();
cout<<”Object ref count after getObjectReference: “
<<objectRef->_refCount()<<endl;
CORBA::release(tempRef);
referenceRef->getOutObjectReference(tempRef);
cout<<”Object ref count after getOutObjectReference: “
<<objectRef->_refCount()<<endl;
}
catch(…){}
cout<<”Object ref count before exiting: “<<
objectRef->_refCount()<<endl;
return 0;
}
Summary
CORBA does not support distributed memory management. This is true for both the basic types as well as
object references. Nevertheless, CORBA specifies the exact semantics of what happens when object
references are passed as parameters and/or returns. Memory is managed via the mechanism of reference
counts. It should be noted that reference count APIs are not CORBA-complaint but APIs introduced by
the vendors.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 5
IDL/Java Mapping
In This Chapter
• Reserved Names in Java IDL Mapping
• Mapping of IDL Types to Java
• IDL Module
• IDL Interfaces
• typedefs and Constants
• IDL Operations
• Using Holder Classes
• Operations with Native IDL Types
• Operations with Constructed IDL Types
• IDL Exceptions
The OMG ratified the IDL-to-Java mapping specifications on June 24, 1997. I
could call the mapping “unintuitive,” because some aspects of IDL do not map
cleanly to Java. But this would not be entirely correct. Most of the IDL maps
almost keyword for keyword, except where parameters are concerned. There are
also some other issues where no one-on-one mapping is possible, such as for IDL
typedefs.
Consider the context of this before accusing me of heresy. CORBA is a
cross-platform technology, where Java is not the only game in town. Altering the
IDL to make it more Java-friendly would result in issues dealing with backward
compatibility with other languages. The IDL is the foundation on which CORBA
implementations in different languages talk to each other. To restructure the IDL
to make it more intuitive for Java would have meant making it unintuitive for
other languages.
Warning:
There are some fundamental differences between the range of values of Java
types and IDL types. Problems can occur when the range of an IDL type is less
than that of Java types. An in value parameter is checked during runtime. This
is particularly important, because Java does not have support for unsigned types
in Java. An error during this conversion results in either a
CORBA::DATA_CONVERSION exception or a CORBA::MARSHAL exception.
TRUE or
boolean TRUE or FALSE boolean
FALSE
char 8-bit char
wchar 16-bit java.lang.String
octet Invariant 8-bit byte
string java.lang.String
short 16-bit short
unsigned
16-bit short
short
long 32-bit int 32-bit
unsigned
32-bit int 32-bit
long
long long 64-bit long 64-bit
unsigned
64-bit long 64-bit
long
long
float single-precision float
floating number
double double-precision double
floating number
IDL Module
The module denotes a naming scope for a set of IDL interfaces. The module
enables you to classify and group sets of related interfaces within a single scope:
module mappings {
//****************
};
The IDL module maps onto a Java package of the same name. Everything
specified in the module scope maps to associated classes and interfaces within this
package:
package mappings;
Module specifications can occur more than one time within a single IDL
definition. This is possible as long as each module consists of different types. In
the following example:
module com {
module mcp {
module library {
};
};
};
maps to
module com{
module mcp{
module library{
interface catalog{
};
};
};
};
maps to
package com.mcp.library;
public interface catalog extends org.omg.CORBA.Object {
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Internals of Interfaces
The interfaces defined correspond to the classes identified in an object model. The classes
defined in the object model define methods with varied visibilities. By this I mean that methods
that are defined in a class can have private, protected, or public visibility. The IDL interfaces
expose the public part of the object model. Within this scope, an interface contains the
following constructions:
• Attributes—The attributes defined in an interface correspond to the instance variables
in a class. The IDL pre-compiler generates accessor and modifier methods for the
attributes defined.
Remember to add support for the attributes defined in an interface, because most
compilers do not generate the declarations of the variable in the classes.
• Syntax Modifiers—The readonly attribute modifier causes the IDL pre-compiler to
generate only the accessor/get function.
The attribute generates the public interfaces for the instance to the instance variables. A
properly designed class results in a manageable number of attributes.
The following interface:
module com{
module mcp{
module library{
interface catalog{
};
};
};
};
generates the following Java interfaces:
package com.mcp.library;
public interface catalog extends org.omg.CORBA.Object {
struct Types
IDL struct maps to a final Java class. A Java class that is defined as final cannot be
subclassed. This class will contain declarations for the members of the struct as public
instance variables of the class. The class will also contain two constructors. The default
constructor contains initialization routines for the entire instance variable. These definitions
initialize the instances with their default value, usually zero or null. The second constructor
provides a parameterized constructor to enable initialization of the struct with external
values.
A struct can be passed as a parameter in an interface operation. Returns on an IDL operation
can also be a struct. Consider the following struct:
module com{
module mcp{
module library{
interface catalog{
struct name{
string first;
string middle;
string last;
};
};
};
};
};
This interface will generate the following class:
package com.mcp.library.catalogPackage;
Enumeration Type
When a set of values is required, it is appropriate to use an enum type. This enables you to
group related values and assign names to them. As with the constructed type, the IDL maps to a
Java final class.
The following IDL:
module com{
module mcp{
module library{
interface Calender{
enum Days{Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
Days getToday(); };
};
};
};
generates the following final class (partial listing) for the constructed type:
package com.mcp.library.CalenderPackage;
…
}
The interface itself maps the operation to the following code:
package com.mcp.library;
public interface Calender extends org.omg.CORBA.Object {
public com.mcp.library.CalenderPackage.Days getToday();
}
The implementation can then directly use the static member variables.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Union Type
module com{
module mcp{
module library{
interface menu{};
interface Dietician{
enum Days{Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
package com.mcp.library.DieticianPackage;
final public class MenuAllocater {
private java.lang.Object _object;
private com.mcp.library.DieticianPackage.Days _disc;
public MenuAllocater() {
}
public com.mcp.library.DieticianPackage.Days
discriminator() {
return _disc;
}
public void
MondayMenu(com.mcp.library.DieticianPackage.Days disc,
com.mcp.library.menu value) {
_disc = disc;
_object = value;
}
…
(Partial Listing…)
Template Types
There are two basic template types in IDL: strings and sequences.
String Type
The string type is the most commonly used complex type in IDL. These are much
easier to handle than the chars are. However, strings do place some extra burden on
the programmer in terms of memory management.
IDL allows the definition of both bound and unbound strings. Be mindful that Java
strings are unbound, which, if not accounted for, could result in conversion error
from IDL string to Java string.
The following IDL defines both bound string and unbound strings:
module com{
module mcp{
module library{
interface catalog{
struct name{
string first;
string middle;
aName last;
};
};
};
};
};
The IDL defines an alias for a string of 20 characters called aName. The struct is
then defined with the alias as the name for one of its members. It is possible to define
the IDL also as the following:
module com{
module mcp{
module library{
interface catalog{
struct name{
string first;
string middle;
string<20> last;
};
};
};
};
};
Both the definitions will result in the following Java class:
package com.mcp.library.catalogPackage;
final public class name {
public java.lang.String first;
public java.lang.String middle;
public java.lang.String last;
public name() {
}
public name(
java.lang.String first,
java.lang.String middle,
java.lang.String last
) {
this.first = first;
this.middle = middle;
this.last = last;
}
}
The generated class uses the original name because Java does not have any support
for typedefs. Because Java does not have this support, the code generation always
maps the typedef to its original name.
The fact that both the bound and the unbound versions generate identical code needs
attention. Remember the unfixed nature of the Java string class. All Java strings are
unbound. Take care when passing a Java string as a bound CORBA.string.
Sequence Types
The sequence is reasonably similar to a one-dimensional array. Like the string, the
sequence can be either bound or unbound. A bound sequence specifies a maximum
number of elements a sequence can contain, and an unbound string has no limit on
the number of elements it can contain.
The following IDL illustrates both a bound and an unbound sequence and their
associated mappings:
module com{
module mcp{
module library{
interface catalog{
struct name{
string first;
string middle;
string last;
};
struct register{
sequence<name,10> somemembers;
sequence<name> allmembers;
};
};
};
};
};
The interface specifies another struct named register, which contains both a
bound and unbound sequence of another struct.
The pre-compiler generates an additional final Java class named register, along
with the Holder and Helper classes for the class:
package com.mcp.library.catalogPackage;
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Array Type
Although a sequence enables the definition of a one-dimensional array of sorts, with an array, definitions of
multidimensional constructions are possible. Arrays of arrays are a little difficult to grasp at first. But when
you master them, they enable you to pass by reference complex collections of data types over IIOP. Just as
with the constructed data type described previously, definitions can be of any kind of arrays. It is possible
to define arrays of structs, arrays of interfaces, basic types, and so forth.
The following IDL generates code almost identical to that of the sequence:
module com{
module mcp{
module library{
interface catalog{
struct name{
string first;
string middle;
string last;
};
package com.mcp.library.catalogPackage;
final public class theWhiteBook {
public com.mcp.library.catalogPackage.name[]
allTheName;
public java.lang.String year;
public theWhiteBook() {
}
public theWhiteBook(
com.mcp.library.catalogPackage.name[] allTheName,
java.lang.String year
) {
this.allTheName = allTheName;
this.year = year;
}
}
An array must be given an alias by a typedef before its use. However, because Java does not have
support for typedef, the Java mapping will contain the real name of the variable instead of the alias.
Although an array seems to do just what a sequence could, there are some differences. They both allow the
passing of “collections” between clients and servers. The main difference is that a sequence can be
variable, but an array is always fixed. This means that the size of a sequence can shrink or grow. Only the
actual length of the sequence is transmitted, whereas an array is always transmitted in its entirety.
Run the following through the VisiBroker idl2java pre-compiler (or any other product that supports Java
mapping):
module com{
module mcp{
module library{
interface catalog{
struct name{
string first;
string middle;
string last;
};
struct register{
sequence<name,10> somemembers;
sequence<name> allmembers;
};
struct theWhiteBook{
theNames allTheName;
string year;
};
register getRegister();
theWhiteBook getWhiteBook();
};
};
};
};
The VisiBroker pre-compiler will generate a file named _example_catalog.java. Rename the file to
something else if required:
package com.mcp.library;
import com.mcp.library.catalogPackage.*;
public class _example_catalog extends com.mcp.library._catalogImplBase {
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The array implementation is found in the getWhiteBook method. The array is defined to
contain 10 elements. The method illustrates that a sequence is more flexible than arrays for
one-dimensional lists. But when multidimensional lists are required or the size of the list is
invariant, the array needs to be used:
try{
for(int i = 0; i < 10; i++){
name temp_ = new name();
temp_.first = “Franklin”;
temp_.middle = “M.”;
temp_.last = new String(new Integer(i).toString());
arrayType.allTheName[i] = new name();
arrayType.allTheName[i].first = temp_.first;
arrayType.allTheName[i].middle = temp_.middle;
arrayType.allTheName[i].last = temp_.last;
}
}
catch(java.lang.ArrayIndexOutOfBoundsException e){
e.printStackTrace();
}
arrayType.year = “1842”;
return arrayType;
}
With the implementation of the Register interface complete, it is required to build a runtime to
host the component. The runtime or server will subclass the DisplayWindow class and use the
GenericServer implemented in the Common Utilities section.
package com.mcp.library;
import org.omg.CORBA.*;
import java.lang.Thread.*;
import com.javau.utils.*;
import java.awt.event.*;
public class SequenceArrayExample extends
DisplayWindow implements DisplayWindowAdapter{
public SeqArray(){
super();
t_SeqArray = new _example_catalog(“Catalog”);
}
The following shows how a client could pass sequences to and from a server:
package com.mcp.library;
import com.mcp.library.catalogPackage.*;
import org.omg.CORBA.*;
import java.lang.Thread;
import com.javau.utils.*;
import java.awt.event.*;
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Operations with Constructed IDL Types
The operation userParams() passes the parameter struct in all three directions. Before
looking at the details of implementing the params interface, the IDL pre-compiler generates Holder
classes for all constructed types as well as the interfaces defined in the module. The IDL pre-compiler
will generate two Holder classes for the params interface; the paramsHolder.java contains the
implementation of the interface Holder class. The source file parameterHolder.java contains
the mapping for the parameter struct to a Holder class. This is the class that is defined in the
Java mapping of the params interface that would allow it to be passed as an argument in an out or
inout operation:
package com.mcp.library.paramsPackage;
final public class parameterHolder implements
org.omg.CORBA.portable.Streamable {
public parameterHolder() {
}
public parameterHolder(
com.mcp.library.paramsPackage.parameter value) {
this.value = value;
}
..
}
The implementation of the Holder class is a final Java class. The content of the argument passed in the
pass-by-reference semantics is mapped to a public instance field. In this case, the content is a struct
(the parameter) which is held in the Holder class.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The params Client Implementation
The client implementation is similar to the example on sequences and arrays. The
initialization and window code is the same. The only difference is in the
RunTest() method:
Here are the complete mappings for Holder classes mapping; the first grid lays out
the mapping for basic IDL types:
Type IN OUT INOUT RETURN
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
IDL Exceptions
Exceptions are an integral part of CORBA. When you are designing a system,
there are two things that are taken into consideration. One tries to figure out how
a solution should work. The other tries to ensure that the solution does not do
what it is not supposed to. Meyers called it “programming by contract.” It
cannot be sufficiently emphasized how important contracts are with distributed
systems. When things that are not supposed to happen do happen with the
server, the client needs notification. This is accomplished through exceptions.
CORBA specifies two types of exceptions:
• CORBA System Exception
The ORB will dispatch these standard exceptions when specified standard
exceptions occur. These exceptions map to a final Java class named
org.omg.CORBA.SystemException. This class is inherited from
java.lang.RuntimeException. Implementers should ensure that
this exception is handled on every remote invocation.
• User-Defined Exception
CORBA enables a designer to construct application-specific exceptions.
These exceptions can be attached to any operation. When an invariant
condition is violated, these exceptions can be thrown. This would allow
the client to trap these violations and handle them correctly and safely.
User-defined exceptions also map to a final Java class. They are derived from
org.omg. CORBA.UserException, which is a subclass of
java.lang.Exception. User exceptions are mapped to the scope in which
they were defined in IDL. If, for example, an exception is defined in an
interface, the Java exception class will be contained within the same scope.
The following IDL generates a final class name Contract. This class
contains the mapping for the user-defined exception:
module com{
module mcp{
module library{
interface Contract{
const short MaleLifeExpectancy = 76;
exception violation{string condition;
short code;};
void checkInvariant(in short age) raises
(violation);
};
};
};
};
On the server side, using the exception can be as follows:
try{
contractRef.checkInvariant(77)
}
catch(ContractPackage.violation v){
System.out.prinln(e.toString());
}
catch(CORBA.SystemException e){
e.printStackTrace();
}
Summary
The IDL-to-Java mappings are consistent with other mappings OMG has
produced. The mapping ensures that CORBA retains its language neutrality.
Aspects of the Java language and its lack of pass-by-reference semantics lead to
a certain complexity to the mapping.
All except a few elements of IDL map to Java. IDL modules map to Java
packages. IDL interfaces map to Java interfaces. This interface needs to be
implemented to construct a CORBA component in Java. IDL enables the
specification of complex types and constructed types. Both these elements map
to Java final classes.
IDL operations map to methods on the interface that a class has to implement.
Parameters passed as arguments introduce a certain artificiality to the Java
mapping. Java does not have a pass-by-reference semantics, so it specifies the
concept of Holder classes. These classes allow passing parameters as out
and/or inout parameters. IDL exceptions integrate naturally with Java’s native
exception handling capability. Dealing with Holder classes is a small price to
pay, considering the transparency of Java memory management. It is something
to be appreciated—especially if you have a choice between Java and C++
implementations.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 6
CORBA C++ Memory Management
In This Chapter
• IDL Operations
• Ownership of Memory
• Memory Management for Strings
• Client-Side Memory Management for Strings
• Memory Management for Sequences
• Memory Management for Arrays
• Summary of C++ Parameter Types
• Memory Management Heuristics
C++ CORBA implementations are the most popular. This popularity stems
from the expressive power of the C++ language. CORBA can be found in the
most hard-to-reach places, such as telephone switching systems and industrial
process controllers. Most of these environments have a premium on resources.
CORBA is also found in environments requiring complex integration of
diverse technologies, including legacy systems. In these environments, it is
prudent to opt for a solution that imposes the least overhead during runtime.
C++ is a good choice in these situations, because designers and implementers
of C++ systems have the freedom to ensure that the language imposes as much
or as little on their system as they choose.
There is nevertheless a price to be paid for this freedom and flexibility. C++
memory management, although simpler than C, imposes a higher
responsibility on the designer and implementer. A carelessly designed and
implemented C++ CORBA application, ignoring the conventional practices on
safe memory management, is guaranteed to fail.
Issues of memory management affect all levels of the C++ IDL mappings.
These issues range from the management of object references to the allocation
and deallocation of memory for parameters. At the implementation level,
especially dealing with complex IDL types such as IDL structs or
sequences, these issues present a certain complex subtlety that can be
confusing at times. This chapter deals primarily with the issues of memory
management pertaining to parameter passing.
IDL Operations
Operations specified in IDL map to C++ member functions. These functions
are implemented by a C++ class to enable a C++ component to provide some
service.
An example of an IDL operation would be the following:
Basic IDL types do not map to any C++ pointers. Therefore, the issue of
memory management does not have to be dealt with.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Memory Management for Strings
IDL strings can be either bounded or unbounded. In both cases, IDL strings map to
char* in C++. The data in the String is null-terminated. The CORBA module
defines a class called String_var that encapsulates a char* value. An instance of
the class dynamically deallocates memory for the pointer when the String_var
destructor executes.
Issues of memory management issues relating to strings are described using the
following IDL interface operation:
CORBA specifies T_var types that are helper classes. These classes encapsulate the
underlying pointer. When called, the T_var class destructor deallocates memory for
the pointer. CORBA also specifies a helper class for CORBA::string called
CORBA::String_var. Usage of this type eases memory management. Because
memory for the pointer is deallocated when the destructor is called, it helps prevent
memory leaks.
Because CORBA::String_var is responsible for deallocation of memory, it should
be used only in the context of clearly defined ownership of string memory. It should not
be used if ownership cannot be ensured. The class defines a copy constructor and
operator=() that make deep copies.
Warning:
Be careful that you do not initialize a String_var with a string literal. The
String_var cannot gain ownership of the literal; hence it cannot deallocate it. The
runtime behavior is undefined. However, given enough time, Murphy’s law will kick in
and cause a runtime exception.
The three parameters passed into the stringmap operation will help demonstrate the
essentials of string memory management:
Managing an in String
The client retains ownership of the memory allocated for the in string. The server can
treat it only as read-only memory. It has access to the memory for the duration of the
call, after which the ORB will deallocate it. If local copy is to be maintained,
CORBA::String_dup() must be used to keep a copy of the string.
//OUT Parameter
outString = CORBA::string_dup(“Out Parameter”);
//INOUT Parameter
cout<<inoutString<<endl;
CORBA::string_free(inoutString);
inoutString = CORBA::string_dup(
“inout from the server”);
//Return type
char* ret_string = CORBA::string_dup(“Return String”);
return ret_string;
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Memory Management for Sequences
IDL sequences map to a class that essentially behaves like an array. The only difference
is that a sequence can be fixed or unbounded. When fixed (or bound), it has an upper
bound and a lower bound. The upper bound is the maximum length a sequence can
have, and the lower bound is the actual length an implementation initializes the
sequence to. When unbounded, the size of the sequence can be changed.
The way a sequence is constructed has implications on how the memory for the
sequence is deallocated. A Boolean flag can be set on one of the overloaded
constructors that indicates whether a sequence’s buffer space is to be deallocated when
the sequence is deleted.
The two constructors in question are
class sequence {
..
..
..
sequence(CORBA::Ulong maximum);
sequence(CORBA::Ulong maximum, CORBA::Ulong
initial_length, CORBA::Long* datum,
CORBA::Boolean release = 0);
..
..
..
}
The first constructor listed has the release flag set to 1 by default. This means that
destroying the sequence will release the memory of the sequence buffer.
The second one allows construction of a sequence that should have its data buffers
explicitly deallocated. Depending on which constructor is used to create a sequence,
memory management issues become slightly different.
This section deals only with memory issues for unbounded sequences, because memory
management for unbounded sequences is determined at runtime as opposed to
compile-time, the way it is for bounded sequences. The following interface illustrates
the principles of sequence memory management:
module Mapper{
interface Memory{
typedef sequence<long> longsequence;
longsequence sequencemap(
in longsequence inSeq,
out longsequence outSeq,
inout longsequence inoutSeq);
};
};
The interface will use an unbounded sequence of long as parameters and the return
type.
Managing an in Sequence
The client owns the memory that is passed for a sequence from the client to the server.
The memory on the server is read-only. The server has access to this memory only for
the duration of the call.
As such, there are no major implications for memory management for passing a
sequence as an IN parameter. The argument is passed as a reference to a C++ object.
As with any out parameter, the server is responsible for allocating the memory and
passes the pointer back to the client. The ORB that manages the underlying copy of the
pointer from the server to the client makes this transparent. The client is responsible for
deallocating the memory that the ORB makes available on the client side. This can be
made painless if a T_var type is used that deallocates the memory when its destructor
is called.
With unbounded sequences, things are a little more complex. If the length of the
sequence is set to a length larger than the current length, this might cause a reallocation
of the sequence data. Reallocation is similar to copying the contents of the old sequence
into a new one. With return types, the client is responsible for deallocating the
sequence returned by the server.
If the release flag is set to 1 when constructing the sequence, the server object receives
a pointer to the sequence object. The memory for this pointer must be explicitly deleted
when no longer required. This deallocation will cause the buffer to be deleted as well.
Using a T_var type will make this issue a bit more transparent. T_vars are smart
pointers that automatically deallocate memory when no longer referenced.
If the release flag is set to 0, the client is responsible for deallocating the memory for
the sequence. However, the server object retains the responsibility for deallocating the
buffer explicitly.
When the release flag is set to 0, an increase in the length of the buffer holding the
sequence will not mean that the old buffer is deallocated when the sequence is assigned
to the new buffer. When the client and the server objects are in the same address space,
it is the client’s responsibility to ensure that the buffer is deallocated after the sequence
is deleted.
If the release flag is set to 1, the server object receives a read/write sequence, which it
can change if required. Changing the length of the buffer will implicitly reallocate a
new buffer and destroy the old one.
Listings 6.2 and 6.3 show memory management of sequences on the server and the
client side, respectively.
Listing 6.2 Sequence Memory Management on the Server Side
Mapper::Memory::longsequence* Mapper::Memory_i::sequencemap
(const Mapper::Memory::longsequence& inSeq,
Mapper::Memory::longsequence*& outSeq,
Mapper::Memory::longsequence& inoutSeq,
CORBA::Environment &IT_env) {
cout<<”IN SEQUENCE”<<endl;
for(CORBA::ULong i = 0; i < inSeq.length(); i++){
cout<<inSeq[i]<<endl;
}
cout<<”INOUT SEQ”<<endl;
for(CORBA::ULong k = 0; k < inoutSeq.length(); k++){
cout<<inoutSeq[k]<<endl;
inoutSeq[k] = k +10;
}
try{
Mapper::Memory::longsequence inSeq(10);
inSeq.length(10);
for(CORBA::ULong i = 0; i < inSeq.length(); i++){
inSeq[i] = i+10;
}
Mapper::Memory::longsequence* outSeq;
Mapper::Memory::longsequence inoutSeq(5);
inSeq.length(5);
for(CORBA::ULong j=0;j<inoutSeq.length(); j++){
inoutSeq[j] = i+11;
}
Mapper::Memory::longsequence* retSeq;
retSeq = memoryRef->sequencemap
(inSeq,outSeq,inoutSeq);
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Memory Management for Arrays
IDL arrays are identical to C++ arrays. As with other IDL types, the
pre-compiler also generates an associative T_var type to make memory
management transparent. If the T_var generated for the array is not used, the
following methods should be used to allocate and deallocate an array:
Managing an in Array
The client retains ownership of this memory for the array. The server can treat
the memory only as read-only. It has access to the memory for the duration of
the call, after which the ORB will deallocate it. Listing 6.4 shows memory
management on the server side, and Listing 6.5 shows memory management on
the client side.
Listing 6.4 Array Memory Management on the Server Side
Mapper::Memory::anArray_slice* Mapper::Memory_i::
sequencemap (const Mapper::Memory::anArray inArray,
Mapper::Memory::anArray outArray,
Mapper::Memory::anArray inoutArray, CORBA::Environment
&IT_env) {
cout<<”IN Array”<<endl;
int i = 0;
for(i ; i < 5; i++){
cout<<inArray[i]<<endl;
}
i = 0;
cout<<”INOUT Array”<<endl;
for(i; i < 5; i++){
cout<<inoutArray[i]<<endl;
inoutArray[i] = i + 10;
}
i = 0;
try{
Mapper::Memory::anArray inArray,
outArray,
inoutArray;
Mapper::Memory::anArray_slice* returnArray;
int i = 0;
for(i ; i < 5; i++){
inArray[i] = i + 2;
inoutArray[i] = i + 3;
}
i = 0;
returnArray = memoryRef->sequencemap
(inArray,outArray,inoutArray);
for(i; i<5; i++){
long x = returnArray[i];
cout<<”INOUT “ <<inoutArray[i]<<endl;
cout<<”RETURN “<<returnArray[i]<<endl;
}
Mapper::Memory::anArray_free(returnArray);
}
catch (CORBA::SystemException &sysEx) {
cerr << “Unexpected system exception” << endl;
cerr << &sysEx;
exit(1);
}
Fixed Struct CA CD C A,
Fixed Union CD SA D
Variable Struct CA CD CA
Variable Union CD SA CD
String CA CD C/S A
Wstring CD SA C/S D
Sequence CA CD Release
CD SA Flag
Depended
Fixed Array CA {C D CA
CD S A}Rets CD
CA
CD
Variable Array CA CD CA
CD SA CD
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The following table lists the types that are generated from IDL, depending on what is
specified. Remember that the heuristics apply only to parameters that are passed as a
pointer either to a type or to a reference to a pointer to a type.
Type in inout out return
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Part III
Common Services
In This Part
• The Naming Service
• The Trader Service
• The Event Service
• The Transaction Service
• The Security Service
Chapter 7
The Naming Service
In This Chapter
• Why Use the Naming Service?
• The Naming Service API
• Using the Naming Service
• Implementing the Hangar
• The Hangar Server Implementation
• The Hangar Client Implementation
One of the most important aspects of CORBA is the fact that the OMG
explicitly set out to build a modular architecture. The implicit goal of the
OMG is not IIOP and IDL or, for that matter, CORBAServices, but something
far more grand. IIOP and IDL can be considered as the plumbing. The true
goal of OMG is to have a solid foundation on which to build the Common
Object Facilities—or, in the common tongue, a platform for building and
deploying Common Business Objects.
We are not there yet. As an old friend once told me, “Let the world wake up to
objects; then perhaps we can think about distributing them...” Though my view
is not as pessimistic as his, there is some truth to it.
The role of CORBAServices is simple. If IIOP and IDL are the plumbing,
CORBAServices is the mortar that will hold the house together—the house
OMG wants you to build. These services enable complex distributed systems
to be built. The great thing about them is that, for the most part, the services
themselves are very simple to understand and use.
This section of the book, beginning with this chapter and concluding with
Chapter 11, “The Security Service,” concentrates on the services that are
commercially available. Although OMG has specified over 20 services, no one
has been able to make a commercial argument for building all of them. In time,
when all the world is a web of distributed objects, all these services will be
available.
The following are services that are currently available with varying level of
compliance:
• The Naming Service—It can be considered the White Pages service
for distributed CORBA objects. Here is simple advice about the Naming
Service: Don’t leave home without one. As a service, it enables
applications to find objects by name.
• The Trader Service—It can be thought of as the Yellow Pages of
CORBA. As yet, there are only a few mature implementations of this
service. It is my opinion that, over time, this service is going to be the
cornerstone of many CORBA applications. As a Yellow Pages service,
the Trader Service enables the classification of similar services.
• The Event Service—This is by far one of the most useful, albeit
underutilized, services out there. The Event Service enables an almost
complete decoupling of the server from the client.
• The Lifecycle Service—The CORBA Lifecycle Service was one of
the first services that was standardized. Unfortunately, there are not that
many commercial implementations out there. The service enables the
transparent creation and destruction (among other things) of object
references. It is extremely handy in applications that use an inordinate
number of remote objects.
• The Transaction Service—Transactions are said to be the lifeblood of
commerce. Nothing much works without a robust and resilient
implementation of this service. CORBA provides an object-oriented
interface to XA-based transaction services. XA is a standard protocol
defined by the X/Open consortium. XA specifies a mechanism for open
distributed transactions.
• The Security Service—In this age of the Internet, security is a hot
issue. CORBA security pushes the envelop. This is particularly true
when considering the fact that CORBA, in essence, is a peer-to-peer
technology. Client/server roles are arbitrary and transient. If you
consider a world with a billion objects distributed over the Web,
security gets a whole new dimension.
The CORBA Naming Service is a most useful service. It enables an
application to locate an object reference by name. This utility is welcome
when you consider the fact that any self-respecting application would probably
have tens of hundreds of objects spread over a number of hosts.
Tip:
Ideally, the Naming Service holds the names only for key object references.
The rest of the objects are passed around as arguments to operations. This
significantly reduces the communications overhead.
What Is in a Name?
struct NameComponent{
string id;
string kind;
};
The name of a component is held in the NameComponent.id. This is the
name that is used to find a name, for instance. The Namecomponent.id is
useful for providing auxiliary information about the component itself. From
the struct, it is but a small step to define a name:
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
An airline company has a number of hangars spread across the world. For simplicity,
let’s say that the Naming Service is available only to the hangars in the U.S. In the U.S.,
the company has four hangars: Los Angeles, New York, Chicago, and Denver. Aircraft
are constantly being moved from one hangar to another. This requires the client to
acquire multiple Hangar object references and operate on them.
The organization of a Naming hierarchy revolves around something called a
NamingContext. A NamingContext enables the organization of names. The
NameServer is the root NamingContext. Everything else is attached to this
context.
If SOAR is to use the Naming Service, its services have to be organized around
NamingContexts. NamingContexts have names attached to them. The first
NamingContext is SOAR. The SOAR NamingContext has four subcontexts:
Denver, Chicago, Los Angeles, and New York. Under each geographical region are
further NamingContexts associated with the various services found in that particular
region. For our purposes, only Chicago and New York have an operations service.
Therefore, the aforementioned NamingContext has the operations
NamingContext. Finally, there are simple names that are attached to the operations
NamingContext: NYOperation and ChicagoOperation have NYHangar and
ChicagoHangar attached to them.
A pictorial representation of this hierarchy is shown in Figure 7.1.
Creating Contexts
Sane applications will always use the NamingContext to relate names in some
ordered structure. There are two APIs for creating the context:
NamingContext new_context();
NamingContext bind_new_context(in Name name)..;
The first operation enables the creation of a NamingContext. The bind_context
then is called to attach a context to a name. The next operation, bind_new_context,
enables the creation of the NamingContext and binding it to a name.
Binding Names
There are two principal APIs for binding names to a Naming Service:
Warning:
Names have to be unique to a NamingContext. If a name is already in use, the
AlreadyBound exception is thrown.
The following are two additional APIs provided by the Naming Service:
void destroy()..
The operation will raise a NotEmpty exception if names are still attached to the
context. If so, repeat the call to remove a name.
Resolving Names
interface Hangar{
struct aircraft{
long id;
};
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Implementing the Hangar Interface
The hangar header file is modified to the extent required by IONA. The hangar itself is a simple array of
Aircraft structs:
#ifndef Hangar_ih
#define Hangar_ih
#include “Hangar.hh”
nclude <iostream.h>
#include “Hangar.hpp”
Hangar_i::Hangar_i():free(10){
}
Hangar_i::~Hangar_i(){
}
Hangar::aircraft temp_;
if(id_ < 10)
temp_.id = this->capacity[id_].id;
free—;
else
throw CORBA::SystemException();
return temp_;
}
The implementation simply assigns an incoming argument to the array if addNewAircraft is called. The
hangars themselves have a capacity for ten aircraft. This is taken into account.
When retrieveAndDeleteAircraft() is called, the appropriate aircraft is returned to the caller, and
the capacity variable is decremented.
#include <iostream.h>
#include “NamingService.hh”
#include “Hangar.hpp”
#define USE_INIT
CosNaming::NamingContext_var
soarContext,regionContext,operationsContext;
CosNaming::NamingContext_var rootContext;
CosNaming::Name_var soar;
CORBA::Object_var object_;
try{
try{
soar = new CosNaming::Name(1);
soar->length(1);
soar[(CORBA::ULong)0].id = CORBA::string_dup(“Super Orb”);
soar[(CORBA::ULong)0].kind = CORBA::string_dup(“”);
CosNaming::Name_var operations = new CosNaming::Name(1);
operations->length(1);
operations[(CORBA::ULong)0].id =
CORBA::string_dup(“Operations”);
operations[(CORBA::ULong)0].kind = CORBA::string_dup(“”);
soarContext = rootContext->bind_new_context(soar);
operationsContext = soarContext->bind_new_context(operations);
regionContext = operationsContext->bind_new_context(region);
CORBA::Orbix.impl_is_ready(“SOAR”);
}
object_ = orb->resolve_initial_references(“NS”);
A simple name is contructed. The Name’s ID is “Super Orb”; the kind is left blank. Names are
constructed in the following manner:
soar->length(1);
soar[(CORBA::ULong)0].id = CORBA::string_dup(“Super Orb”);
soar[(CORBA::ULong)0].kind = CORBA::string_dup(“”);
From that point, three further names are constructed: Operations, New York, and NYHangar. When all
the names are constructed, they are bound to the context to create new contexts. The soarContext is
bound to the rootContext. On the soarContext, the operationsContext is bound. Finally the
last context, the regionContext, is bound to the operationsContext. The bind operations are
carried out in the following code:
soarContext = rootContext->bind_new_context(soar);
operationsContext = soarContext->bind_new_context(operations);
regionContext = operationsContext->bind_new_context(region);
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The last step is to bind a name pointing to the appropriate context. This is to be the Hangar
object reference. Its name, hangar_name, is bound to the regionsContext. The
construction of the object reference and the bind of the object is shown in the following:
#include <iostream.h>
#include “NamingService.hh”
#include “Hangar.hh”
#define USE_INIT
CosNaming::NamingContext_var rootContext;
CosNaming::Name_var soar;
Hangar_var theHangar;
CORBA::Object_var object_;
try{
try{
soar = new CosNaming::Name(4);
soar->length(4);
soar[(CORBA::ULong)0].id = CORBA::string_dup(“Super Orb”);
soar[(CORBA::ULong)0].kind = CORBA::string_dup(“”);
soar[(CORBA::ULong)1].id = CORBA::string_dup(“Operations”);
soar[(CORBA::ULong)1].kind = CORBA::string_dup(“”);
soar[(CORBA::ULong)2].id = CORBA::string_dup(“New York”);
soar[(CORBA::ULong)2].kind = CORBA::string_dup(“”);
soar[(CORBA::ULong)3].id = CORBA::string_dup(“NYHangar”);
soar[(CORBA::ULong)3].kind = CORBA::string_dup(“”);
object_ = rootContext->resolve(soar);
theHangar = Hangar::_narrow(object_);
}
catch (CosNaming::NamingContext::NotFound &) {
cerr << “CosNaming::NamingContext::NotFound” << endl;
exit(1);
}
catch (CosNaming::NamingContext::CannotProceed &) {
cerr << “CosNaming::NamingContext::CannotProceed” << endl;
exit(1);
}
catch (CosNaming::NamingContext::InvalidName &) {
cerr << “CosNaming::NamingContext::InvalidName” << endl;
exit(1);
}
catch (CosNaming::NamingContext::AlreadyBound &) {
cerr << “CosNaming::NamingContext::AlreadyBound” << endl;
exit(1);
}
catch (CosNaming::NamingContext::NotEmpty &) {
cerr << “CosNaming::NamingContext::NotEmpty” << endl;
exit(1);
}
catch (CORBA::SystemException &sysEx) {
cerr << “Unexpected system exception” << endl;
cerr << &sysEx;
exit(1);
}
catch (...) {
cout << “Unexpected exception” << endl;
exit(1);
}
}
First, a client has to construct a name for the potential object reference. In this case, the soar
name is constructed. It is a compound name that has four elements. The last one is the name of the
object reference that is required by the client. The construction of the name is shown in the
following code:
object_ = rootContext->resolve(soar);
theHangar = Hangar::_narrow(object_);
With the object reference in hand, it is now possible to make invocations on the object reference.
Summary
The Name Service is a critical CORBA service. It enables objects to be transparently distributed,
irrespective of host or location. It is a service that has a very simple API. As such, it is not a very
complicated service to use. As a service, it is sort of a White Pages directory. It can be used to
locate objects by name.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 8
The Trader Service
In This Chapter
• Trader Service Interaction
• Design Rationale for the Trader Service
• Main Components of the Trader Service
• State of CORBA Trader Implementations
• The CUTrader Implementation
• Using the CUTrader
• The CUTrader Importer
The Trader Service is very similar to the Naming Service. Applications can
register their objects with the service, which acts as a locator service for a client
requiring some service. For example, a banking application client can require the
services of a payment server. In that case, the client can use the Naming Service
or the Trader Service to locate this service on the network. However, there is one
important distinction between the Trader Service and the Naming Service.
Although the Naming Service acts as a simple White Pages service, by which a
client can locate an object by name, the Trader Service acts as a Yellow Pages
service. A Trader Service could offer many different types of payment objects,
all of them classified under the general category “payments.” The client needs to
figure out the payment object that best suits its requirements.
This enables the client to request the service by a type or by an offer. When a
request is made, the Trader searches its directory for an object that matches the
requirements of the client. When such an object is found, its object reference is
returned to the client.
This interaction is very much that like of a trading operation between a merchant
and a customer. The customer wants something that the merchant has in the
store, and the trade is done on monetary terms. The Trader Service enables such
trading to be accomplished using user-defined Quality of Service criteria.
struct Property{
PropertyName name;
PropertyValue value;
};
struct offer{
Object reference;
PropertySeq properties;
};
struct OfferInfo{
Object reference;
ServiceTypeName type;
PropertySeq properties;
};
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
State of CORBA Trader Implementations
Unfortunately, CORBA Trader implementations are only recently becoming available. This
situation will change over the next year or two. The available implementations offer varying
degrees of CORBA compliance. It is only a matter of time before a forward-looking company will
offer a CORBA Trader over the Web. The implementation that follows will use the Trader
implementation from Dais as part of the J2 implementation.
Let’s consider a simple Trader implementation. The example will illustrate the basics of how a
Trader should work.
The CUTrader interface will demonstrate the property concept of the CosTrading module. The
CosTrading module contains the specification for the CORBA Trader Service. In conjunction
with this, it will also show an adaptation of the service-offer interface to enable objects to advertise
their capabilities. A component may register its capabilities or have its registration withdrawn from
the CUTrader implementation. Listing 8.1 shows the CUTrader interface. The CUTrader will
help us demonstrate a minimalist Trader Service.
Listing 8.1 The CUTrader Interface
module CUTrader {
struct Property{
PropertyName name;
PropertyValue value;
};
typedef sequence<Property> PropertySeq;
struct Offer{
Object reference;
PropertySeq properties;
};
};
The core of the CUTrader model is the Register interface. This interface allows the three
operations:
package CUTrader;
import com.objectspace.jgl.*;
public _example_Register() {
super();
}
RepositoryObject returnOffer =
(RepositoryObject)repository.get(temp_);
if(returnOffer == null)
throw new org.omg.CORBA.BAD_TYPECODE();
return returnOffer.getOffer();
}
}
The implementation uses a class named RepositoryObject. It is this object that is stored in the
JGL Hashset. When a client invokes an add() method, it passes an array of properties and a string
identifying the object’s capability. The add() method also has an an object reference argument of
the component that wants to advertise its services. The Offer class has the following definition:
package CUTrader;
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The Offer class defines two public instance variables: a CORBA.Object and an array of
properties. The CORBA.Object refers to the exporter’s reference. The array holds the list of
offer constraints that the exporter was to advertise. Both these are initialized with the parameters
passed in.
The Repository class is necessary for using the JGL Hashset. It has the structure shown in
Listing 8.3.
Listing 8.3 The Repository Class
package CUTrader;
The CUTrader server is a run-of-the-mill CORBA server. The following code should be
self-explanatory:
}
The Register object reference is initialized with the name CUTrader. After this is done, the
basic object adapter is notified of its readiness, and the server goes into a blocked state waiting for
incoming invocations.
A CUTrader Exporter
Essentially, a service that wants to export its capability needs to acquire the Trader’s object
reference and invoke the add() operation on it. It passes the required parameters into add().
These parameters include the name of the service and an array of properties that it would like to
advertise using the Trader.
The CUTrader exporter will use the following interface to implement a skeletal service:
module SOAR{
interface Reservations{
void reserve();
};
};
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Run this IDL through the VisiBroker idl2java pre-compiler. The interface need not be implemented.
This example shows only how the Reservation uses the server to export its capabilities with the
CUTrader. Listing 8.4 shows the server implementation.
Listing 8.4 Using the CUTrader Service
package SOAR;
import CUTrader.*;
boa.obj_is_ready(reservations);
System.out.println(reservations + “ is ready.”);
boa.impl_is_ready();
offer[0].name = “Cost”;
offer[0].value = “$30”;
offer[1].name = “Availability”;
offer[1].value = “24 Hours”;
try{
trader.add(reservations, Service_id, offer);
}
catch(org.omg.CORBA.SystemException e){
System.out.println(e.toString());
}
}
}
When an instance of SOAR.Reservations is instantiated, the implementation calls the
RegisterServer() method. The server has to call the add() method on the CUTrader server. It
has to pass three parameters: a CORBA.Object object reference, an array of properties, and the name
of the service.
The object reference it passes is Reservations. The following is a list of properties:
offer[0].name = “Cost”;
offer[0].value = “$30”;
offer[1].name = “Availability”;
offer[1].value = “24 Hours”;
The Reservations object exports two properties: “Cost” and “Availability”. The first is a
common constraint to use in a trading environment. This instance of the Reservation server charges
“$30” for its services. It further distinguishes itself by specifying its availablity constraint, which is
“24 Hours”.
This enables a potential client to constrain a search by specifying a particular property. For instance, it
can specify that a cost of a service should be less than “$40”. This would, in theory, return the
Reservations object reference from the CUTrader.
package SOAR;
import CUTrader.*;
public class ReservationClient {
criteria[0].name = “Cost”;
criteria[0].value = “$30”;
try{
bestOffer = trader.get(Service_id, criteria);
}
catch(org.omg.CORBA.SystemException e){
System.out.println(e.toString());
}
if(bestOffer != null)
System.out.println(“Got it..”);
else
System.out.println(“Haven’t Got it..”);
}
}
}
The client wants to locate a service by a single constraint. It constructs this constraint by initializing an
array of properties. The array has only one element. This is shown the following code:
criteria[0].name = “Cost”;
criteria[0].value = “$30”;
When this is done, the Property object, along with a string identifying the service, is passed on to the
CUTrader. If an appropiate service is located, this is returned. Otherwise, the CUTrader throws an
exception that is handled.
Summary
The Trader Service could prove to be an extremely useful service. It enables a service to export its
capabilities through a service analogous to the Naming Service. The difference is that the Trader acts as
a Yellow Pages service.
The Trader can maintain a database of properties a service would like to advertise. These properties
could then be used by clients to locate a service by a desirable constraint. These could be a variety of
constraints, both qualities of service criteria as well as technical qualifications.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 9
The Event Service
In This Chapter
• The Power Nine Problem
• Reducing Interface Dependencies
• The Supplier-Driven Event Model
• The Consumer-Driven Event Model
• The Delegated Event Model
• Semantics of Delegation-Based Event Models
• Event Types
• Implementing a CORBAEvents-Based Application
• Implementing Untyped Push Models
• push Event Client Implementation
• Implementing Typed Push Models
• The Typed Event Interface
• Implementing a Typed Push Supplier
• Typed Push Consumer Implementation
Modeling a system on state transitions enables the construction of very flexible
and cohesive systems. The system is loosely coupled because it is designed
with the fewest possible dependencies. Object-orientation is a paradigm that
enables systems with fewer dependencies than those possible with a more
procedural approach. Although object- oriented approaches have banished the
dependencies based on global variables through encapsulation, they have their
own dependencies. In object-oriented approaches, these dependencies are on
the interfaces that objects expose.
Nevertheless, after an interface has been designed, it is a contract that should
not be violated. This means that these interfaces should not change. Objects, in
short, are sensitive to changes in the interface. This is, to a certain extent,
manageable with deskbound systems. Under distributed systems, this
sensitivity could be problematic.
interface PushConsumer{
void push (in any date) raises (Disconnected);
void disconnect_push_consumer();
};
When a supplier has an event to deliver, the supplier invokes the push
operation on the client. The operation takes CORBA::Any as the argument.
The disconnect_push_ consumer() provides an operative mechanism
for the server to disconnect the consumer for some reason. Such needs may
arise for quality-of-service criteria. A server could offer its services only
periodically. When the server needs to be shut down, it could use this method
to disconnect all consumers.
A supplier of push events has to support a simple interface:
interface PushSupplier{
void disconnect_push_supplier();
};
This API provides the consumer of the pushed events with the capability of
informing the event server that it is about to disconnect itself from the
interaction.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The Consumer-Driven Event Model
This model enables the consumer of an event to dictate when an event is
delivered. This is also known as the pull model of event delivery. The model in
terms of its API is very similar to the push model, except that the
responsibility of retrieving the event rests with the consumer. This results in
the reversal of roles, so to speak. The consumer is the client and the producer
of the event is the server. When a supplier supports pull as a mechanism for
supporting event delivery, it needs to support the following interface:
interface PullSupplier{
interface ConsumerAdmin{
ProxyPushSuppler obtain_push_supplier();
ProxyPullSuppler obtain_pull_supplier();
..
};
The supplier and consumer ultimately deal with the proxy object. These two
interfaces enable them to retrieve the appropriate proxy object references held
by the Event Channel:
interface EventChannel{
ConsumerAdmin for_consumer();
ConsumerAdmin for_suppliers();
void destroy();
};
When the reference to EventChannel is obtained, a client can request the
interfaces for the consumer or for the supplier. These interfaces are used to
obtain the required proxy references. Using the proxies, the client can connect
to the required model for event delivery.
Event Types
CORBA supports two types of events: untyped events and typed events. The
interfaces described previously were for untyped events.
Untyped Events
Whether an application is using push or pull models for event delivery, with
untyped events, these operations deliver an Any type. As you may recall, a
CORBA::Any type is essentially a void pointer with an associated typecode
pointer.
An any type can encapsulate any IDL type. One of the consequences of this
feature is the fact that the client must clearly understand the semantics of the
encapsulated type. This means that if the client is excepting a foo type
encapsulated in an Any, the server should not deliver a bar type.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Typed Events
Typed events enable a supplier to deliver IDL interfaces as events to a consumer. It means that any
valid IDL interfaces could be passed as an event using typed events. This is the closest an object
reference–based system such as CORBA can come to a Mobile Agent type of architecture. The
mechanism for events is different, depending upon what model is used to deliver an event.
If the model used is a push model, the event consumer supplies the interface. If the model used is a pull
model, the supplier furnishes an interface. The typed events also enable the construction of typed
Event Channels that deliver typed events.
To implement an OrbixEvents push supplier, the code should accomplish three things:
1. The supplier retrieves a ProxyPushConsumer from the event channel.
2. After the ProxyPushConsumer is retrieved, it invokes the connect_push_
supplier().
3. After the push supplier is connected, the event server can push events into the Event
Channel.
The Event Channel can be obtained in one of three ways. The first mechanism is to obtain an Event
Channel by using the low-level bind() that is usually provided by ORB implementations. Keep in
mind that some of these implementations are not CORBA-compliant. This means that the bind() call
does not usually use IIOP to attach to a server by a vendor-specific protocol. The second mechanism is
to use the Naming Service or the Trader Service. This is a much more flexible manner of advertising
an Event Service channel. The third mechanism is to use the IOR. This mechanism might be used if the
design is to enable interoperability of an Event Channel across multiple ORBs.
With the Event Channel retrieved, the server should invoke the for_suppliers() method on it to
retrieve the SupplierAdmin object reference. The SupplierAdmin supports the following
operations:
interface SupplierAdmin{
ProxyPushSupplier obtain_push_supplier();
ProxyPullSupplier obtain_pull_supplier();
};
Because the application is using the push model, the server invokes the first operation. If the server
implements a pull model, it invokes an obtain_pull_supplier to register its services.
With the SupplierAdmin object reference in hand, the server invokes the
obtain_push_supplier() to retrieve a reference to a ProxyPushSupplier. The
ProxyPushSupplier is the interface that the server uses to connect its supply to the Event
Channel:
interface ProxyPushSupplier:CosEventComm:PushSupplier{
void connect_push_supplier(in CosEventComm:PushSupplier
push_supplier) raises (AlreadyConnected);
};
With the administration of the event server out of the way, the server can then proceed to push events
into the Event Channel, as events become available.
The server implements the following IDL as the event type to pass to the Event Channel. The file
contains a struct that will hold departure details for flights:
Departure.idl
struct DepartureDetail{
string<5> flighno;
string<5> time;
string<1> status;
};
The idea is to have an event server that transmits departure data when this information becomes
available. Compile the preceding structure with the Orbix IDL pre-compiler:
flightserver.hpp
public:
FlightSupplier_i(){
m_connected = 0;
}
};
The disconnect_push_supplier() is the method that is overridden to support the
PushSupplier interface. This method enables the client to disconnect from the server when
required. Calling this method ends the connection to the channel and releases all associated resources.
Listing 9.1 shows the implementation of the untyped event supplier.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Listing 9.1 The Untyped Event Supplier Implementation
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <EventService.hh>
#include “flightserver.hpp”
CORBA::Orbix.bindUsingIIOP(TRUE);
CORBA(Any) event_data;
DepartureDetail departures;
departures.flighno = CORBA::string_dup(“GRN445”);
departures.time = CORBA::string_dup(“01:30”);;
departures.status = CORBA::string_dup(“D”);
CosEventChannelAdmin::EventChannel_var ec;
try{
Channel =
CosEventChannelAdmin::EventChannel::_bind(“Departures:ES”);
CosEventChannelAdmin::SupplierAdmin_var admin_ =
Channel->for_suppliers();
CosEventChannelAdmin::ProxyPushConsumer_var proxy_ =
sadmin->obtain_push_consumer();
FlightSupplier_i DepartureData;
proxy_->connect_push_supplier(&DepartureData);
DepartureData.connected(1);
int count = 1;
while (DepartureData.connected()){
proxy_->push(event_data);
}
proxy_->disconnect_push_consumer();
}
catch (CORBA(SystemException) &sysEx) {
cerr << “”Unexpected System Exception “ << endl;
cerr << &sysEx;
exit(1);
}
catch(...){
cerr << “Unexpected Exception” << endl;
exit(-1);
}
}
Because we are dealing with CORBA::Any type, a user-defined type has to be cast into
CORBA::Any. This is accomplished using the overloaded left-shift operator, which inserts a type into
an Any. First, a departure structure is constructed, and then it is cast into a CORBA::Any. Here is the
relevant code:
DepartureDetail departures;
departures.flighno = CORBA::string_dup(“GRN445”);
departures.time = CORBA::string_dup(“01:30”);;
departures.status = CORBA::string_dup(“D”);
event_data <<= departures;
The Naming Service is the preferred mechanism for acquiring the Event Channel, but this
implementation uses the Orbix-specific bind() method. The departure server binds to the
OrbixEvent server ES:
Channel = CosEventChannelAdmin::EventChannel::_bind(“Departures:ES”);
After the object reference to the Event Service is retrieved, the SupplierAdmin object reference has
to be retrieved from the Event Channel. The SupplierAdmin enables the retrieval of the proxy
object references from the Event Channel:
CosEventChannelAdmin::SupplierAdmin_var Admin_ =
Channel->for_suppliers();
At this point, the ProxyPushConsumer is retrieved from the SupplierAdmin object reference.
ProxyPushConsumer enables the delivery of push events. The code snippet to accomplish this is
shown in the following:
CosEventChannelAdmin::ProxyPushConsumer_var proxy_ =
sadmin->obtain_push_consumer();
The connect_push_supplier() connects the event structure with the ProxyPushConsumer.
As long as the PushSupplier is connected to the Event Channel, the event server invokes push on
the proxy object reference, pushing the DepartureData structure into the channel.
The application should be linked with ES.lib.
Note:
The headers required for the Event Service need to be generated by running the IDL pre-compiler on
EventService.idl.
The EventChannel server is registered with the Orbix implementation repository. When registered,
an Event Channel proxy is initialized with the server. This can be done as follows:
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
push Event Client Implementation
A PushConsumer inherits from the PushConsumer interface. This interface has two operations
to be implemented: The first, the push() operation, is the method through which the
EventChannel communicates with the consumer. The second operation, disconnect_
push_consumer(), enables a client to disconnect the consumer from the channel. Listing 9.2
shows the implementation of the push event client.
Listing 9.2 The push Event Client Implementation
#include “eventClient.hpp”
public:
FlightClient(){
m_connected = 0;
}
DepartureDetail *departures;
DepartureAny >>= departures;
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <EventService.hh>
#include “Eventclient.hpp”
CORBA::Orbix.bindUsingIIOP(TRUE);
CosEventChannelAdmin::EventChannel_var channel_;
try{
channel_ =
CosEventChannelAdmin::EventChannel::_bind(“Departures:ES”);
CosEventChannelAdmin::ConsumerAdmin_var admin_ =
channel_->for_consumers();
CosEventChannelAdmin::ProxyPushSupplier_var proxy_ =
admin_->obtain_push_supplier();
FlightClient DepartureChannel;
proxy_->connect_push_consumer(&DepartureChannel);
DepartureChannel.connected(1);
while(DepartureChannel.connected()){
CORBA::Orbix.processNextEvent();
}
proxy_->disconnect_push_supplier();
delete &DepartureChannel;
}
catch (CORBA::SystemException &sysEx) {
cerr << “Unexpected System Exception “ << endl;
cerr << &sysEx;
exit(1);
}
catch(...){
cout << “Unexpected Exception “ << endl;
exit(-1);
}
return 0;
}
The client has to retrieve the Event Channel to receive events. The implementation uses the
Orbix-specific bind() method to acquire the EventChannel object reference. The Event
Channel is registered as Departure with the Event Service. Here is the code:
channel_ =
CosEventChannelAdmin::EventChannel::_bind(“Departures:ES”);
The for_consumer() method retrieves the ConsumerAdmin object reference for connecting
the consumer with the EventChannel.
The ConsumerAdmin object reference enables the client to refer to the ProxyPushSupplier,
the object reference. This object reference enables the client to establish a connection to the Event
Channel.
CosEventChannelAdmin::ProxyPushSupplier_var proxy_ =
admin_->obtain_push_supplier();
The ProxyPushSupplier object reference can be used by the the Consumer, which connects
to the Event Channel The PushConsumer in this implementation is FlightClient. Here is the
code:
FlightClient DepartureChannel;
proxy_->connect_push_consumer(&DepartureChannel);
After the Consumer is connected, the instance variable indicating whether the connection is alive
is set to true. For the duration of this truth, the implementation loops on this variable, listening for
incoming events.
CORBA::Orbix.processNextEvent() enables an implementation to be sensitive to three
types of events that the ORB interface handles:
• Operations requests
• Connection requests
• Disconnection requests
The event falls into the operations request. Before exiting, the EventConsumer disconnects from
the EventChannel.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The Typed Event Interface
The Event Consumer application uses the following interface to have events delivered to it:
Flight.idl
#include “EventService.idl”
interface Flight:CosTypedEventComm::TypedPushConsumer {
prompt$>idl -B -S -R flight.idl
This implementation must support the operation that needs to be overridden for the
TypedPushConsumer interface. The implementation will be used by the EventConsumer
when it is built.
The header file for the Flight class is shown in Listing 9.4.
Listing 9.4 The Flight Interface Implementation
#include “arrival.hh”
#include <EventService.hh>
class IT_DECLSPEC_arrival Flight_i: public virtual FlightBOAImpl{
Flight_i(){
m_disconnected = 0;
}
public:
virtual void getArrival (const char * flightNo
,const char * time
,const char * status
,CORBA::Environment
&IT_env=CORBA::default_environment);
};
There are two classes that the Flight_i subclasses and overrides: the FlightBOAImpl and
the PushConsumer interface defined in the CosEventComm module. The class implements
the methods defined in the interface. As with the untyped application, the class defines a flag
m_connected, which indicates whether the object is connected to the Typed Event Channel.
The class should implement the getArrival() method:
#include <iostream.h>
#include “arrival.hpp”
#include <iostream.h>
#include “arrival.hh”
#include “EventService.hh”
class ArrivalSupplier_i:public CosEventComm::PushSupplierBOAImpl{
ArrivalSupplier_i(){
m_disconnected = 0;
}
virtual void disconnect_push_supplier(
CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv()){
m_disconnected = 1;
}
char disconnected(){
return m_disconnected;
}
};
The disconnect_push_supplier() is implemented in this code. This enables a client to
disconnect the supplier from the Event Channel and have all associate resources deallocated (see
Listing 9.5).
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Listing 9.5 The Typed Supplier Implementation
#include “typedsupplier.hpp”
CosTypedEventChannelAdmin::TypedEventChannel_var channel_;
CosTypedEventChannelAdmin::TypedSupplierAdmin_var admin_;
CosTypedEventChannelAdmin::TypedProxyPushConsumer_var proxy_;
ArrivalSupplier_i Eventsupplier_;
CORBA::Object_ptr object_;
try{
CORBA(Orbix).bindUsingIIOP(TRUE);
channel_ =
CosTypedEventChannelAdmin::TypedEventChannel::_bind(
“Arrival:typed_es”
);
admin_ = channel_->for_suppliers();
proxy_= admin_->obtain_typed_push_consumer(“Flight”);
proxy_->connect_push_supplier(&Eventsupplier_);
object_ = proxy_->get_typed_consumer();
arrivalData = Flight::_narrow(object_);
}
catch(CosTypedEventChannelAdmin::InterfaceNotSupported &) {
cout << “Interface not supported” << endl;
exit(1);
}
catch(CORBA(SystemException& se)){
cout << “System Exception “ << &se << endl;
exit(1);
}
catch(...){
cout << “Unknown error” << endl;
exit(1);
}
while(!Eventsupplier_.disconnected()){
try{
arrivalData->getArrival(“RF199”,”13:50”,”N”);
}
catch(CORBA(SystemException& se)){
cout << “System Exception: “ << &se << endl;
exit(1);
}
catch(...){
cout << “Unknown exception” << endl; exit(1);
}
}
CORBA::release(object_);
cout << “Disconnecting ...” << endl;
proxy_->disconnect_push_consumer();
}
In this implementation, the Orbix bind() method is used to retrieve the
TypedEventChannel object reference.
With the TypedEventChannel object reference resolved, the administration object
reference for suppliers has to be retrieved. The Admin interface enables the retrieval of the
proxy object. This retrieval is accomplished by invoking the following method on the Event
Channel:
admin_ = channel_->for_suppliers();
The next step is to retrieve the proxy object. The proxy object reference for typed push
consumers is retrieved by invoking the obtain_typed_push_consumer() operation on
the Admin reference. This operation needs a string name of the interface that the supplier
hopes to deliver events. The code snippet for this invocation follows:
proxy_= admin_->obtain_typed_push_consumer(“Flight”);
The implementation then has to register the typed event supplier with the proxy. This enables
proxies to invoke disconnects on the supplier if necessary. The relevant code follows:
proxy_->connect_push_supplier(&Eventsupplier_);
With the supplier registered, the application must retrieve an object reference to the interface it
uses to deliver events. This is accomplished by invoking get_typed_consumer(). The
operation returns a CORBA::Object:
object_ = proxy_->get_typed_consumer();
The CORBA::Object that is retrieved is narrowed to provide the right type.
The following line:
arrivalData->getArrival(“RF199”,”13:50”,”N”);
is an infinite loop that invokes the lone operation getArrival() on the interface that was
retrieved from the proxy.
Note:
Ensure that the following steps have been taken with the implementation:
• The headers required for the Event Service need to be generated by running the IDL
pre-compiler on EventService.idl.
• The application needs to be linked with typed_es.lib.
• The typed EventChannel server needs to be registered with the Orbix
implementation repository. It needs to be registered as typed_es, because it is the
name used in the code.
• The typed Event Service uses the Interface Repository (IR) to retrieve the required
interface. In our case, it is the Flight interface. Therefore, this interface must be
registed with the repository.
This can be accomplished by running the IDL pre-compiler with the following switches:
This will populate the IR with the Flight interface. The IR needs to be registered with
the Orbix Implementation Repository as well so that it can be launched when required.
When registered, an Event Channel proxy is initialized with the server. This can be done as
follows:
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Typed Push Consumer Implementation
The Typed Push Consumer uses the Flight interface to have the event delivered to it.
The architecture of this solution is simple. The consumer defines the interface that it will
expose, using the IR to an event supplier. The supplier acquires a reference to this interface
through the TypedProxyPushConsumer. When this remote object reference is
retrieved, the communication is essentially the event server making an invocation on a
remote server. The only difference is that these invocations are delegated to the
TypedEventChannel for delivery.
The interface inherits from TypedPushConsumer, which is a subinterface of
PushConsumer. For this reason, push() is overridden in the implementation. The
implemention of this interface and its associated skeletons must be linked with the
Consumer application. The following steps should be taken for the consumer to receive
events from the EventChannel:
1. Obtain an object reference to the TypedEvent channel.
2. Obtain the TypedConsumerAdmin from the channel.
3. Obtain the ProxyPushSupplier object reference from the admin object
reference.
The implementation is shown in Listing 9.6.
Listing 9.6 The Typed Event Client
#include “arrival.hpp”
#include “EventService.hh”
#include <iostream.h>
Flight_i Listener;
CosTypedEventChannelAdmin::TypedEventChannel_var channel_;
CosTypedEventChannelAdmin::TypedConsumerAdmin_var admin_;
CosEventChannelAdmin::ProxyPushSupplier_var proxy_;
CORBA(Orbix).bindUsingIIOP(1);
try{
channel_=
CosTypedEventChannelAdmin::TypedEventChannel::_bind(
“Arrival:typed_es”
);
admin_ = channel_->for_consumers();
proxy_ = admin_->obtain_typed_push_supplier(“Flight”);
proxy_->connect_push_consumer(&Listener);
while(!Listener.disconnected()){
CORBA(Orbix).processNextEvent();
}
proxy_->disconnect_push_supplier();
}
catch(CosTypedEventChannelAdmin::NoSuchImplementation &) {
cout << “No Such Implementation” << endl;
exit(1);
}
catch(CORBA(SystemException& se)){
cout << “System Exception: “ << &se << endl;
exit(1);
}
catch(...){
cout << “Unknown exception” << endl;
exit(1);
}
}
The first thing the client has to do is to acquire the Event Channel. Under typical
circumstances, it is best to use the Naming Service to achieve this; but in this case, the
Event Channel is acquired by using the Orbix bind() method.
channel_ =
CosTypedEventChannelAdmin::TypedEventChannel::_bind(
“Arrival:typed_es”
With the Event Channel retrieved, the client can request the object reference to the
TypedConsumerAdmin for consumers from the Event Channel:
admin_ = channel_->for_consumers();
The client in this implementation is interested in receiving events from the Flight event
implementation. In order to do so, the client has to initialize the ProxyPushSupplier
object reference. The code for doing that is shown in the following line:
proxy_ = admin_->obtain_typed_push_supplier(“Flight”);
The Typed interface is registered with the proxy reference to listen for incoming events.
The Typed interface is the Flight interface that was implemented at the beginning of
the chapter. Here is the code that does this registration:
proxy_->connect_push_consumer(&Listener);
As long as the connection is maintained, the consumer listens to the ORB for ORB events.
To ensure that the client will listen to incoming events until the disconnect is called, the
following code will execute listening to events:
while(!Listener.disconnected()){
CORBA(Orbix).processNextEvent();
}
Summary
The CORBA Event Service is one of the key services specified by the OMG. The pri-
mary rationale behind using the Event Service is the possibility of using events to reduce
inter-dependencies between clients and servers.
The CORBA Event Service specifies two types of events. CosEvents can be either typed
or untyped events. Untyped events deliver types encapsulated as CORBA::Any type.
In the context of typed and untyped events, the CORBA Event Service specifies three
components in its architecture. The Event Supplier generates the events. The Event
Consumer acts on the events delivered. Within the specification, the architecture also
defines an Event Channel, which enables a delegation-based event model. Between these
three components, CORBA supports both push- and pull-based event models.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 10
The Transaction Service
In This Chapter
• Definition of Transaction
• Transaction Service
• Developing OTS Clients
• The LittleBank Interface Implementation
• The LittleBankServer Implementation
• The LittleBank Transaction Client
• Example: e-commerce
• The OnlineBookStore’s Transaction Client
The Object Transaction Service (OTS) presents a model for managing
transactions. A practical definition of transactions follows, along with how to
use transactions in the context of the OTS.
This chapter describes two examples where a distributed transaction is
required. The first example is a simple fund transfer for a banking application.
The second example is order processing for an online bookstore.
Transactional programming is key to controlling complex software systems in
a distributed computing environment. A well-designed transaction is a
manageable piece of work, executing in an autonomous fashion. The
robustness of a transaction acts as a strong foundation for large software
systems.
Distributed systems often have decentralized access to system resources. This
separation introduces potentially complex permutations of object interaction.
Under this circumstance, you have to have some third agency that coordinates
all these complex permutations of object interactions. Under CORBA, the OTS
provides a relatively transparent solution to the problem of coordinating
changes to distributed objects.
Definition of Transaction
A transaction is a unit of work that is atomic, consistent, isolated, and durable
(ACID). These characteristics are defined in the following list:
• Atomic—A transaction is an indivisible set of operations, grouped as
an all-or-nothing unit of work. If one of the operations fails or is
interrupted, changes made by the other operations are reverted to their
state before the start of the transaction.
• Consistent—Invariant properties must be preserved. If they were not
broken before the transaction, they should not be broken due to the
transaction.
• Isolated—Transactions are unaffected by concurrent transactions. If
two transactions make changes to the same resource, it appears as if they
access the resource in a serial manner. Additionally, transactions are
protected from intermediate states of other transactions that may fail.
• Durable—When the transaction is complete, any changes the
transaction makes are permanent and persistent.
Note:
When grouped together, sets of operations with ACID characteristics
represent a unit of work. Transaction boundaries are defined by the
beginning and ending of the unit of work.
Transaction Service
The CORBAServices specification for the OTS defines several IDL interfaces
for transaction management. These interfaces are essentially the latest
incarnation of an API defined by the X/Open Group, the Distributed
Transaction Process (DTP).
The three interfaces for DTP are
• XA for data sources
• TX for transaction processing monitors
• RM for other resources
Note:
An OTS-compliant service is required to support
• XA sources
• Import/export transaction for the TX interface
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Transaction Manager
The Transaction Manager (TM) is the software controlling the transaction behind the scenes. The
implementation of the TM is entirely up to the implementor.
The TM can be linked into the client and perform its duties in the process, sharing the same
process as the client. Or, the TM can be a standalone service that remotely controls the
transaction.
With the OTS interfaces, the TM is flexible, scalable, and extensible. Flexibility means the TM
can optimize small transactional systems, scalability is for large transactional systems, and
extensibility means the largest systems can delegate transactional processing to external
transaction managers.
Transaction Context
The transaction context is a shared entity passed between operations associated with a single
transaction. All the operations in a unit of work share a single transaction context.
The TM typically maintains the transaction context. The context may be contained in a single
thread, multiple threads, or extend to processes across a network.
Implicit/Explicit Propagation
How do you propagate the transactional context? You can do it implicitly or explicitly. Implicit
propagation is the transparent mechanism for coordinating transactions. Explicit propagation
involves specification of the transaction context to objects that are intended to participate in the
transaction.
When using implicit propagation, the client code begins the transaction and then performs a
commit or rollback at some point. This is the simplest way to add transactional capabilities to
existing and new applications.
Transactional resources join the transaction as they are called by the client. The transaction
context is propagated implicitly to these resources by the TM.
For 95 percent of transactional development, implicit propagation is sufficient for robust
transaction management.
There are cases when the client application needs to control the transaction to a finer granularity.
The client may wish to determine which operations are included in the transaction.
The Interface
During implicit transaction propagation, the client application interacts with the Current
interface. This interface is defined by this IDL:
Status get_status();
string get_transaction_name();
void set_timeout( in unsigned long seconds );
Control get_control();
Control suspend();
void resume( in Control which ) raises( InvalidControl );
};
Current is a pseudo-object provided by the ORB and OTS vendors to implement implicit
propagation. The client code may assume that the object always exists and is available.
To start a transaction, call Current’s begin() method. To delineate each transaction,
perform a commit(). If you wish to cancel changes since the last commit(), call
rollback().
Database vendors implemented the X/Open DTP XA interface for external TMs to coordinate
their transactions. For an OTS to be compliant, it must interface with XA resource interfaces.
This enables you to coordinate a transaction over two or more databases at the same time.
With the XA interface, the data source connection must be handled through the XA interface, not
the DBMS’s transactional API. For C and C++, this isn’t a problem; if the DBMS vendor
supports XA, it should have a C library interface for exposing the XA interface to the TM.
Java is more of a challenge for XA access. The JDBC 1.0 specification for database access does
not specify a method for the XA connection.
The JDBC 2.0 Extension Specification includes the javax.sql.XAConnection and
javax.sql.XADataSource classes. It is not yet clear whether these classes will allow
connection delegation for OTS or are just to support the upcoming JTA-XA interface.
Until the dust settles, vendor-specific extensions seem to be the only way a Java application can
extend a transaction across more than one DBMS. The vendor extensions often involve a native
interface, which means sacrificing Java’s portability.
Data-joining tools can make separate databases appear as if they are a single database. This
solves the problem of coordinating transactions across different databases.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Example: Banking
This example is an implementation of the funds transfer scenario described earlier in this chapter. The
architecture defines a CORBA interface for the savings account and another interface for the checking
account. Each of these interfaces accesses a separate table in the database.
Scenario Description
Figure 10.1 shows the operations participating in the transaction. The client application instructs the
OTS to begin the transaction. As the client’s operations involve transactional resources, the Transaction
Manager includes these resources in the transaction. In this case, the transactional resources are the two
databases. The TM also manages the connection to these resources on behalf of the client application.
In step 1, the client application instructs the Savings object to debit an account. The Savings object
performs a query on the Savings table in the database. This query returns the current balance for the
specified account. The Savings object then subtracts the debit amount and updates the account
balance in the database.
If the client application experiences a crash, the changes caused by the table update would be rolled
back. The integrity of the data would be maintained.
The client application calls the Checking object’s credit operation in step 2. The Checking object
retrieves the balance for the Checking account, calculates the new balance, and updates the account
balance.
#include “cos_ots.idl”
module LittleBank
{
struct AccountTransfer
{
string accountId;
string transferId;
string clerkId;
double amount;
};
#include “littlebank.hh”
namespace LittleBank{
{
public:
virtual AccountTransfer debit(
const char* anAccountId,
double anAmount
CORBA::Environment& IT_env=CORBA::default_environment );
};
};
#endif
varchar dbAccountId;
double dbAmount;
dbAccountId.len = strlen(
strcpy( (char*)dbAccountId.arr, anAccountId ) );
EXEC SQL
SELECT BALANCE
INTO :dbAmount
FROM SAVINGSACC
WHERE ID = :dbAccountId
FOR FETCH ONLY;
return dbAmount;
}
double dbGetCheckingAccountBalance(
const char* anAccountId )
{
EXEC SQL BEGIN DECLARE SECTION;
varchar dbAccountId;
double dbAmount;
dbAccountId.len = strlen(
strcpy( (char*)dbAccountId.arr, anAccountId ) );
EXEC SQL
SELECT BALANCE
INTO :dbAmount
FROM CHECNINGACC
WHERE ID = :dbAccountId
FOR FETCH ONLY;
return dbAmount;
}
char* dbUpdateSavingsAccountBalance(
const char* anAccountId, double anAmount )
{
varchar dbAccountId;
varchar dbAmount;
dbAccountId.len = strlen(
strcpy( (char*)dbAccountId.arr, anAccountId ) );
EXEC SQL
UPDATE SAVINGSACC
SET BALANCE = :dbAmount
WHERE ID = :dbAccountId;
return NULL;
}
char* dbUpdateCheckingAccountBalance(
const char* anAccountId, double anAmount )
{
EXEC SQL BEGIN DECLARE SECTION;
varchar dbAccountId;
varchar dbAmount;
dbAccountId.len = strlen(
strcpy( (char*)dbAccountId.arr, anAccountId ) );
EXEC SQL
UPDATE CHECKINGACC
SET BALANCE = :dbAmount
WHERE ID = :dbAccountId;
return NULL;
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The LittleBank Interface Implementation
The interface implementation is composed of straightforward calls to the embedded SQL functions. The
debit() method retrieves the balance from the SAVINGSACC table, subtracts the amount specified, and
then updates the balance in the same table. The balance is packaged into an AccountTransfer structure
and returned to the client.
The credit() method retrieves the balance from the CHECKINACC table, increases the balance by the
amount specified in the AccountTransfer structure, and then updates the balance in the same table.
With the implementation of the required embedded SQL out of the way (Listing 10.3), we can now
implement the interface that was specified for LittleBank.idl in Listing 10.1. This is the interface that
the client will use to make the call on the server object (see Listing 10.4).
Listing 10.4 LittleBank.CPP
#include “littlebank.hpp”
extern ‘C’
{
#include “oracle.h”
}
LittleBank::AccountTransfer* LittleBank::SavingsAccount_i::debit(
const char* anAccountId,
double anAmount
CORBA::Environment& IT_env );
{
double balanace = dbGetSavingsAccountBalance(
anAccountId );
balance -= anAmount;
dbUpdateSavingsAccountBalance(
anAccountId, balance );
void LittleBank::CheckingAccount_i::credit(
const AccountTransfer* aTransfer,
CORBA::Environment& IT_env );
{
double balanace = dbGetCheckingAccountBalance(
anAccountId );
balance += aTransfer->amount;
dbUpdateCheckingAccountBalance(
anAccountId, balance );
}
#include <OrbixOTS.hh>
#include <stdio.h>
extern ‘C’
{
#include “oracle.h”
extern __declspec( dllimport ) struct xa_switch_t xaosw;
}
ots->serverName ( “:OTS1” );
ots->logDevice ( “ots1.log” );
ots->restartFile ( “ots1.r1” );
ots->mirrorRestartFile( “ots1.r2” );
ots->init();
/* Implementation Objects
*/
SavingsAccount_i savings;
CheckingAccount_i checking;
try
{
ots->impl_is_ready();
}
catch ( const CORBA::SystemException& anException )
{
cerr << “Unexpected system exception” << endl;
cerr << anException << endl;
ots->exit( 1 );
}
catch ( ... )
{
cerr << “Exception raised” << endl;
ots->exit( 1 );
}
ots->exit( 0 );
}
#include <iostream.h>
#include <stdlib.h>
#include <ctype.h>
#include <OrbixOTS.hh>
#include “littlebank.hh”
CosTransactions::Current_var current =
CosTransactions::Current::IT_create();
current->set_timeout( 30 );
try
{
currentContext->begin();
SavingsAccount_var savingsAccount =
SavingsAccount::_bind( “OTS1”, “localhost” );
CheckingAccount_var checkingAccount =
CheckingAccount::_bind( “OTS1”, “localhost” );
AccountTransfer* transfer = savingsAccount->debit( 100.00 );
checkingAccount->credit( transfer );
currentContext->commit();
}
catch ( CORBA::SystemException& anException )
{
cerr << “(system exception)” << “ “
<< anException << endl;
currentContext ->rollback();
}
catch ( CORBA::UserException& anException )
{
cerr << “(user exception)” << “ “
<< anException << endl;
currentContext ->rollback();
}
catch ( ... )
{
cerr << “(unknown exception)” << endl;
currentContext ->rollback();
}
ots->exit( 0 );
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Example: e-commerce
This section shows how you use transactions in a highly distributed environment. The
problem domain is an online bookstore, and the example covers the checkout transaction:
1. First, the user selects a book. The inventory interface then holds the book.
2. The user selects another book that is also put on hold by the inventory interface.
3. When the user completes his or her order, the order information goes to the
order interface, which creates an order in the order database.
4. The order interface then calls the credit card authorization interface.
5. The order interface tells the inventory interface to decrement the book count for
each book in the order.
6. The order interface sends the order information to the shipping interface.
Figure 10.2 shows the propagation of the device context for each operation. The dashed
lines represent an interaction with a transactional interface. If any of the operations fail, a
rollback occurs. The rollback reverts any changes that were made in either database.
If all operations complete without error, the Transaction Manager will query the two
databases as to their commit status. When the DBMSs report that they are ready to
commit, the TM will then issue a commit against each of them.
Examine the interaction diagram closely. You will see a combination of transactional
operations that must be coordinated behind the scenes. The TM does this coordination
transparently.
The client finishes the transaction by instructing the TM to commit. The TM tells the
DBMSs to prepare; each returns a vote to commit. The TM then has the DBMSs commit
their respective transactions.
At the end of the transaction, the data is in a consistent state. During the transaction, other
applications are blocked from accessing the uncommitted changes; this insures the data
integrity.
Compared to the LittleBank example, the following IDL, as shown in Listing 10.7, is
quite extensive. There is an IDL for a MerchantServices system and the
OnlineBookShop itself. The MerchantServices defines the
CreditCardAuthorization interface. This interface is a wrapper to an external
application for authorizing credit card transactions.
The OnlineBookShop defines several structures and three interfaces. The interfaces
are Inventory, Order, and Shipping. Each interface has methods corresponding to
those outlined in the scenario description.
Listing 10.7 OnlineBookStore.IDL
#include <OrbixOTS.idl>
module MerchantServices
{
struct SaleTransaction
{
string number;
string expiration;
double amount;
};
interface CreditCardAuthorization :
CosTransactions::TransactionalObject
{
AuthorizationCode authorize(
in SaleTransaction aSale );
};
module OnlineBookShop
{
struct SaleOrder
{
ShippingAddress address;
MerchantServices::SaleTransaction creditCardInfo;
sequence<SaleItem> items;
};
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The OnlineBookStore Transaction Client
For such an involved transaction, the client code, as shown in Listing 10.8, is quite simple. After
the transaction context is established, the two books are put on hold. Then an order is built up. In
a real application, the data would come from a Web front-end.
The order object is then instructed to create an order based on the gathered data. The
implementation of the order interface authorizes the credit card, sends the order to shipping, and
removes the books from inventory.
Listing 10.8 OnlineBookStoreClient.CPP
#include <iostream.h>
#include <stdlib.h>
#include <ctype.h>
#include <OrbixOTS.hh>
#include “Inventory.hh”
#include “Order.hh”
/* Transaction Body
*/
try
{
SaleItem book1;
SaleItem book2;
/* Make sure no one else can get our copies of these books
*/
book1.holdCode = inventory->holdBook( “ISBN-123”, 1 ); //
STEP 1
book2.holdCode = inventory->holdBook( “ISBN-456”, 1 ); //
STEP 2
sale.number = “4389-8940-0904-0094”;
sale.expiration = “09/99”;
sale.amount = 98.99;
/* Full order
*/
SaleOrder bookOrder;
/* Add books
*/
book1.isbn = “ISBN 123”;
book1.price = 50.00;
bookOrder.items.add( book1 );
bookOrder.items.add( book2 );
currentContext->commit();
}
catch ( CORBA::SystemException& anException )
{
cerr << “(system exception)” << “ “ << anException << endl;
currentContext ->rollback();
}
catch ( CORBA::UserException& anException )
{
cerr << “(user exception)” << “ “ << anException << endl;
currentContext ->rollback();
}
catch ( … )
{
cerr << “(unknown exception)” << endl;
currentContext ->rollback();
}
ots->exit( 0 );
}
If any of the steps were to roll back, the three databases would revert the intermediate changes to
their original values. The integrity of the data sources would be maintained.
If other transactions attempted to access the records that were updated, they would be blocked
until this transaction completed. Thereby, the customer would be assured that the books they
ordered were not sold while they were waiting for the transaction to complete.
MOM
Nested Transactions
The examples presented in this chapter use flat transactions: There is a single unit of work that is
either committed or rolled back. If any operation in the transaction fails, all other changes are
reverted.
For many transactional scenarios, flat transactions are appropriate. There are cases where it
would be more efficient to correct the fault that caused the rollback of part of a transaction,
thereby creating a committable transaction.
The OTS specification enables an alternative to flat transactions: nested transactions. Nested
transactions are not required for an OTS to be compliant, so it is up to the implementor. With
nested transactions, you can recover and commit transactions when a subtransaction fails.
Transaction Processing (TP) Monitors have many similarities to the CORBA architecture.
Although both OTS and TP Monitors coordinate transactions, they have different agendas.
Combining the ORB and OTS provides much of the TP Monitor’s transactional functionality.
Summary
In this chapter, you learned about explored simple and complex examples of transactional
coordination in a distributed development environment. What is critical to remember is that
CORBA transactions allow you to deploy complex distributed transactions using rather simple
API to control.
Once again, CORBA implementations provide both the integration and management elements
for creating large enterprise systems.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 11
The Security Service
In This Chapter
• Distributed Security Issues
• Secure Transport
• CORBASEC Compliance
• ORB Integration and the Current Interface
• Pervasive Security Standards
Using the OMG’s CORBA Security Specification (CORBASEC), layers of
security can be added to a CORBA application. These security layers can be
integrated into existing transports and system security. It is also possible to
compromise a system’s integrity through CORBASEC, either maliciously or
accidentally.
The elements of the CORBASEC are heavily based on the Distributed
Computing Environment (DCE) security model. CORBASEC provides an
object-based model for the DCE’s security model.
The CORBA developer encounters many security obstacles when deploying
applications. Some of these hurdles include firewalls and the Java security
“sandbox.” These issues have little to do with CORBASEC, but present
security-related challenges to developers.
This chapter presents an overview of security, a description of CORBASEC, a
discussion on integrating CORBASEC with existing security mechanisms, and
an overview of security products.
Goals of CORBASEC
CORBASEC is a unification framework for different security mechanisms
with differing levels of security.
Performing service without changing application code is a common goal for
CORBAServices. The security services are designed to slide in and around
defined CORBA layers (see Figure 11.1). The ORB accesses standard security
interfaces. You can set up a secure data transport at both the sockets layer and
above IIOP.
Elements of CORBASEC
Principal
A person or application who tries to gain access to a protected resource is
considered a principal. You must be able to authenticate a principal’s identity.
A principal is uniquely identifiable, an entity who is responsible for his or her
own actions.
Principals have attributes associated with them. Some of these attributes
include the principal’s identity, role, group, and access control lists.
Policy
A policy is a high-level rule that limits access control. Usually, you enforce the
policies within the scope of a domain.
A system may specify that all users are restricted from accessing a subdomain,
this would be an example of a policy. A user could subsequently be granted
the privilege to access the subdomain.
Privileges
Privileges authorize or deny access for a principal or groups of principals. A
privilege can control access to a host, object, or even an individual method.
While policies provide broad-based access control, privileges provide
exception-based resource management. An example of privileges is provided
in the policy definition.
Session/Context
A security session or context refers to an authenticated state between a client
and target. A session is managed transparently, and the ORB propagates the
context to objects involved in the client’s request.
Domain
A domain is a geographic or logical scope against which policies are applied.
You can use domains as convenient divisions in which to apply access and
provide performance-based or protection-based administration.
Figure 11.2 illustrates how a domain is related to policies and privileges, as
explained earlier. Figure 11.3 shows how security is handled when objects
interact across multiple domains.
Server Location
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Constantly Changing Implementations
Invocation Chaining
A client object’s call chain can extend to several target objects. You can reach
each target within different security domains (refer to Figure 11.3, which
shows invocations chaining across domains). There is the potential for a user
to access systems without appropriate authorization.
This is a difficult security model to conceptualize for system administrators.
Through delegation control, principal information can travel to the target
objects, who can decide whether to authorize the principal.
Secure Transport
As data travels over network connections, it can be intercepted and examined.
Not only can the confidentiality be compromised; data can be tampered with.
If the data is encrypted and validated through a secure mechanism upon
arrival, data integrity is increased dramatically. Applying cryptography to the
transport facilitates this protection.
ORB vendors have two ways to add cryptography: Secure Socket Layer (SSL)
and Secure Inter-ORB Protocol (SECIOP). SECIOP promises interoperable
cryptography, and SSL implementations are implemented through proprietary
mechanisms. SSL is proven technology; Web browsers use SSL to secure
credit card transactions over the Internet.
SSL sits between the IIOP protocol and TCP/IP. SECIOP differs from SSL, in
that it is layered between GIOP/IIOP and the ORB. Both provide a means for
securing the data transport. SECIOP is the CORBA-specified method for data
encryption. The CSI is the interoperable specification for SECIOP, providing
three levels of conformance for SECIOP implementations.
Within the scope of SECIOP, the specification deals with various security
mechanisms and associated cryptographic algorithms. It also goes on to
specify the actual SECIOP protocol message and IOR security tags that are
involved when clients and servers interact over SECIOP. CSI specifies three
levels of compliance. Each of these levels of compliance goes with its own
security mechanism and has its own associated cryptographic mechanism to
assure the required level of security.
The following are levels of CSI conformance:
1. ID passes to target, no delegation beyond target—This provides user
authentication at the target.
2. ID and delegation to other targets—The ID is passed beyond the
target; if operations attempt to access restricted resources, the operation
will fail.
3. Controlled delegation and assignment of privileges—The target may
dynamically assign privileges and even allow the client to negotiate for
access rights. If access fails, the client application would have other
access contingencies.
CORBASEC Compliance
Levels of compliance are defined in the CORBASEC specification. These
levels are intended to provide logical implementation levels for vendors. A
vendor would state a conformance level for their product. The reality is that
vendors mix and match functionality across conformance levels.
The first of two compliance levels, Level 1 is meant for applications that are
unaware of security. Security is implemented through external mechanisms
that interface with the ORB. These external mechanisms could be the
underlying security model of the host operating system. Such an access to
security would constitute a mechanism for ensuring CSI Level 1 security
compliance. Authentication and auditing are handled outside the ORB, and this
level of conformance does not affect the applications themselves.
The ORB does need to provide the methods necessary for the Current object
to access the Credentials object of the current principal. The principal
object was discussed a little earlier under the elements of the CORBASEC
architectural elements.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
CORBASEC Compliance Level 2
Many intranets protect themselves from the Internet using firewalls and proxy
firewalls. Firewalls restrict access to ports on specific machines. This prevents
malicious or inadvertent user tampering. Firewalls also can filter connections
to those ports, terminating connections when certain criteria violate the access
restrictions defined by the administrator.
IIOP connections can be made on any port and often are dynamically
generated. Allowing connections to any port defeats the effectiveness of the
firewall.
Proxy firewalls translate IP addresses to prevent unauthorized access to
protected networks. This translation prevents ORBs from executing callbacks.
An IIOP proxy server can be used to resolve the firewall and proxy firewall
conflicts. The proxy server represents a network of ORBs on different ports as
a single port connection on one machine.
A firewall can be configured to expose the proxy server’s port to the Internet.
Now external ORBs can connect to ORBs behind the firewall. The price is the
processing and translation of IP addresses in every IIOP message.
Because the only exposed IP address is the proxy server, there is no need for
external resources to have direct access to client applications.
HTTP Tunneling
IIOP proxy servers often support wrapping IIOP requests in the HTTP
protocol. Wrapped as an HTTP connection, the IIOP requests can pass through
highly restrictive firewalls. HTTP tunneling adds another layer of potential
inefficiency on top of the proxy translation.
Figure 11.5 illustrates how HTTP tunneling works.
Java Security
Built from the ground up with security in mind, Java proves to be quite
prohibitive for distributed computing. The Java sandbox security model places
prohibitive restrictions on Java applets. The sandbox prevents applets from
having network connections to any host other than the one the applet came
from.
IIOP proxy servers come to the rescue again. The proxy server allows Java
applets to obey their security restrictions while being able to participate
completely in a CORBA environment.
Figure 11.6 shows how a proxy server can fit into a CORBA environment.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Pervasive Security Standards
The following descriptions cover many of the security technologies used in
some form throughout most security frameworks, including CORBASEC.
Kerberos
Summary
Software systems are becoming more highly integrated than they have been in
the past. Monolithic mainframes are exposing interfaces to their data and
applications to intranets and the Internet. CORBA middleware, among other
solutions, is being deployed to facilitate this integration.
Enforcing mainframe security in a distributed, heterogeneous workstation/PC
environment is a goal that might never be reached. The OMG has defined a
security service to help provide the foundation for a solution. The Security
Service Specification defines interfaces and facilities for providing security in
a CORBA environment.
Both secure access and secure communication are defined by the specification.
It has been contended that the specification has ambiguous elements,
preventing implementers from creating standalone Security Services.
Proprietary solutions are being created, with tight integration between the
ORB and the Security Service.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Part IV
Vendor Extensions and Implementation Issues
In This Part
• CORBA Server Activation Modes
• Orbix Filters
• CORBA and Threads
• Orbix Dynamic Loaders
• Distributed Callbacks
• VisiBroker Caffeine
• VisiBroker SmartStubs
• Handing Distributed Events
• VisiBroker Interceptors
Chapter 12
CORBA Server Activation Modes
In This Chapter
• Process Activation Modes
• Orbix C++ Implementation
• OrbixWeb Implementation
• Shared Activation Mode
• The Server Implementations
• Shared Per-Client Process Activation
• Unshared Activation Mode
• Per-Method Activation
• Persistent Server Activation
Activation modes determine the scalability of a CORBA server. CORBA servers constitute
processes that are launched either automatically or manually. CORBA specifies a number of
modes for launching these servers. These different modes help engineers design servers having
different scalability and availability constraints.
The capability of a server to have state determines how scalable it is. A pure stateless server is
the most scalable. These servers are usually supported by a stateless protocol as well. Web
Servers and the HTTP protocol fall into this category. In fact, it can even be argued that the
explosive growth of the Internet is owed in part to this aspect of Web technology.
CORBA and IIOP are stateful. This has repercussions on design. Although it is true that most
implementations do not explicitly leverage this aspect of CORBA, it is being done under the
covers. This statefulness is required for server-driven transactions. It is also required for security.
However, sometimes design dictates that this statefulness has different semantics. By this, I
mean that sometimes the degree of state needs to be controlled to ensure maximum scalability.
This is usually derived from some design constraints. For example, sometimes a design might
dictate that the state of the server and the objects contained therein be unique for each client. Or
consider a data access server; it is usually designed as a single server that services any number of
clients. Both of these requirements can be implemented as a single process with severe
dependence on threads.
CORBA does allow the use of threads, explicitly or painlessly, as part of the ORB
implementation. When threads are controlled explicitly by the programmer, the programmer has
to deal with the underlying Operating System thread API to control threads. Several
implementations of the ORB also provide simplified API for using threads. CORBA also allows
designers to explicitly control the activation of processes themselves so that many such design
issues may be tackled without resorting to multithreading. This is what process activation modes
(PAM) enable a programmer to accomplish. Nevertheless, keep in mind that multithreading is
never far away from using CORBA effectively. At least in one form of process activation known
as shared activation, multithreading is used in most design.
Using PAM, a designer could specify that a process be unique for a client or that a process be
shared between any number of clients. The activation modes even specify that CORBA servers
function essentially like a CGI process. All these modes of CORBA allow a great deal of
flexibility in determining the degree of scalability a server has. It should be noted here that
throughout this chapter, the words servers and processes will be used interchangeably. A
CORBA server is a process that can contain one or more services encapsulated in objects that a
client accesses.
This chapter primarily covers how Orbix C++ 2.3 and OrbixWeb deal with process activation.
Simple examples are used to illustrate how PAM can be used.
Note:
Please note that the three subactivation modes are found for both the shared activation and the
unshared activation modes. This means, for example, that there is a shared per-client activation
mode and an unshared per-client activation mode.
Let’s first develop a simple application that demonstrates how these can be used in a design. All
PAM applications will use the following interface:
module pam{
struct simpleStruct{
short one;
long two;
double three;
};
interface activation{
};
interface nactivation{
void dinactivate(in simpleStruct inStruct) raises (
activationException);
void doutactivatate(out simpleStruct outStruct) raises (
activationException);
void dinoutactivate(inout simpleStruct outStruct) raises (
activationException);
simpleStruct returnnactivation() raises (
activationException);
};
};
The interface is nothing to write home about! Nevertheless, it will show how to use process
activation. The implementation itself is extremely simple. The pam module specifies two
interfaces: activation and nactivation. Both interfaces perform the same function.
There are two identical interfaces to show the differences between various process activation
modes. These differences and similarities are explained later in this chapter when I explain the
various activation modes in detail.
Previous Table of Contents Next
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Compile the pam interfaces using the Orbix IDL pre-compiler. We will implement the interface using
both C++ (Orbix) and Java (OrbixWeb). First, we will do the implementation using C++ (Orbix), and
then we will do the implementation using Java (OrbixWeb).
prompt$>idl -B -S pam.idl
Listing 12.1 shows how the pam module is implemented. In itself, the interfaces do not demonstrate any
underlying activation mode that is being planned. Later in the chapter, you will learn how these
implementations relate to activation modes.
Listing 12.1 The C++ Implementation of pam Module
#include “pam.hpp”
#include <iostream.h>
void pam::activation_i::outactivatate (
pam::simpleStruct& outStruct, CORBA::Environment &IT_env) {
outStruct.one = 1001;
outStruct.two = 3455;
outStruct.three = 6456;
}
pam::simpleStruct pam::activation_i::returnStruct (
CORBA::Environment &IT_env) {
pam::simpleStruct returnpam;
returnpam.one = 1001;
returnpam.two = 3455;
returnpam.three = 6456;
return returnpam;
}
void pam::nactivation_i:: dinactivate(
const pam::simpleStruct& inStruct, CORBA::Environment &IT_env) {
outStruct.one = 101001;
outStruct.two = 34455;
outStruct.three = 69456;
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
OrbixWeb Implementation
Compile the interfaces using the OrbixWeb IDL pre-compiler. Run the IDL pre-compiler
using the following command:
prompt$>idl -B -S pam.idl
Listing 12.2 shows the corresponding Java implementation of the pam module. As with the
C++ implementation, the rationale for the code is similar to the Orbix C++
implementation. I use Java code as well as C++ code to show that the amount of work that
is required between the two is very similar.
Listing 12.2 The Java Implementation of PAM::activation
package pam;
public class activation_i extends pam._activationImplBase {
public activation_i(java.lang.String name) {
super(name);
}
public activation_i() {
super();
}
public void inactivate(
pam.simpleStruct inStruct
) throws
pam.activationException {
package pam;
public class nactivation_i extends pam._nactivationImplBase {
public nactivation_i(java.lang.String name) {
super(name);
}
public nactivation_i() {
super();
}
public void dinactivate(
pam.simpleStruct inStruct
) throws
pam.activationException {
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Shared Activation Mode
Shared activation mode is usually the default activation for CORBA
implementations. In this mode of activation, all the objects that have the same
server name are contained and managed within one server. This means that
when the server process is launched, all the objects that are hosted by the
server are instantiated.
If you consider the data access server, this is one of the solutions you could
propose. Using a shared activation mode would have a number of benefits. The
following are several of these benefits:
• Conservation of system resources
• Ability to share state across clients.
• Ability to retain state across sessions.
• The option of using multithreading to improve responsiveness
Processes are expensive in terms of the resources that they could use. Every
time a process is launched, it uses resources that are available from the
underlying operating environment.
The constraint of resources is mainly in terms of memory. Resources of any
kind are always finite. If such resources are used arbitrarily, a process can be
starved, and it inevitably affects the availability and responsiveness of the
server-to-client request.
A single process enables you to conserve these resources.
If the data server were designed as a shared activation server, certain
consequences would flow from that decision. The first would be that a
mechanism would have to be found to allow clients to be serviced
simultaneously. This would allow a server to respond to more than one client
at the same time. It is obvious that a data server would have to be
multithreaded.
When the data server is designed to respond using threads, you could choose a
number of strategies and patterns to ensure that more than one client is
serviced efficiently. You will find more information on multithreading issues
in Chapter 14, “CORBA and Threads.”
Under Orbix, this type of activation must be registered with the
Implementation Repository. A shared server that is registered with Orbix does
have one demand: The name given to the impl_is_ready() must be the same
name that is used in registering the server.
This type of activation launches a new server process for every client-side
process that is launched by a user. This enables designs where a server process
is unique for every client process. This can be ideal where a server process
cannot have shared resources between clients.
If the constraints on resources are severe, such an activation process is not
suitable to “heavyweight” processes. The use of per-client process activation is
suitable only for lightweight processes. Ideally, such processes would contain
very little state. The per-client process activation modes should ideally be
designed and implemented to timeout by themselves.
Data servers are very rarely implemented using per-client process activation
modes. These modes could prove useful if the constraint of scalability is not
present. Nevertheless, activation of processes is usually heavy. It has been my
experience that UNIX operating systems are usually more responsive to
process activation than others.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The Server Implementations
The C++ server shown in Listing 12.4 is used for shared activation modes. After we have
finished building the C++ server, we will implement the server in Java.
Listing 12.4 Shared C++ Server
#include <iostream.h>
#include <stdlib.h>
#include “pam.hpp”
int main() {
pam::activation_var activation = new pam::activation_i();
pam::nactivation_var _nactivation = new pam::nactivation_i();
try {
CORBA::Orbix.impl_is_ready(“PAM”);
}
catch (CORBA::SystemException &sysEx) {
cerr << “Unexpected system exception” << endl;
cerr << &sysEx;
exit(1);
} catch (...) {
cout << “Unexpected exception” << endl;
exit(1);
}
cout << “server exiting” << endl;
return 0;
}
Listing 12.5 shows how to implement a shared server using Java.
Listing 12.5 Shared Java Server
package pam;
import IE.Iona.OrbixWeb._OrbixWeb;
import IE.Iona.OrbixWeb._CORBA;
import org.omg.CORBA.ORB;
Server Registration
The Orbix putit utility or the Orbix Server Manager may be used to register the server under
Orbix or OrbixWeb. You usually use the former tool if you are running Orbix under UNIX.
Because we are using Windows NT for these examples, we will use the Server Manager. The
Server Manager is a graphical tool to register the servers with the Basic Object Adapter.
The server is registered as a shared server. The activation is set to client. This means that
when a client connection comes in, the Basic Object Adapter will automatically activate the
Shared Server when a client attempts a connection. This would be especially useful when
availability of a server is critical to an application.
The server is registered as a shared server. The action is set to Per Client PID. The shared
per-client activation is launched automatically for every new client process identifier. A physical
node on the network will have a unique process identifier. This means that when a new PID
attempts to launch a server, the Basic Object Adapter automatically launches the server, provided
that the PID is not registered with a server that is already running.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Unshared Activation Mode
In the unshared activation mode, each object has to be registered individually. This means that each
object is going to run in its own server process. This activation might be useful where individual
objects need to be isolated in terms of their state.
The server implementation has to change. Each object that can be invoked using the unshared
activation mode has to be given a marker. This is illustrated in Listing 12.6. When a client makes a
request to a server using this marker, the Basic Object Adapter will launch a server that will host
this object. In the context of the examples that have been used, the interfaces specified in the pam
module contain two interfaces: activation and nactivation. In Listing 12.6, two objects are
instantiated. The nactivation object is assigned the marker so that a client can invoke it on a
per-object mode.
Listing 12.6 An Unshared C++ Server
#include <iostream.h>
#include <stdlib.h>
#include “pam.hpp”
int main() {
pam::activation_var activation = new pam::activation_i();
pam::nactivation_var nactivation = new pam::nactivation_i();
nactivation._marker(“Method”);
try {
CORBA::Orbix.impl_is_ready(“PAM”);
}
catch (CORBA::SystemException &sysEx) {
cerr << “Unexpected system exception” << endl;
cerr << &sysEx;
exit(1);
} catch (...) {
cout << “Unexpected exception” << endl;
exit(1);
}
cout << “server exiting” << endl;
return 0;
}
Listing 12.7 shows the implementation of the server in Java.
Listing 12.7 An Unshared Java Server
try {
activate = new activation_i();
activate.setMarker(“Method”);
_ CORBA.Orbix.impl_is_ready(“PAM”);
System.out.println(“Shutting down server...”);
orb.disconnect(activate);
}
catch(org.omg.CORBA.SystemException se) {
System.out.println(“Exception raised
during creation of PAM_Implementation” + se.toString());
System.exit(1);
}
System.out.println (“Server exiting....”);
}
}
As you can see, the server implementation remains the same as other shared servers in the preceding
section, except the nactivation object is given a marker name, Method. A client can use this
name to bind to a particular object within the server PAM. This implementation contains only one
object that is activated on a per-object basis; therefore, only one marker name is used. Under typical
circumstances, it is usual for a server such as this to contain more than one object. This would
require that each one be given unique names. The marker names cannot be hard-coded in the
implementation. The solution under these cases is to use a property file or an initialization file to
assign markers to the object. If a client needs to use a “marked” object, the bind method on the
object reference should be modified slightly. Instead of specifying just the server to bind to, a client
should also provide the marker name of the object that needs to be acquired explicitly.
The implementation is shown in Listing 12.8. As with the previous examples, first we will do a
client in C++; then a Java client will be built.
Listing 12.8 A C++ Unshared Activation Client
#include “pam.hh”
#include <iostream.h>
pam::simpleStruct inStruct;
inStruct.one = 10010;
inStruct.two = 11010;
inStruct.three = 12110;
try {
active->inactivate (inStruct);
active->outactivatate(inStruct);
nactive->dinactivate(inStruct);
nactive-> doutactivatate(inStruct)
pam::simpleStruct AnStruct active->returnnactivation();
pam::simpleStruct DnStruct nactive->returnnactivation();
}
catch (CORBA::SystemException &sysEx) {
cerr << “Unexpected system exception” << endl;
cerr << &sysEx;
exit(1);
}
}
The key methods in this code are
try {
active = pam::activation::_bind(“:PAM”);
nactive = pam::nactivation::_bind(“Method:PAM”);
}
The nactive object reference is retrieved using the marker “Method”. This allows the client to
retrieve the exact object from the server. When the objects are retrieved successfully, the client can
invoke methods on the service as usual.
The implementation for a Java client is shown in Listing 12.9. The ORB is OrbixWeb 3.0. The
process of activating a server is the same as with the VisiBroker ORB.
Listing 12.9 A Java Shared Activation Client
package pam;
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
When the server has been constructed, the servers have to be registered with
the Implementation Repository. Both OrbixWeb and Orbix include the Server
Manager tool to assist in the registration process, which is depicted in Figure
12.3.
Per-Method Activation
The per-method activation mode is closest to a CGI process. This activation
model enables a process to be registered by a method or an attribute operation.
When an invocation comes into the method, a new process can be instantiated
and executed.
The per-method activation mode can be used as you would use a CGI process.
Extremely long calculations or other computational processes can be designed
to be encapsulated in a per-method activation server. This ought to be designed
as a stateless server, existing only for the duration of the computation.
Keep in mind that processes are expensive to construct. This means that you
would have to design your per-method servers to be as light as possible, so as
to utilize as little of the underlying resources as possible. The code used for the
per-method registration is the same as the code that we used for the unshared
per-client activation model.
Figure 12.4 depicts the registration with the server manager. The method has
to be registered as well; this is shown in Figure 12.5.
_CORBA.Orbix.impl_is_ready();
This line shows the C++ Code:
CORBA::Orbix.impl_is_ready();
The server is coded as usual except that no name is provided in the
impl_is_ready() method. Persistent servers do not have to be registered
with the Implementation Repository.
Summary
Process activation is the mechanism by which a server is launched by the
Basic Object Adapter when a client requests a service. There are a number of
modes that are possible under various implementations of CORBA.
Process activation is a design issue. It is primarily a function of how much
state a designer wants a server to maintain. CORBA allows a server to be a
static process serving any number of clients. CORBA also allows servers that
are unique to every client, in essence the ability to have a single server for each
client that needs a service.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 13
Orbix Filters
In This Chapter
• Filter Monitor Points
• Per-Process Filters
• Per-Object Filters
Filters are a useful functionality provided by Orbix. They enable an application
to execute some code either before or after an invocation comes into a normal
operation on the server side. This functionality could prove to be vital in many
designs. For instance, Orbix pre-filters could be used to authenticate a client’s
requests or for redirection, whereas post-filters can be used for logging or
caching requests. Another vital area of using filters is for designing a
thread-shared server. VisiBroker provides a similar mechanism called
Interceptor. This is covered in detail in Chapter 20, “VisiBroker Interceptors.”
Orbix specifies two types of filters: per-process filters and per-object filters. A
per-process filter has the capability of monitoring the entire application space.
This is irrespective of the number of objects contained in a process. These
per-process filters are for the entire application.
The per-object filter is responsible for only a given object. This could be used
to shield sensitive objects in an application. For instance, if an invocation
comes in for a security-sensitive object, a per-object pre-filter could intercept
the request before it arrives at the object. It could then perform a request
authentication to ensure that the client has the right credentials to make the
request. A per-object post-filter could log when a service has been rendered to
a client request. With such logging, the client might not be able to repudiate a
service being rendered by the server. Nonrepudiation is extremely important in
a distributed environment. Essentially, it means that a client cannot deny that it
requested and received some service from the server. For instance, a client
could request a remote object to make a transfer of money from one account to
another. After the service has been rendered, the client should not have the
option of ever denying this fact.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Per-Process Filters
To define a per-process filter, derive a class from the CORBA::Filter abstract class. This class
defines ten methods that may be overridden to implement per-process filters. The derivation is
shown in Listing 13.1. No matter what type of filter you want to build, the CORBA::Filter class
encapsulates all the functions necessary. If you want to implement the inReplyPreMarshal()
function for the client, you subclass the CORBA::Filter class and implement this function. The
following list describes each of these ten methods:
• outRequestPreMarshal()
A monitor point to execute a filter before an outgoing request. This occurs before the actual
marshaling is done. Before a client invocation is marshaled, this filter monitor will execute.
• outRequestPostMarshal()
A monitor point that executes after the marshaling of a request. This monitor is triggered
before the process responds to a request.
• inRequestPreMarshal()
When a server process receives an invocation, this monitor point is triggered. The filter
executes before the actual marshaling takes place.
• inRequestPostMarshal()
This monitor triggers at the receiver’s end after the invocation has been marshaled.
• outReplyPreMarshal()
This filter point executes on the server’s end before responses to invocations are sent out,
meaning before they are marshaled.
• outReplyPostMarshal()
After the server has marshaled the response, a filter that has overridden this method will
execute.
• inReplyPreMarshal()
A client filter that executes before a server’s response has been marshaled.
• inReplyPostMarshal()
After the server’s response has been marshaled, this trigger will kick in and execute.
• outReplyFailure()
If the server or one of the preceding trigger points raises some exception, a filter can take
appropriate action. For instance, it could log the exception or enable recovery strategies.
• inReplyFailure()
This is the same as outReplyFailure(), but one that triggers in the caller’s address
space. As with outReplyFailure(), this monitor point will allow the addition of error
correction, logging, or auditing to take place.
CORBA design is never simple. As the power and flexibility of the architecure becomes apparent,
the design invariably changes. But more often than not, a number of auxiliary but critical design
constraints are usually considered last. Such issues as security and fault tolerance are considered
when the design is in an advanced stage. At that point, it is usually difficult to modify the design.
Monitor points are a good mechanism to alter the design of a system. The auxiliary design
constraints mentioned earlier can be added with as little disruption to the overall design as possible.
The per-process filter methods take two essential parameters: the usual CORBA::Environment
and the CORBA::Request object. By far, the Request object is very interesting. For instance,
its API (discussed in Appendix A, “CORBA API Reference” enables you to discover useful
information, such as which user is making the invocation, which object is the target object the
invocation is meant for, and so forth.
Listing 13.1 shows a simple object that traps all ten triggers. It does not do much except retrieve the
principal that is making the request. The principal corresponds to the user that is making the
request.
Listing 13.1 Constructing a Filter
#define EXCEPTIONS
#include <CORBA.h>
#include <iostream.h>
public:
request.principal();
The code returns a constant character array indicating the user. This information may be used to
implement a simple Access Control Filter.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The operation is discovered by using r.operation (). It returns a character array
telling the name of the operation. A combination of principal and the requested operation
would allow the design of profile-based access to an interface. For instance, the filter
could maintain a list of users and a list of associated authorization to an interface. If an
unauthorized invocation is attempted, the filter could throw an exception.
Installation of per-process filters is easy: Just instantiate an instance of the filter in the
appropriate process. For instance, consider the following interface:
module sentry{
interface filters{
};
This interface will illustrate how to use a filter. The interface itself is rather trivial. The
idea is to show how filters can be used.
A per-process filter can be installed to both the client and the server processes. Listing
13.2 illustrates installing a filter on the server.
Listing 13.2 Installing the Server Filter
#include “filter.hpp”
#include “processf.h”
ProcessFilter ServerFilter;
int main() {
sentry::filters_var Filters_ = new sentry::filters_i();
try {
CORBA::Orbix.impl_is_ready(“Filters”);
}
catch (CORBA::SystemException &sysEx) {
cerr << “Unexpected system exception” << endl;
cerr << &sysEx;
exit(1);
} catch (...) {
cout << “Unexpected exception” << endl;
exit(1);
}
cout << “server exiting” << endl;
return 0;
}
In the preceding code, the per-process filter is installed as a global object. This filter will
trigger two methods: the inRequestPreMarshal() and the
outReplyPostMarshal(). Listing 13.3 shows intalling a filter on the client.
Listing 13.3 Installing the Client Filter
#include “filter.hh”
#include <iostream.h>
#include “processf.h”
ProcessFilter Filter_instance;
try {
Filters_->inn(200);
CORBA::Short out_val;
Filters_->outt(out_val);
cout<<out_val<<endl;
cout<<Filters_->response()<<endl;
}
catch (CORBA::SystemException &sysEx) {
cerr << “Unexpected system exception” << endl;
cerr << &sysEx;
exit(1);
}
}
As in the server, the filter is instantiated as a global object. The filter will trigger two
methods: outRequestPreMarshal() and inReplyPreMarshal().
Per-Object Filters
Unlike the per-process filters, the per-object filter is attached to individual objects. There
are a couple of details that you have to follow when using the per-object filtering
technique:
• The implementation must use TIE to facilitate per-object filtering.
• The -F switch must be toggled on the Orbix IDL pre-compiler to generate
per-object filtering on the object.
The mechanism of defining a per-object filter involves creating two classes that intercept
the invocation of operations specified in an interface. Let us consider such an interface:
interface objectFilter{
#ifndef objfil_ih
#define objfil_ih
#include “objfil.hh”
#include <iostream.h>
class PreObjectFilter{
public:
virtual CORBA::Short objFilter (CORBA::Short a_val,
CORBA::Short& b_val, CORBA::Environment
&IT_env=CORBA::default_environment) {
cout<<“Pre Filter Called..”<<endl;
cout<<a_val<<endl;
b_val = 0;
return 0;
}
};
class PostObjectFilter{
public:
virtual CORBA::Short objFilter (CORBA::Short a_val,
CORBA::Short& b_val, CORBA::Environment
&IT_env=CORBA::default_environment) {
cout<<“Post Filter Called..”<<endl;
cout<<a_val<<endl;
cout<<b_val<<endl;
return 0;
}
};
DEF_TIE_objectFilter(objectFilter_i);
DEF_TIE_objectFilter(PreObjectFilter);
DEF_TIE_objectFilter(PostObjectFilter);
#endif
Along with the modification to the objectFilter class, two additional classes are
defined: PreObjectFilter and PosObjecttFilter.
The second thing is the definition of three macros. These macros will generate TIE
support. (Refer to Appendix B, “TIE and BOA,” for more information.) Orbix’s
implementation of the filters mandates TIE support as compulsory for using per-object
filters.
The implementation of the objectFilter class itself is trivial. Nevertheless, it is
shown for clarity:
#include “objfil.hpp”
#include <iostream.h>
cout<<a_val<<endl;
b_val = 1001;
return 1002;
}
Keep in mind that the parameters that are passed into the per-object filter cannot be
modified. Any modification will be lost. This is all that remains to building per-object
filters. The next step is to create a server to host the TIE object and to attach the filters to
this object.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The Per-Object Filter Server
The code for the server is given in Listing 13.5. If you are not familiar with the TIE approach,
please refer to Appendix B.
Listing 13.5 Constructing a Per-Object Filter Server
#include “objfil.hpp”
#include <iostream.h>
try {
CORBA::Orbix.impl_is_ready(“ObjectFilter”);
}
catch (CORBA::SystemException &sysEx) {
cerr << “Unexpected system exception” << endl;
cerr << &sysEx;
exit(1);
}
catch (...) {
cerr << “Unexpected exception : impl_is_ready” << endl;
exit(0);
}
CORBA::release(objFilter);
CORBA::release(PreFilterPtr);
CORBA::release(PostFilterPtr);
return 0;
}
The first three lines of code essentially instantiate the three TIE objects. After these objects are
successfully instantiated, the filters have to be attached to the object that requires pre- and
post-filtering.
Here are the lines that accomplish this:
The client implementation is straightforward, as usual. If the client does not have any process
filter support, it does not even know that the server objects have filter support. The code is given
in Listing 13.6.
Listing 13.6 Constructing a Per-Object Filter Client
#include “objfil.hh”
#include <iostream.h>
#include <stdlib.h>
objectFilter_var objecfilter;
try{
objecfilter = objectFilter::_bind (“:ObjectFilter”);
}
catch (CORBA::SystemException &sysEx) {
cerr << &sysEx;
cerr << “Bind failed” << endl;
exit(1);
}
try{
CORBA::Short out_val;
CORBA::Short x = objecfilter->objFilter(1002,out_val);
cout<<x<<endl;
cout<<out_val<<endl;
}
catch (CORBA::SystemException &sysEx) {
cerr << &sysEx;
cerr << “Bind failed” << endl;
exit(1);
}
return 0;
}
The code is typical for any “normal” CORBA client. It binds to the appropriate object reference
and make its invocations as usual. The client is oblivious to the fact that there are filters between
it and the server objects.
Summary
Orbix filters are a versatile mechanism for controlling client/server interactions to a very fine
granularity. These filters have the capability of intercepting incoming and outgoing method
invocations.
Through this interception, a number of auxiliary design constraints may be imposed on an
implementation. These could be issues relating to security or auditing requirements. There are
two types of Orbix filters. The first type is a global filtering mechanism known as a per-process
filter. It is attached to the process as a whole. The second type of filter is a per-object filter. It
allows the filtering of invocations on individual objects. Between the two, a certain granularity of
interceptions can be achieved.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 14
CORBA and Threads
In This Chapter
• CORBA Thread Strategies
• Implementing Threads
One of the consequences of a shared activation server is the fact that, in terms of
scalability, its potential is rather limited. For simple objects, this might not be a
problem. On the other hand, if a server process has an arbitrarily large number of
objects, each of them complex, the ensuing design will be full of bottlenecks that
will degrade performance.
Threads provide a clean and consistent mechanism for introducing parallelism into
a design. Just as there are different modes of process activation, there are also
different strategies for managing threads.
Any complex CORBA application design will use threads. Some ORB
implementations have built-in multithread support that can be leveraged by just
changing the link libraries. Some ORBs, on the other hand, require server design to
explicitly use threads. This chapter will look at some of the issues involved.
Thread Pools
This is the most efficient strategy in terms of resource allocations. A fixed set of
threads is created during server initialization. When a request comes in, this request
is assigned to a thread. If all the threads are busy, the request could be buffered in a
queue until a thread becomes free. The thread pool is shown in Figure 14.3.
Implementing Threads
Threads are implemented using variations of waiting on shared semaphores. A
semaphore is a mechanism that prevents two or more threads from accessing a
shared resource simultaneously. In conjunction with interceptors or Orbix filters, a
thread could be designed to wait on this shared resource in an
inRequestPreMarshal() filter.
When the filter is triggered, the synchronization mutex could be signaled so that
the waiting thread could do its job on a thread.
Thread-Per-Client
interface tpc {
void read(out short a_value);
void write(in short b_value);
};
The goal is to have a client call these two operations asynchronously. The example
will not attempt to show how to efficiently synchronize the invocations so that
reads and writes happen in one step.
A number of factors affect the functioning of threads. Thread priority is determined
by the underlying operating system. But it is possible to change the priority setting
by invoking the appropriate API.
The client will invoke the two methods on two separate threads, while the server,
on the other hand, should be able to service the request on a thread. This means that
each incoming request will have the server launch a thread to handle the request.
The implementation uses Orbix and Orbix filters. Compile tpc.idl with the
following switches:
prompt$>idl -S tpc.idl
Implementing Thread-Per-Client
Modify the generated header file, as shown in Listing 14.1.
Listing 14.1 Thread-Per-Client Header File
#ifndef tpc_ih
#define tpc_ih
#include “tpc.hh”
#include <windows.h>
#include <process.h>
DEF_TIE_tpc(tpc_i)
#endif
Support for WIN32 threads are defined in <process.h>. This chapter assumes
familiarity with WIN32 threads. The tcp class has to use the TIE mechanism
because it is using Orbix filters to implement threads.
The variable mutex_ is the shared synchronized variable that will maintain a
synchronization lock on a shared resource. In this case, it is CORBA::Short
ReadWriteVariable. A constructor and destructor are also defined for the
class.
The implementation will use an Orbix filter to control the threading requirements.
This can be accomplished by subclassing the CORBA::ThreadFilter class.
This is a per-process filter. As with any per-process filter, the 10 monitor points
described in Chapter 13, “Orbix Filters,” may be handled on a thread. For this
exercise only, the inRequestPreMarshal() monitor point is attached to the
filter. This means that when a request comes in, the filter will intercept the request
before marshaling the code.
Finally, the TIE macro DEF_TIE_tpc(tpc_i) is defined so that TIE support is
generated for the tpc_i class.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The Per-Client Thread Implementation
With the header files defined, the associated classes can be implemented, as shown in Listing 14.2.
Listing 14.2 Thread-Per-Client Implementation File
#include <iostream.h>
#include “tpc.hpp”
tpc_i::tpc_i(){
readWriteVariable = 0;
mutex_ = CreateMutex(0,FALSE,0);
}
tpc_i::~tpc_i(){
CloseHandle(mutex_);
}
int tpcFilter::inRequestPreMarshal(
CORBA::Request& request, CORBA::Environment& env){
int threadid;
threadid =
(int)_beginthread((void(*)(void*))startThread,0,(void *)&request);
return -1;
}
tpcFilter threadDispatcher;
The constructor creates an unnamed mutex object. Any thread of the calling process can specify the mutex
object handle in a call to one of the wait functions. The single-object wait functions return when the state of
the specified object is signaled. The destructor of the class releases the mutex handle.
The heart of the threading strategy is the implementation of the inRequestPreMarshal method:
int tpcFilter::inRequestPreMarshal(
CORBA::Request& request, CORBA::Environment& env){
int threadid;
threadid =
(int)_beginthread((void(*)(void*))startThread,0,(void *)&request);
return -1;
}
This method will be invoked when a request comes in. The implementation creates a thread using the
_beginThread() function that is defined in <process.h>. The function takes three parameters. The
first parameter is a pointer to the starting address of the routine that begins execution of the new thread. The
second is the stack size that has to be allocated for the thread. The third is any argument that needs to be
passed into the thread.
The starting address of the routine that begins execution of the new thread is defined in the function
startThread():
#include <string.h>
#include “tpc.hpp”
#include <iostream.h>
#include <stdlib.h>
int main()
{
tpc_ptr ThreadForAClient;
ThreadForAClient = new TIE_tpc(tpc_i) (new tpc_i());
try {
CORBA::Orbix.impl_is_ready(“TPC”);
}
catch (CORBA::SystemException &sysEx) {
cerr << “Unexpected system exception” << endl;
cerr << &sysEx;
cerr << “Exception in impl_is_ready” << endl;
exit(1);
}
catch (...) {
cerr << “Exception in impl_is_ready” << endl;
exit(1);
}
CORBA::release(ThreadForAClient);
return 0;
}
Register the server as a shared activation process “TPC”.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The Per-Client Thread Client Implementation
The client implementation, shown in Listing 14.4, is a little involved. It uses two threads: one to
invoke the write operation and another to invoke the read operation.
Listing 14.4 Per-Client Thread Client Implementation
#include “tpc.hh”
#include <stdlib.h>
#include <iostream.h>
#include <process.h>
try {
object_one = tpc::_bind (“:TPC”);
object_two = object_one;
}
catch (CORBA::SystemException &sysEx) {
cerr << “Unexpected system exception” << endl;
cerr << &sysEx;
}
catch (...) {
cerr << “Unexpected exception” << endl;
}
if(WaitForSingleObject
((HANDLE)thread_one, INFINITE) == WAIT_FAILED){
cerr << “thread:” << thread_one << “ wait failed ” << endl;
}
if(WaitForSingleObject
((HANDLE)thread_two, INFINITE) == WAIT_FAILED){
cerr << “thread:” << thread_two << “ wait failed “ << endl;
}
CORBA::release(object_one);
return 0;
}
return ptr_;
}
return ptr_;
}
The application defines two instances of the tpc interface. The first one is acquired by calling
Orbix bind(). The second one essentially points to the same object reference. This means that
object_one and object_two point to the same object reference.
The implementation defines two functions: thread_read() and thread_write(). These
functions are executed on the threads created by the following functions:
if ((thread_one =
(unsigned long)CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)thread_write,
(void *)object_one, 0, &thread_one)) == NULL){
cerr << “thread: creation failed” << endl;
exit(-1);
}
if ((thread_two =
(unsigned long)CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)thread_read, (void *)object_two,
0, &thread_two)) == NULL){
cerr << “thread: creation failed” << endl;
exit(-1);
}
The CreateThread() function is a WIN32 API function to create threads within the application.
On these threads, the two functions execute thread_read() and thread_write().
The thread_read() and thread_write() functions are essentially normal CORBA code.
The only distinction is the fact that they execute on two separate threads.
The execution of two clients and the server is shown in Figure 14.4.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Thread Pools
This section demonstrates how thread pools can be used to control requests on a
server. Typically, thread pools are configured to handle a maximum number of
simultaneous threads, where numerous requests are made on the server. Thread
pooling is the default thread model for VisiBroker 3.3, which will be used to
demonstrate this example. The following interface will be used to demonstrate the
thread pool model. Compile the IDL using VisiBroker’s idl2java pre-compiler:
module Pool {
interface Request{
void stopThread(in long stime);
};
};
The implementation of the interface, Request, is shown in Listing 14.5.
Listing 14.5 Request Implementation
package Pool;
import java.io.*;
public RequestImpl() {
super();
}
public void stopThread(int stime) {
Thread thisThread = Thread.currentThread();
try{
thisThread.sleep((long)stime);
} catch (Exception e){
System.out.println(“Sleep Exception”);
}
}
}
The argument to the stopThread() method is an integer representing the time (in
milliseconds) that is used to put the object to sleep:
package Pool;
import java.util.*;
}
catch(org.omg.CORBA.SystemException ex){
System.out.println(ex.toString());
}
boa_.impl_is_ready();
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Listing 14.7 Request Client Implementation
package Pool;
}
}
The request client uses a String argument, sRequest, to determine which request object
to bind to. The stopThread() method on the request object is invoked using a time of
30,000 milliseconds, or 30 seconds. This invocation puts the request object’s thread to sleep,
thus keeping the thread busy for a while.
Now it is time to run the example. Start the request server:
Now, start three request client executions, using Request1, Request2, and Request3
as arguments:
The Request1 and Request2 client programs invoke the stopThread() method on
their respective request server objects, as shown by the entry “Invoking request on
RequestX”. Because the maximum thread pool size is set to two, the Request3 client
must wait until a thread is returned to the thread pool. After 30 seconds, Request1 is
finished, and its thread is returned to the thread pool. Request3 now has a thread available
to process its request, which it obtains to start its invocation (see Figure 14.7).
Summary
Threads are an integral part of any serious CORBA solution. Designing programs with
multiple independent threads provides concurrency within an application and improves
performance. Threads enable the design of applications to service multiple requests
simultaneously.
This is required especially when multiple clients connect to a shared server and request an
arbitrary number of services at the same time. Threads enable writing apps that
asynchronously manage incoming requests.
There are three types are threading strategies: thread per session, where every client is
allocated a thread to service its requirements; thread per object, which involves every object
executing in its own thread; and thread pooling, which enables allocation of a fixed number
of threads to service any request that comes in.
Ultimately, threads improve performance and increase the efficiency of an implementation’s
utilization of host resources. However, care must be taken when using threads, because
implementations that do not take the underlying complexity of threaded architectures into
account tend to create unmaintainable and unreliable applications.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 15
Orbix Dynamic Loaders
In This Chapter
• Why Write Loaders?
• Using a Loader
• Implementing a Loader
• Providing Persistence Using Loaders
• The Loader Server Implementation
Orbix Loaders enable a server to load objects during runtime. The loaders can provide
this service under two situations. The loaders can instantiate an object when a client
dispatches a request for an object reference through string_to_object() or
bind() and when a request comes into the server’s address space for an object that isn’t
initialized. Under the latter scenario, the ORB responds with an exception if an object is
not found. If, however, a loader is installed, the server will request it to load the object
when an invocation comes in.
Loaders are implemented by subclassing the CORBA::LoaderClass. When a loader
is defined and implemented, it has to be installed with the Orbix ORB. A loader can be
associated with an object when it is created. This is accomplished by passing a pointer to
the loader as a parameter into either the TIE constructor or the BOAImpl constructor with
Orbix.
Using a Loader
The process of implementing an interface that uses a loader is not any different from
what we have done before. In fact, Orbix uses a default loader if you do not provide one.
To illustrate the implementation of a loader, let’s consider a simple interface to illustrate
the essentials of coding an Orbix loader.
We will implement loaders in both C++ and Java. The differences between them are not
that great. Both implementations use the following interface. It is only in the server
implementation that changes occur. That is primarily because it is easier to construct user
interfaces in Java Swing than in MFC:
Here is the Hangar interface:
module Soar{
interface Aircraft{
attribute long identifier;
};
interface Hangar{
Soar::Aircraft CheckOutAircraft(in long identifier);
void CheckInAircraft(in Soar::Aircraft anAircraft);
};
};
The preceding module is very simple. It specifies two interfaces, one named Aircraft
and the other named Hangar. The object is to pass an instance of an Aircraft to and
from the Hangar. It is rather simple. The interface is not under scrutiny here. Rather, at
issue is how we could write a loader to intervene between the object that implements the
interface and the client that requests the interface. The interface can be implemented in
the following manner.
The first thing that you have to provide for an interface implementation is to overload its
constructor so that it can use a user-defined constructor. If this is not done, the default
Orbix loader is used. Listing 15.1 is the C++ Soar::Aircraft implementation,
Listing 15.2 is the Java AirCraft implementation, and Listing 15.3 is the Java
Hangar Implementation.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Listing 15.1 C++ Soar::Aircraft Implementation
#ifndef hangar_ih
#define hangar_ih
#include “hangar.hh”
namespace Soar{
#endif
#endif
Listing 15.2 Java AirCraft Implementation
package Loader;
import IE.Iona.OrbixWeb.Features.LoaderClass;
import Soar.*;
package Loader;
import Soar.*;
import IE.Iona.OrbixWeb.Features.LoaderClass;
include “hangar.hpp”
#inclide <iostream.h>
CORBA::Long Soar::Aircraft_i::identifier(
CORBA::Environment &IT_env){
return id;
}
Soar::Hangar_i::Hangar_i(
const char* marker, CORBA::LoaderClass* loader){
aircraft = new Soar::Aircraft_i(“AirCraftOne”,t_loader);
}
Soar::Hangar_i::~Hangar_i(){
CORBA::release(aircraft);
}
Soar::Aircraft_ptr Soar::Hangar_i::CheckOutAircraft (
CORBA::Long identifier, CORBA::Environment &IT_env) {
return Soar::Aircraft::_duplicate(aircraft);
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Implementing a Loader
As mentioned earlier, the loader has to be a subclass of CORBA::LoaderClass (or
Corba.LoaderClass in Java). Further, it needs to override the necessary methods. This means that if
you need to specialize any of the methods that are specified in the CORBA::LoaderClass (or
Corba.LoaderClass), it can be done in the subclass. Listing 15.5 is a simple loader interface.
Listing 15.5 A Simple Loader Interface
#include <CORBA.h>
#include <string.h>
#include “hangar.hpp”
public:
loader ();
virtual ~loader ();
virtual CORBA::ObjectRef load (const char* interfce,
const char* marker, CORBA::Boolean isBind, CORBA::Environment& env);
virtual void save (CORBA::Object*, CORBA::saveReason reason,
CORBA::Environment&);
};
The loader overrides the load() and save() methods. The load() method is called when an object
needs to be loaded. The save() method is called when the object is deleted or if the process is deleted.
Later in this chapter, you learn how persistence could be provided to these classes as well.
Listing 15.6 shows only the implementation of the loader class.
Listing 15.6 A Simple Loader Implementation
#include “loader.hpp”
#include <iostream.h>
#include <string.h>
loader::loader ():CORBA::LoaderClass(1){}
loader::~loader () {}
CORBA::ObjectRef retValue;
if(strcmp(marker,“”)){
Soar::Aircraft_ptr aircraft = new Soar::Aircraft_i(marker, this);
cout << “[LOADER]: ”<<marker<< “ Loaded”<< endl;
retValue = Soar::Aircraft::_duplicate(aircraft);
}
if(strcmp(marker,“Hangar51”)){
Soar::Hangar_ptr hangar = new Soar::Hangar_i(marker, this);
cout << “[LOADER]: ”<<marker<< “ Loaded”<< endl;
retValue = Soar::Hangar::_duplicate(hangar);
}
else{
cout << “[LOADER]: ”<<marker<< “ Not Loaded”<< endl;
}
return retValue;
}
if(reason == CORBA::processTermination){
const char *marker = obj->_marker ();
if(strcmp(marker,“Hangar51”){
Soar::Hangar *hangar = (Soar::Hangar*)obj
hangar->cleanOut();
}
cout<<“[LOADER]:Save called for ”<< marker << endl;
CORBA::release(obj);
}
The cleanOut() operation could be implemented to accomplish any number of things. This could do things
such as logging or persistence of state data. If persistence is accomplished, this data could be read back when
the load() method is called so that the state of an object could be initialized to a state that was saved before
the object was deactivated.
Logging can be accomplished through a simple text log. A more sophisticated log could use a database to
capture the log information. Persistence can be accomplished by using either serialization or through the use of
an object- oriented database.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Implementing a loader in Java is reasonably similar to C++. The only major distinction between them is that
Java does memory management differently. As far as Orbix and OrbixWeb is concerned, the Application
Programming Interfaces are fairly similar. The superclass that has to be subclassed to construct a loader is
defined in IE.Iona.OrbixWeb.Features.LoaderClass. This class has to be subclassed and the
relevant methods overridden.
The first thing about the loader itself is that just as in C++, the Java loader has to be subclassed from
IE.Iona.OrbixWeb.CORBA.LoaderClass. In terms of the actual implementation, you then need to
determine which methods need to be overridden. In Listing 15.7, you will find an implementation of a loader
that closely mirrors the loader that was implemented earlier in C++.
Listing 15.7 Java Loader Implementation
package Loader;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.Object;
import IE.Iona.OrbixWeb.Features.LoaderClass;
import IE.Iona.OrbixWeb._CORBA;
import IE.Iona.OrbixWeb._OrbixWeb;
import org.omg.CORBA.StringHolder;
import java.io.*;
public Loader() {
super (true);
try{
FileWriter file = new FileWriter(“loader.log”);
log = new PrintWriter(file,true);
}
catch(IOException e){
System.out.println(e.toString());
System.exit(-1);
}
}
if(marker.equals(“”)){
Soar.Aircraft returnAircraft = new Loader.Aircraft_i(marker,this);
String message = “[LOADER] ” + marker + “ Loaded.”;
System.out.println(message);
log.println(message);
returnobject = returnAircraft;
}
if(marker.equals(“Hangar51”)){
Soar.Hangar returnHangar = new Loader.Hangar_i(marker,this);
String message = “[LOADER] ” + marker + “ Loaded.”;
System.out.println(message);
log.println(message);
returnobject = returnHangar;
}
return returnobject;
}
if (reason == _CORBA.processTermination){
message = “Save Called because reason = _CORBA.processTermination”;
System.out.println(message);
log.println(message);
}
else if (reason == _CORBA.explicitCall){
message = “Save Called because reason = _CORBA.explicitCall”;
System.out.println(message);
log.println(message);
}
else if (reason == _CORBA.objectDeletion){
message = “Save Called because reason = _CORBA.objectDeletion”;
System.out.println(message);
log.println(message);
}
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The save() method can be called under three different circumstances:
• processTermination A process termination is triggered when a server exits. At that
point, the save() method on the loader instance will be called. This point can be useful for
general “cleaning up” of a system. For instance, in Listing 15.7, the log file is closed when the
save() is called because of process termination.
• explicitCall An explicit call is made when a process or an object explicitly invokes the
save() method on the loader instance.
• objectDeletion When an object that is controlled by the loader is deleted, the save()
method is called. As mentioned earlier, this would be a good place to provide persistence for
the objects that are controlled by the loader process.
Note:
Another, perhaps more important, action that can be taken on all three types of reasons for the save()
method being called is to enable the objects that are being controlled by the loader to save their state to
some medium such as a database of a file system. Of course, the benefit of the loader is to enable these
objects to also internalize their state when the load() method is called.
#include <iostream.h>
#include “loader.hpp”
try {
CORBA::Orbix.impl_is_ready(“Area51”);
}
catch (CORBA::SystemException &sysEx) {
cerr << “Unexpected system exception” << endl;
cerr << &sysEx;
}
catch(...) {
cerr << “Unexpected exception” << endl;
}
}
Listing 15.9 Java Server
package Loader;
import com.sun.java.swing.*;
import IE.Iona.OrbixWeb._CORBA;
import IE.Iona.OrbixWeb._OrbixWeb;
import org.omg.CORBA.ORB;
import org.omg.CORBA.SystemException;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
}
public LoaderServer() {
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
}
void StopButton_mouseClicked(MouseEvent e) {
lthread.stop();
StartButton.enable(true);
StopButton.enable(false);
}
void StartButton_mouseClicked(MouseEvent e) {
lthread = new LoaderThread();
thread = new Thread(lthread);
thread.start();
StartButton.enable(false);
StopButton.enable(true);
}
void this_windowClosing(WindowEvent e) {
System.exit(0);
}
}
try {
_CORBA.Orbix.impl_is_ready(“Area51”);
}
catch(SystemException se) {
System.out.println(
“Exception during creation of construction of Area51: ”
+ se.toString());
return;
}
System.out.println(“ Server Exiting ...”);
_CORBA.Orbix.finalize();
}
public void stop(){
}
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Note that in the code, neither the Hangar nor the Aircraft is instantiated. The loader will
construct them when an invocation comes in from a client. In Listings 15.8 and 15.9, besides the
instantiation of the loader, no other object is constructed. When a request for a particular object does
come in, the loader will dynamically construct and return the object through the load() method.
Implementation of the client is just as simple as implementation of the server. Listings 15.10 and
15.11 illustrate this implementation.
Listing 15.10 C++ Client
#include <iostream.h>
#include “hangar.hh”
Soar::Hangar_ptr hangar;
try {
hangar = Soar::Hangar::_bind (“Hangar51:Area51”);
}
catch (CORBA::SystemException &sysEx) {
cerr << “Unexpected system exception” << endl;
cerr << &sysEx;
}
catch(...) {
cerr << “Unexpected exception” << endl;
}
try{
Soar::Aircraft_ptr aircraft = hangar->ChecOutAircraft(1554);
CORBA::release(aircraft);
}
catch (CORBA::SystemException &sysEx) {
cerr << “Unexpected system exception” << endl;
cerr << &sysEx;
}
CORBA::release(hangar);
}
Listing 15.11 JAVA CLIENT
package Loader;
import IE.Iona.OrbixWeb._OrbixWeb;
import IE.Iona.OrbixWeb._CORBA;
import org.omg.CORBA.ORB;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.IntHolder;
public LoaderClient() {
try{
hangar = Soar.HangarHelper.bind(“Hangar51:Area51”);
aircraft = Soar.AircraftHelper.bind(“:Area51”);
}
catch(SystemException se) {
System.out.println(
“Exception during retrieval of the Hangar object reference”
+ se.toString());
return;
}
try{
aircraft.identifier(5001);
hangar.CheckInAircraft(aircraft);
Soar.Aircraft temp = hangar.CheckOutAircraft(5001);
}
catch(SystemException se) {
System.out.println(
“Exception during retrieval of the Hangar object reference”
+ se.toString());
return;
}
}
Summary
Orbix loaders enable the dynamic loading of classes. This could be a benefit in designing flexible
systems that could intercept an incoming request for objects. Depending on the name of the object
that is requested to be loaded, the loader can load the object. If can also load different
implementations of the same interface.
This can be used in security features if required. The other benefit of the loader is that it also gets
called when an object is deactivated or if the process is naturally terminated. The save() method
enables an object to save data that needs to be persistent. It can also log important data. The object
can use the state that is made persistent when it is loaded again so that the state of the object can be
long-lived.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 16
Distributed Callbacks
In This Chapter
• Defining Callback Interfaces
• Asynchronous Callbacks
• The Client Interface Implementation
Callbacks are a mechanism of coupling components. The use of the word components
is intentional. It is meant to denote a system where the subcomponents are essentially
“black box” elements. In such a system, designers try to ensure as little coupling as
possible. This leads to systems that are inherently more flexible and scalable.
One of the ways of achieving this is by designing systems that do callbacks. Using this
technique, a client registers with a server for notification of a state change or
something similar through callbacks. The callback registration interface needs to be
kept as simple as possible so that the dependency introduced between client and server
is minimized. Another approach is to standardize the callback interfaces through
standard coding conventions so that the server will always know what operation to
invoke on the client. This is the approach taken by the JavaBeans event notification
design. This standardization should be two-sided. The client interfaces for notification
and the server interfaces for registration should be standard.
Callbacks allow the design of systems that are driven by the server. If asynchronicity
is introduced using oneway calls from the client to the server, callbacks will enable a
mechanism for the server to notify the client if the operation succeeded. Another
possibility is to use callbacks as a simpler and lighter alternative to the CORBA Event
Services that implement the push mechanism. Using callbacks, especially in a
peer-to-peer CORBA application, interdependencies can be managed by using
callback interfaces that enable the various peers to notify each other when conditions
are met.
Callbacks in themselves are rather simple to implement. However, care should be
taken to use the callback mechanism judiciously. If used, the interfaces need to be
defined to be consistent across the entire architecture. This would allow the callback
interfaces to be reused across the architecture.
Areas where callbacks can be used include simple push architectures. They are also an
ideal pattern when a different component of the architecture needs to be synchronized
to a consistent state. In this chapter, we will build the latter to demonstrate callbacks.
The chat server needs to do two things. Its primary function is to act as a gateway for
chat clients. Its other function is to enable clients to register themselves for callbacks
so that chat messages can be passed on to the appropriate client. Any number of
clients can be logged into the server. The intricacies of concurrency are beyond the
scope of the chapter. However, one constraint that can be imposed on the
implementation is that authors of messages should not be notified of their own
messages.
The following interface is simple enough to demonstate how a chat server can be built:
module callback{
interface ChatParty{
attribute string name;
void chatListener(in string message);
};
interface chatServer{
void registerParty( in ChatParty party);
void deregister(in ChatParty party);
void chat(in ChatParty party, in string message);
};
};
chat.idl specifies two interfaces. One will be implemented as a chatServer,
and the other as a chatClient. Both these are to be implemented as servers.
Depending on the roles the interfaces are playing, one of them will always be a client
while the other is the server.
The primary operations for allowing a client to be registered for notifications or
deregistration are the registerParty() and deregister() operations. For
registration or deregistration, the chatServer is treated as a server. After it is
registered, a chatParty can send messages to others by invoking the chat()
operation. When the chatServer receives the invocation through this operation, the
server then chooses the client role and invokes the chatListener() method to
notify the client of what other clients have sent to the chatServer. Listing 16.1
shows the implementation of the chatParty interface.
Listing 16.1 Implementing the chatParty Interface
package callback;
import com.sun.java.swing.*;
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Implementing the chatParty Client/Server
The client code presented in Listing 16.2 is, for most part, GUI-related. All the server-related code is in the
chatClientServer class.
Listing 16.2 The Chat Client
package callback;
import java.awt.*;
import com.sun.java.swing.*;
import java.awt.event.*;
void LoginButton_mouseClicked(MouseEvent e) {
party.register(LoginName.getText());
chatSrv = party.getChatServer();
thread = new Thread(party);
thread.start();
}
void Logoutbutton_mouseClicked(MouseEvent e) {
party.deregister();
thread.stop();
}
void ChatButton_mouseClicked(MouseEvent e) {
String msg = LocalWindow.getText();
try{
chatSrv.chat(LoginName.getText(),msg);
}
catch(org.omg.CORBA.SystemException e){
System.out.println(e.toString());
}
}
}
//The Server Implementation
class chatClientServer implements Runnable{
private org.omg.CORBA.ORB orb;
private org.omg.CORBA.BOA boa;
private callback.ChatPty party;
private com.visigenic.vbroker.URLNaming.Resolver resolver = null;
private callback.chatServer chatSrv = null;
chatClientServer(JTextArea RemoteWindow){
orb = org.omg.CORBA.ORB.init();
boa = orb.BOA_init();
party = new callback.ChatPty(“Party”, RemoteWindow);
try{
resolver =
com.visigenic.vbroker.URLNaming.ResolverHelper.narrow(
orb.resolve_initial_references(“URLNamingResolver”));
org.omg.CORBA.Object obj = resolver.locate(
“http://localhost:15000/chatserver.ior”);
chatSrv = callback.chatServerHelper.narrow(obj);
}
catch(org.omg.CORBA.SystemException e){
e.printStackTrace();
}
catch(org.omg.CORBA.ORBPackage.InvalidName ivn){
ivn.printStackTrace();
}
catch(com.visigenic.vbroker.URLNaming.ReqFailure rf){
rf.printStackTrace();
}
catch(com.visigenic.vbroker.URLNaming.CommFailure cf){
cf.printStackTrace();
}
catch(com.visigenic.vbroker.URLNaming.InvalidURL ivu){
ivu.printStackTrace();
}
}
callback.chatServer getChatServer(){
return chatSrv;
}
}
An instance of chatClientServer is created. This is accomplished in the line
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The logic of the implementation is in the user-generated events. When the user clicks login, the
chatParty is registered with the chatServer. The reverse occurs when deregister is called on the
chatServer. When the chat button is clicked, the message that was typed in the LocalWindow is sent
to the chatServer.
The chatClientServer is implemented as a thread so that the user interface can be operated upon,
independent of the chatClientServer. This is required because the impl_is_ready() blocks until
the server exists. When implemented, the server can be launched just as any other CORBA server.
The chatServer will use a JGL stack to keep a list of chatPartys in memory. When a request comes
in, the implementation will iterate over the stack and invoke the chatListener() operation on the
chatClient. Listing 16.3 shows the implementation of chatServer.
Listing 16.3 The Chat Server
package callback;
import com.objectspace.jgl.*;
import java.util.*;
The runtime to host the chatServer implementation is typical of most of the servers we have
constructed. It uses the VisiBroker URL naming to register the IOR so that a client can easily bind to the
server, as shown in Listing 16.4.
Listing 16.4 The Chat Server Runtime
package callback;
public ChatServerImpl() {
try{
orb = org.omg.CORBA.ORB.init();
boa = orb.BOA_init();
chatserver = new callback.ChatSrv(“ChatServer”);
resolver =
com.visigenic.vbroker.URLNaming.ResolverHelper.narrow(
orb.resolve_initial_references(“URLNamingResolver”));
resolver.force_register_url(
“http://localhost:15000/chatserver.ior”,chatserver);
}
catch(org.omg.CORBA.SystemException e){
e.printStackTrace();
}
catch(org.omg.CORBA.ORBPackage.InvalidName ivn){
ivn.printStackTrace();
}
catch(com.visigenic.vbroker.URLNaming.ReqFailure rf){
rf.printStackTrace();
}
catch(com.visigenic.vbroker.URLNaming.CommFailure cf){
cf.printStackTrace();
}
catch(com.visigenic.vbroker.URLNaming.InvalidURL ivu){
ivu.printStackTrace();
}
try{
boa.impl_is_ready();
}
catch(org.omg.CORBA.SystemException e){
e.printStackTrace();
}
}
Let’s implement another callback application, this time using OrbixWeb. The use of some ORB application
programming interfaces to enable callbacks will differentiate the OrbixWeb implementation from the
VisiBroker. A further distinction is the use of asynchronous callbacks in the OrbixWeb application.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Asynchronous Callbacks
The default invocation semantics that CORBA enables is a blocking call from the client to a service. This
means that for the duration of the invocation, the client blocks until the service returns from the invocation.
Callbacks are one requirement where this type of invocation might be undesirable.
An asynchronous invocation by the client means that the invocation immediately returns from the call,
enabling the client to continue with autonomous processing. This is accomplished if operations on the
interface are specified as oneway. When an operation is specified as being oneway, a client that makes
the invocation is immediately returned control from the call. The oneway calls do have some constraints
placed on them. For one, the operations that are specified as being oneway cannot return anything to the
client. This means that the only type that can be returned is a void. The other restriction is that an oneway
invocation cannot throw any exception. A service that implements a oneway operation is not required to
notify the client if the operation succeeds or not.
A simple interface will illustrate how callbacks are accomplished asynchronously. The basic principles are
the same as that for the VisiBroker Java implementation. In the VisiBroker application, we leveraged the
peer-to-peer nature of CORBA. This enabled us to reverse the roles of clients and servers as and when it
suited us. The OrbixWeb application will be a little more “traditional”: There will be clearly defined client
and server roles. The following interface shows how asynchronous callbacks can be constructed:
module Callback{
interface client{
void notify(in string message);
};
interface server{
oneway void registerReference(in Callback::client aClient);
};
};
The single operation in the server interface is specified as oneway. This enables the Callback.client
to invoke the operation asynchronously. The next sections implement the server and then the client.
Every client that is interested in having the CallbackService object asychronously callback on the
registered interface is kept in an instance of Java.util.Vector. The CallbackService
implements the Callback.server interface. It also implements the Java Runnable interface so that
you can execute the service in a thread.
The callback is made in the run() method. Every client that is registered with the server object is
retrieved and the notify() method is invoked on it. This callback from the server does not go through
the ORB. The consequence of this aspect is that the CallbackService object does not have to retrieve
the Callback.client interface through a bind() or other mechanism. The server can directly invoke
the method on the client. The invocation is made in the run() method so that it does not interfere with the
normal running of the server that host the CallbackService object. Listing 16.5 shows the
implementation of the asynchronous server.
Listing 16.5 The Asynchronous Server Implementation
package Callbacks;
import Callback.*;
import java.util.Vector;
import com.sun.java.swing.*;
The implementation shown in Listing 16.6 is very similar to the other servers that we have implemented
using OrbixWeb. The constructor CallbackServer() initializes the CallbackService object,
passing it an instance of the GUI object. CallbackService is then executed in an instance of a
Thread object so that the implementation can execute the callback routines without interfering with the
server’s execution.
Listing 16.6 The Callback Server
package Callbacks;
import java.util.*;
import IE.Iona.OrbixWeb._OrbixWeb;
import IE.Iona.OrbixWeb._CORBA;
import org.omg.CORBA.ORB;
import com.sun.java.swing.*;
CallbackService cs = null;
JTextArea DisplayWindow = null;
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The Callback Server User Interface
The user interface, as shown in Listing 16.7, is a simple JText object to display any messages that the
service or the server object wants to display. The CallbackServer is constructed when the user clicks the
Start button. When this event occurs, the CallbackServer is constructed and launched in a thread. When
the server is up and running, it just waits for a Callback client to register its object reference for callback.
Listing 16.7 The Callback Server User Interface
package Callbacks;
import java.awt.*;
import com.sun.java.swing.*;
import java.awt.event.*;
public CallbackServerWindow() {
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
}
void StartButton_mouseClicked(MouseEvent e) {
cs = new CallbackServer(DisplayWindow);
thread = new Thread(cs);
thread.start();
}
}
We can now construct a client to make the callback on the CallbackServer.
package Callbacks;
import com.sun.java.swing.*;
import java.awt.*;
import java.awt.event.*;
import IE.Iona.OrbixWeb._CORBA;
import org.omg.CORBA.SystemException;
class CallbackClient extends Callback._clientImplBase implements Runnable{
private JTextArea DisplayWindow;
try{
Callback.server srv = Callback.serverHelper.bind(“:Callback”);
srv.registerReference(this);
}
catch (SystemException se) {
System.out.println(“Unexpected exception:\n”+ se.toString());
}
}
srv.registerReference(this);
When the registration is completed, the core of the client code will be executed. This occurs in the following
lines of code, which are executed in the run() method of the client code:
try {
_CORBA.Orbix.processEvents();
}
catch (SystemException se) {
System.out.println(“Unexpected exception:\n” + se.toString());
return;
}
_CORBA.Orbix.processEvents() waits for incoming events from the Callback server. When this
occurs, methods on the callback interface on the client side are invoked to notify the client that an event
arrived from the server.
Next, we implement a user interface to host the Callback.client object.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The Callback Client GUI
The callback client GUI, as shown in Listing 16.9, is very simple. The user interface is very similar to
the server user interface. A window is created to display the server callback messages. The user
interface button enables a user to click on it to launch the Client object on a thread. When that
occurs, the client connects to the server and registers its interface to enable the server to make a
callback.
Listing 16.9 The Callback Client User Interface
package Callbacks;
import com.sun.java.swing.*;
import java.awt.*;
import java.awt.event.*;
import IE.Iona.OrbixWeb._CORBA;
import org.omg.CORBA.SystemException;
public CallbackClientWindow() {
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
}
void ListenButton_mouseClicked(MouseEvent e) {
cc = new CallbackClient(DisplayWindow);
thread = new Thread(cc);
thread.start();
}
}
As you can see, the VisiBroker and OrbixWeb callback strategies are similar.
Summary
Callback is a mechanism that enables coupling of clients and servers in a bidirectional manner. This
enables architectures that are driven by both tiers of a client/server relationship. One of the pitfalls of
this type of design is that it introduces all sorts of dependencies between the client and server. This can
be mitigated to a large extent if the design adheres to a standardized interface throughout the
architecture.
Callbacks can be designed as either synchronous or asynchronous callbacks. Both can be used to
benefit an architecture. Asynchronous callbacks can be useful when a client needs to autonomously
process while it makes an invocation on the server. Asynchronous callbacks, on the other hand, enable
a client to wait for a server while the server completes some processing on its end.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 17
VisiBroker Caffeine
In This Chapter
• Java to IIOP
• Implementing a Java2iiop Application
• Constructing the Server
• Implementing a Java2iiop Client
The Caffeine extensions from Visigenic are extensions of the standard CORBA specification.
As a product, it is in the forefront of CORBA—Java integration. Application level
development is rather cumbersome in C++. In this type of environment, the emphasis is most
likely to be on the generation of user interfaces. C++ is my language of choice for
general-purpose systems level programming. By this, I mean programming general
architectural services. Java, on the other hand, is ideally suited for more general level
programming, because it enables you to test your assumptions and show it off to the user.
Just as Caffeine extends CORBA, Caffeine is also an attempt to extend this usefulness of Java
to distributed computing. Using Caffeine, you can design and implement CORBA
components in Java. The development process using Caffeine works largely like Java RMI. It
enables a developer to work with the constructs that Java provides. At any point, if the design
requires these components to be distributed, they can use the tools provided with Caffeine.
The Caffeine enhancements come with three components:
• The Java2iiop compiler
• The Java2idl compiler
• The URL Naming facility
Java2iiop
Java2iiop enables an application to work with IIOP-compliant stubs and skeletons. It is ideal
for a Java-only implementation. Using Java2iiop, you could pass a Java interface and the
compiler would generate IIOP-compliant stubs, skeletons, and support files.
Java2idl
The Java2idl compiler turns normal Java interfaces to idl. If certain constraints are adhered to,
the compiler generates OMG IDL. If usage of Java is pushed to constructions that may not
have support in IDL, VisiBroker IDL is generated. This idl cannot be used outside the
VisiBroker for Java environment.
URL Naming
URL Naming is a nifty feature that enables a Java-based application to refer to an object with
an URL.
Java to IIOP
The starting point of Java2iiop is a Java interface. Within this Java interface, you can define a
distributable public interface for which you can generate the CORBA support code. The
benefit of such a scheme is that an applications developer is working only with Java
interfaces, no IDL. As long as you understand one of the major reasons for having OMG IDL,
you can use this feature. The OMG IDL is used to provide language independence to
CORBA. If there is no IDL, there is no language independence on the fly. Therefore, if Java is
the only platform in your distributed application, Java2iiop may not be such a bad thing.
It is possible to generate OMG-compliant IDL using Java2iiop and Java2IDL. Then it stands
to reason that it is a lot easier to start with OMG IDL in the first place! I would use Caffeine
when OMG IDL is no longer enough to implement all my requirements without undue
heartache. Caffeine is an extension to CORBA. As such, it should be used only when standard
CORBA is not enough.
For instance, it would be useful to pass objects such as Vectors or Hashtables between
clients and servers. IDL enables you to pass a sequence or an array. For example, a data
access server that provides access to databases could find it useful to pass Hashtables
between server and client.
All Java interfaces must be subclasses of org.omg.CORBA.Object. Only such interfaces
can be used with Java2iiop. This marker enables the Java2iiop tool to recognize whether an
interface is a Caffeine interface. On finding the marker, the compiler generates all the
marshaling and demarshaling code. The rest of the development process is mostly typical to
CORBA.
This brings up the question: When could I use Caffeine?. Use Caffeine if requirements dictate
that the full power and flexibility of Java as a language be utilized for a solution. Such a
solution would be an all-Java affair, because the solution would be using IDL that the rest of
CORBA would not be able to understand. The following example demonstrates one such use.
Before getting into the details of development, let’s define a Caffeine interface in Java:
The First step in the Caffeine development process is to define a Java interface that subclasses
from org.omg.CORBA.Object:
package Java2iiop;
Note:
Keep in mind the semantics of pass-by-value: A copy is passed back, not an object reference.
This means that an action on the copy is local and not transferred back to the original.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Implementing a Java2iiop Application
Take the following steps to construct a Java2iiop application:
1. Define a Java interface subclassed from org.omg.CORBA.Object.
2. Compile the interface produced in step one using the javac compiler.
3. On the Java bytecode produced by step two, run the Java2iiop compiler.
The Java2iiop compiler produces the CORBA stubs and skeletons source
files.
At this point, implementation of the interface can be done as with any other
CORBA implementation.
4. Implement the Server.
5. Compile all the source files using the Javac bytecode pre-compiler.
6. Deploy the server.
7. Implement the client to use the server interface.
8. Compile and run the client.
The development process is depicted in Figure 17.1.
package Java2iiop;
import com.sun.java.swing.*;
public DistributedJava(java.lang.String
name,com.objectspace.jgl.Stack stack,DistributedModel
model,JTextArea gui) {
super(name);
this.stack = stack;
this.model = model;
this.gui = gui;
}
public DistributedJava() {
super();
}
public java.lang.Object getTableModel() {
return model;
}
public java.lang.Object getJGLContainter() {
return stack;
}
public java.lang.Object getGuiObject() {
return gui;
}
}
The coupling between the component and the rest of the application is
implemented with the following constructor:
Note:
Swing uses the Smalltalk-inspired Model View Controller pattern to couple user
interfaces to data. Almost all the user interfaces that make up a Swing have an
associated Model object that encapsulates data. In the previous case, Jtable
objects have a corresponding Model object that is derived from the the
DefaultTableModel class. In the previous discussion, this object is referred
to as the Datamodel.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Listing 17.2 Java2iiop Server
package Java2iiop;
import java.awt.*;
import java.util.*;
import com.sun.java.swing.table.*;
import com.sun.java.swing.event.TableModelListener;
import com.sun.java.swing.event.TableModelEvent;
import com.sun.java.swing.*;
import com.objectspace.jgl.*;
import java.awt.event.*;
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
}
t_stackObject.push(temp_[0]);
t_stackObject.push(temp_[1]);
GuiObject.setText(“This text is from the remote object”);
this.setVisible(true);
}
void StartButton_mouseClicked(MouseEvent e) {
orb_ = new orbRuntime(arg, t_stackObject, t_tabelModel, GuiObject);
Thread thread = new Thread(orb_);
thread.start();
}
void StopButton_mouseClicked(MouseEvent e) {
this.dispose();
}
}
public DistributedModel(){
}
orbRuntime(String[] args,com.objectspace.jgl.Stack
stack,DistributedModel model,JTextArea gui){
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args,null);
boa = orb.BOA_init();
RuntimeInterface impl = new DistributedJava(“Server”,stack,model,gui);
boa.obj_is_ready(impl);
System.out.println(impl + “ is ready.”);
}
try{
boa.impl_is_ready();
}
catch(org.omg.CORBA.SystemException e){
e.toString();
}
}
}
The CORBA component is initialized in the constructor for the orbRuntime:
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The three objects initialized by the server are passed in the CORBA component. Because
Java2iiop passes objects around by value, the object state as the server initialized them is passed
to the client. The Datamodel is initialized with just one row. The stack object contains only one
object. The GuiObject is initialized with a line of text, “This text is from the
remote object”. The establishes the state of the GuiObject, which is an instance of
JText.
This ends the implementation of the server object. All that remains is to implement a client that
uses the Java2iiop interface.
package Java2iiop;
import java.awt.*;
import java.util.*;
import java.awt.event.*;
import com.sun.java.swing.*;
try{
t_tabelModel = (DistributedModel)
remoteObject.getTableModel();
TableObject.setModel(t_tabelModel);
GuiObject = (JTextArea)remoteObject.getGuiObject();
t_stackObject = (com.objectspace.jgl.Stack)
remoteObject.getJGLContainter();
GuiObject.append(“\n” + t_stackObject.toString());
}
catch(org.omg.CORBA.SystemException e){
e.toString();
}
catch(Exception ex){
ex.toString();
}
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
}
private void jbInit() throws Exception {
this.setTitle(“Pass By Value Client”);
jScrollPane1.setBounds(new Rectangle(6, 127, 415, 112));
jScrollPane2.setBounds(new Rectangle(7, 5, 413, 120));
this.getContentPane().setLayout(null);
this.getContentPane().add(jScrollPane1, null);
jScrollPane1.getViewport().add(GuiObject, null);
this.getContentPane().add(jScrollPane2, null);
jScrollPane2.getViewport().add(TableObject, null);
this.setVisible(true);
}
t_tabelModel = (DistributedModel)remoteObject.getTableModel();
TableObject.setModel(t_tabelModel);
GuiObject = (JTextArea)remoteObject.getGuiObject();
t_stackObject = (com.objectspace.jgl.Stack)
remoteObject.getJGLContainter();
GuiObject.append(“\n” + t_stackObject.toString());
All the methods defined by the Java2iiop are specified to return java.lang.Object.
Therefore, the implementation has to explicitly cast into the desired object. The state of the
objects is the same as it was when we initialized the objects on the server side. This means that
the state of the object is copied by value over to the client side. This does not mean, however,
that modifying the state of the object is transparently transmitted back to the server.
The finished application is shown executing in Figure 17.2.
Summary
The Caffeine extensions from Visigenic are a powerful extension over the OMG-defined
standard for CORBA. If a CORBA application uses only Java, it is a technology that enables
CORBA to have pass-by-value semantics. Using pass-by-value, a client can retrieve a server
component by value. This enables the state of a component to be distributed as well.
The Caffeine product enables a developer to work entirely in Java. The Java2iiop technology
enables a developer to generate iiop compliant from Java interfaces. When this is done, any Java
Language construction can be passed between client and server. Care must be taken with such
design on two levels: First, it is quite easy to build implementations that can work only with
Visigenic. The second, more important pitfall is that a designer might inadvertently introduce
multidimensional dependencies between distributed components. An indication of this habit was
briefly touched on when an unnecessary coupling was introduced between client and server in
the previous implementation.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 18
VisiBroker SmartStubs
In This Chapter
• Internals of a SmartStub
• Constructing a SmartStub
Interactions between clients and servers are rather simple in their mechanisms. Architecturally, a
server is coupled to a skeleton, and a client is connected to a stub. Invocations in either direction are
marshaled and demarshaled by these components.
An invocation going in from the client side is marshaled by the stubs and passed to the ORB. A
server receiving an invocation receives the invocation after it has been demarshaled by a skeleton.
The reverse of this process is done when the server responds to the invocation. VisiBroker enables
you to modify the proxy on the client side. SmartStubs are a mechanism by which the stubs on the
client side can be modified to interpose between the client and the server.
SmartStubs can intercept the messages before they are marshaled or demarshaled on the client side.
The SmartStub, in turn, can invoke the methods on the actual stub that the VisiBroker idl2java
pre-compiler generates to make the invocation on the server. The difference is shown in Figures
18.1 and 18.2.
Functionally, SmartStubs and Interceptors seem similar. However, they are different in fundamental
respects. VisiBroker Interceptors are more general and low-level than SmartStubs. Interceptors are
installed with the ORB. As such, they can be used on the client side or the server side. Interceptors
can redirect invocations at a very low level. SmartStubs are specific for each interface, whereas
Interceptors can be generalized to be useful with any client. As a result, the general areas of using
SmartStubs are rather limited. Among the areas where you could use SmartStubs are the following:
• Logging—Potentially any methods you specify in an interface could be designed to be
invoked through a SmartStub. This feature can be used to transparently log the client
invocations and server responses. This means that when a client invokes a method on an
interface, the SmartStub could be designed to intercept the invocation and log information to
some form of persistent log. Such logging could be used as a trace log, or perhaps you could
establish nonrepudiation parameters on a client requesting a service from a server.
• Network invocations—No matter how efficient the protocol is, it is still slower than a local
method call. Proxies, whether normal stubs or SmartStubs are statically bound to a client. If a
SmartStub is implemented to cache state information that a server responds with, the next
time the client makes a repeat invocation on the server it could have the SmartStub return the
cached information. This means that repeated invocations on the server object would be
reduced. When an invocation is made by the client, the SmartStub could respond with a
cached result. The parameters for what information is cached are, of course, dependent on the
fact that the information the server provides is more-or-less invariant in time. If this
invariance cannot be established, an expiration time on the state of the cached information
would have to be determined to get rid of state information.
Such a mechanism could be used in a variety of applications. Consider an application that
uses real-time data—for example, a weather-monitoring application. This application usually
uses polling to retrieve data from the monitoring station. Polling is usually inefficient because
it tends to repeat over a predetermined interval of time. This application can be modified to
use SmartStubs to reduce the network communication by providing the client with cached
information.
Internals of a SmartStub
Extending a normal stub generated by the VisiBroker idl2java pre-compiler creates SmartStubs. The
pre-compiler specifies a switch that generates SmartStub support. Running the following command:
module smartStub{
interface proxy{
};
};
This interface needs to be pre-compiled with the idl2Java pre-compiler that VisiBroker provides.
Run this interface through the pre-compiler using the following command:
Note:
You can create any number of specific SmartStubs and, depending on the context of the client,
you can specify that one or the other is loaded.
• getStubClass() enables you to access the current stub that is being used. Perhaps you
could run the Java operator instanceof to figure out what stub is being used.
• resetStub() method releases the SmartStub and relinquishes control to the normal
default stub.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
These methods are defined on the helper class for the interface. The next code snippet illustrates
how SmartStubs can be used with the previous API.
The interface that was specified previously is named proxy. The IDL pre-compiler generates a
holder class for this interface, which is named proxyholder. The previously mentioned
methods are specified in this class. So if you want to invoke these methods, you could do the
following:
proxyholder.setStubClass(..);
proxyholder.getStubClass();
proxyholder.resetStub(..)
Constructing a SmartStub
Developing a SmartStub is reasonably simple. In terms of this development, the effect on the rest of
your normal development is almost zero. You still build your servers the way you always do. The
client has a minor change; you use the setStubClass() to install the SmartStub with the client.
The interface that was defined previously will now be developed in full to show how SmartStubs
can be used. More specifically, you create a simple SmartStub to do logging:
module smartStub{
interface proxy{
};
};
The interface specifies three methods. The first one passes a string object to the server. For
performance reasons it is possible that if the same string is sent to the server, the invocation need
not be made. Instead, the proxy could be programmed to do nothing. The second method returns a
string. Here, there is a clear case for the invocation not being made at all after the string has been
retrieved. The third method is a little trickier. There is one inout parameter and an out
parameter. It can be argued that the server might choose to send over whatever it likes. This means
that the SmartStub really cannot cache anything locally, just in case it gets it wrong. On top of all
this, the logging bit has to be implemented as well.
You could use the SmartStub to override the newConnection() and the
getConnectionName() methods and not the getParameters() method. But for clarity, we
will implement support for all the methods.
Run the idl2java pre-compiler on the interface. Here is the exact command:
}
The implementation of the interface is standard as advertised. For instance, the
getParameters() method is passed two StringHolder classes. As mentioned in Chapter 5,
“Java/IDL Mapping,” when complex types are passed as out or inout, the types map to a holder
class. All the implementation does is check out the inout parameter, set its state, and then set the
state of the out parameter.
The server is also standard. It advertises to the VisiBroker ORB that it is ready for business, as
shown in Listing 18.2.
Listing 18.2 The SmartStub Server Implementation
package smartStub;
You implement the SmartStub by subclassing a normal stub and providing value added support. In
this case, the idl2java pre-compiler would generate a class called _st_proxy. This class can be
extended to create a SmartStub. The implementation is shown in the next section (see Listing 18.3).
As discussed earlier, we will support all the operations that we specified in the proxy interface
definition. In order to build a SmartStub, you do the following:
1. Create a class that subclasses the appropriate stub class. In this case, you subclass the
st_proxy class.
2. Override all the methods that you want to handle in the SmartStub from the superclass. In
this example, we want to support the entire proxy interface in the SmartStub. This means that
we need to override all the methods.
Previous Table of Contents Next
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Implementation of the Proxy SmartStub Client
The SmartStub serves two purposes in this case. In the first instance, it logs every invocation. The
second purpose is to cache information so that unnecessary invocations to the server are not made. A
sample implementation is shown in Listing 18.3.
Listing 18.3 Implementation of the Proxy SmartStub
package smartStub;
import org.omg.CORBA.StringHolder;
import java.io.*;
import java.util.*;
public class SmartProxy extends _st_proxy{
String ConnectionName = null;
FileOutputStream logFile = null;
PrintWriter ios = null;
Date now = new Date();
public SmartProxy() {
try{
logFile = new FileOutputStream(“Log.dat”,true);
ios = new PrintWriter(logFile,true);
}
catch(IOException e){
System.out.println(e.toString());
}
ios.println(new String(“[Logfile]: Started ” + now.toString()));
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The first thing the SmartStub checks for is whether its own state variable is initialized. If it is not, it invokes the
newConnection() method on the server. It logs the invocation and then initializes its own state variable
with the one the client has passed to the server. This is essentially what the server is doing. So the state of the
server object is mirrored by the SmartStub. On the other hand, if the local state variable is initialized, the stub
checks whether the new string that is passed to the server is the same as before. Only if it is a new string does
the stub pass the invocation to the normal stub. Otherwise, it does nothing. Whenever an invocation is made on
the server, the stub always resets its own local variable to the string that is being passed to the server.
The getConnectionName() on the server is essentially the connection string the client passes to the server
when it invokes newConnection on the server. Because this state is mirrored in the stub, the implementation
calls getConnectionName() only if the stub’s state does not mirror that of the server object.
The getParameters() is not cached, because the state of the server cannot be mirrored on the SmartStub.
This essentially means that the server’s state, as far as this method is concerned, cannot be mirrored. Therefore,
this method is always passed on to the normal stub. The only thing the SmartStub does here is log the
invocation.
That completes the implementation of the SmartStub. Now let us look at the implementation of the client.
The only difference between normal clients and a client that uses a SmartStub is that a SmartStub has to be
installed, as shown in Listing 18.4.
Listing 18.4 Implementation of the Proxy Smart Client
package smartStub;
try{
remoteObject.newConnection(“scot:tiger”);
String connection = remoteObject.getConnectionNAme();
for(int i = 0; i<3; i++){
remoteObject.newConnection(“scot:tiger”);
}
}
catch(org.omg.CORBA.SystemException e){
System.out.println(e.toString());
}
try{
org.omg.CORBA.StringHolder status = new org.omg.CORBA.StringHolder();
status.value = new String(“Open”);
org.omg.CORBA.StringHolder params = new org.omg.CORBA.StringHolder();
remoteObject.getParameters(status,params);
System.out.println(status.value);
System.out.println(params.value);
}
catch(org.omg.CORBA.SystemException e){
System.out.println(“Holder ” + e.toString());
}
}
proxyHelper.setStubClass(SmartProxy.class)
This is executed before the normal initialization of the client. Once this has been done, the client can be
implemented as usual. The implementation invokes newConnection() with the same parameter,
scot:tiger, three times. The first time, the SmartStub makes a remote call. Additional times, the calls won’t
be made on the server object. This should be evident from this log, which is shown for a sample run:
Summary
SmartStubs are a VisiBroker feature that enables a developer to add value to the client side of a CORBA
client/server interaction. Unlike VisiBroker Interceptor or Orbix filters, SmartStubs are used only on the client
side. SmartStubs enable a developer to extend the default stub that is statically linked to a client. These
extensions can take a number of forms. They can be used for caching state information that is long-lived.
Caching minimizes the quantum of network traffic. Logging is another feature that is enabled using SmartStubs.
Logging on the client side enables a developer to record various information that could prove useful for fault
recovery or security audit.
Previous Table of Contents Next
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 19
Handling Distributed Events
In This Chapter
• Client-Side Event Handler
• The Registration Interface
• Installing a Handler
• Server-Side Handlers
VisiBroker provides an elegant event-handling mechanism that will notify clients and server
objects when a system event occurs. The event-handling mechanism is similar to the concept
of VisiBroker Interceptors except for the nature of events that are handled.
VisiBroker specifies two types of event handlers: one for the client side and one for the
server side. In the context of these events, the client can handle certain types of events, and
the server is capable of handling different kinds of events. The process of creating a handler
is the same for both the server and the client.
Specify and implement a communications event handler class. Within this class, you can
override any of the allowed methods, depending on where you intend to install the event
handler. Either the server side or the client side handler can handle the events shown in
Table 19.1.
Table 19.1 Event Types
struct ConnectionInfo{
string hostname;
long port;
};
This structure is passed as a parameter into the methods on the client side. It can be used to
figure out rudimentary information about the requested connection. Listing 19.1 illustrates
how a ClientEventHandler can be built.
Listing 19.1 Client Event Handler
import com.visigenic.vbroker.interceptor.*;
MyClientEventHandler(String name) {
Handler_name = name;
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The Registration Interface
With the ClientEventHandler built, it is necessary to register the handler. This is
accomplished by using the services of the HandlerRegistry interface. It provides a
number of operations that can be used to register a Handler. The interface is given in full
in Listing 19.2.
Listing 19.2 Registration Interface
interface HandlerRegistry {
void reg_obj_client_handler(
in Object obj,
in interceptor ClientEventHandler handler
)
raises(
interceptor HandlerExists,
interceptor InvalidObject
);
void reg_glob_client_handler(
in interceptor ClientEventHandler handler
)
raises(
interceptor HandlerExists
);
void reg_obj_impl_handler(
in Object obj,
in interceptor ImplEventHandler handler
)
raises(
interceptor HandlerExists,
interceptor InvalidObject
);
void reg_glob_impl_handler(
in interceptor ImplEventHandler handler
)
raises(
interceptor HandlerExists
);
void unreg_obj_client_handler(
in Object obj
)
raises(
interceptor NoHandler,
interceptor InvalidObject
);
void unreg_glob_client_handler(
)
raises(
interceptor NoHandler
);
void unreg_obj_impl_handler(
in Object obj
)
raises(
interceptor NoHandler,
interceptor InvalidObject
);
void unreg_glob_impl_handler(
)
raises(
interceptor NoHandler
);
interceptor ConnectionInfo get_client_info(
in Object obj
);
};
The interface in Listing 19.2 is used by both the client and the server objects for handler
administration. Four of the methods correspond to the client-side requirements, and the other
four provide support for the server side. The client-side operations are the ones with the
word client in the names of the operations. Server-side handler operations have the word
impl in their names.
A client can register the handlers two ways. It can add handler support for one object that is
a ClientEventHandler for a single object. VisiBroker also enables a client to have a
single handler registered for all the objects that it chooses to communicate with.
If the client needs to have support for monitoring events for just one object reference, it can
use the reg_obj_client_handler() operation. When using this method, the client
has to pass the instance of the handler and the reference to the remote object. Only a single
handler can be registered for the object at any given time.
On the other end, an implementation can register a single handler for all the objects the
client communicates with. Such a handler is called a global handler. This type of handler is
registered using the reg_glob_client_handler() operation. Just like the per-object
event handler, only one global event handler can exist at any given time.
After the objects are registered, they need to be unregistered. This is accomplished by using
the following API:
Installing a Handler
The following client implementation is taken from Chapter 20, “VisiBroker Interceptors.”
Because, technically, EventHandlers are the same as Interceptors, it would be nice
to show both Interceptors and the ClientEventHandler side by side.
The client has to be modified. The difference between Interceptors and
EventHandlers is highlighted in bold in Listing 19.3.
Listing 19.3 Client Handler Implementation
package Interceptor;
SimpleClientEventHandler(“ObjectClientHandler”);
registry.reg_glob_client_handler(handler);
Filter Object_filter = FilterHelper.bind(orb, “Filter”);
Object_filter.foo((short)1002);
}
}
The handler instance is retrieved by invoking
orb.resolve_initial_references(). When the instance of the
HandlerRegistry is retrieved, the appropriate API methods, which were discussed
earlier in the chapter, can be used to administer the client-side handler.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Server-Side Handlers
On the server side, as on the client side, an object reference can register server-side handlers with
the ORB. The administration process is on the same object as for the client, on the
HandlerRegistry object.
The handler on the server side has to be subclassed from ImplEventHandler. The following
methods are provided for the subclasses to override:
interface ImplEventHandler {
MyImplEventHandler(String name) {
_name = name;
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Listing 19.5 Filter Server Implementation
package Interceptor;
serverFactory.add(new Interceptor.ServerFilterFactory());
}
catch(org.omg.CORBA.ORBPackage.InvalidName e){
System.out.println(e.toString());
}
}
}
The installation of the handler is given in the following line of code:
serverFactory.add(new Interceptor.ServerFilterFactory());
The code in Listing 19.6 initializes the server object as in Listing 19.3, with the slight complication of
initializing the Handler object and registering them with the ORB.
Summary
The VisiBroker distributed events are a mechanism that enables clients and servers to respond to various
predefined events that take place on the client side and the server side. These events mainly indicate when
an ORB-level event takes place. These events are the request to bind or remove a bind. These events can
also give indications as to the state of the server process or the client process.
These event handlers can be attached either on the client side or the server side. The main purpose of these
events is to allow preprocessing before and/or after these predefined events take place. This would enable
the implementations to perform routines such as logging and security audits.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 20
VisiBroker Interceptors
In This Chapter
• Types of Interceptors
• Constructing a ServerInterceptor
• Constructing a BindInterceptor
VisiBroker interceptors are filter objects that, when deployed, act as sieves
through which invocations to clients or servers pass. These messages may be
viewed or altered as they pass through the interceptor. The benefit of an
interceptor is that it enables your applications to view how an ORB processes a
request. This feature might be of benefit in the following ways:
• Security measures, in terms of access privileges, could be controlled
through an interceptor. This could enable the design of a message-based
Access Control List implementation strategy.
• Low-level load balancing could be implemented using interceptors.
Depending on load constraints imposed on a server, invocations could
be forwarded to alternate instances of a CORBA server running in a
cluster.
Note:
Designing an interceptor for load balancing can be effective only in
proportion to its statelessness. If an interceptor has to manage its state
as well, design would probably dictate a load balance agent for an
interceptor. The more stateless an interceptor is, the more scalable it
will be in terms of handling client requests.
Types of Interceptors
There are essentially three basic types of interceptor that a developer can
inherit and/or extend:
• Server interceptor
A server interceptor is an interceptor that works on the server side.
Invocations coming in from a client to a server can be intercepted to
perform functions that you want the interceptor to do. These could be
any one of the areas where an interceptor can be used.
• Client interceptor
A client interceptor is analogous to the server interceptor except it is
installed on the client side. This interceptor can filter both outgoing
invocations as well as incoming messages on the client side.
• Bind interceptor
Whenever a bind or associated method is called, the bind interceptor
intercedes on behalf of the invocation. At this point, an interceptor can
be used to design a low-level access control list–based service for your
applications.
Interceptor APIs are mostly defined to take in parameters. These parameters
are passed to the interceptors from the ORB. Interceptor APIs are also
provided with inout parameters that allow bidirectional communication
between the ORB and the interceptors. These are especially powerful because
they provide a mechanism for directly interacting with the ORB messages
themselves.
Previous Table of Contents Next
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Interceptor APIs
Let us now look at some of the important APIs for each of the interceptor classes. The APIs
can be extended from the default interceptor classes to enable the developer to implement
specific interceptor behavior.
ServerInterceptor Class
The ServerInterceptor methods are invoked on the server by the ORB when client
requests are made:
ClientInterceptor Class
The ClientInterceptor methods are invoked by the ORB on the client side when
requests are made to the server:
BindInterceptor Class
The BindInterceptor methods are invoked by the ORB whenever a bind() or
rebind() request is made by the client to the server:
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Constructing a ServerInterceptor
This section will construct a server-side interceptor. The implementation of client-side and bind
interceptors is similar in methodology, differing only in the semantics of the classes that are used.
The ServerInterceptor interface is an abstract interface that must be implemented to create an
interceptor. We will attempt to override only the methods that I have elaborated for the
ServerInterceptor class. Visigenic provides default interceptor classes that may be reused through
extension rather that using the interface. This would enable you to override only the methods that I
elaborated previously.
The interceptor demonstration is going to use a very simple interface to demonstrate how it is going to
work:
module Interceptor
interface Filter{
void foo(in short in_value);
};
};
The interface is extremely trivial. Run the interface through the VisiBroker IDL pre- compiler to generate
all the support files. The implementation of the Filter interface is equally simple. This is shown in Listing
20.1.
Listing 20.1 FilterObject Implementation
package Interceptor;
public class FilterObject extends Interceptor._FilterImplBase {
private short mine = 0;
public FilterObject(java.lang.String name) {
super(name);
}
public FilterObject() {
super();
}
public void foo(
short in_value
) {
mine = in_value;
}
}
The foo() method does not do anything except set its own instance variable, mine, to the value passed to
it.
Critical about interceptors is the fact that they can be used with minimal changes to the server
implementation. Before doing that, let’s implement the ServerInterceptor and install it with the
ORB.
First, you implement the interceptor and name it ServerFilter. This can be easily accomplished by
subclassing the DefaultServerInterceptor class provided by Visigenic. The benefit of doing this
rather than implementing the ServerInterceptor interface is that you don’t need to implement all the
methods specified in the DefaultServerInterceptor class—just the ones you are interested in. The
implementation of the interceptor is shown in Listing 20.2.
Listing 20.2 ServerFilter Implementation
package Interceptor;
import com.visigenic.vbroker.interceptor.*;
import com.visigenic.vbroker.IOP.IOR;
import
com.visigenic.vbroker.interceptor.ServerInterceptorPackage.
ShutdownReason;
import org.omg.CORBA.portable.*;
import com.visigenic.vbroker.GIOP.*;
import org.omg.CORBA.Principal;
public ServerFilter(){
System.out.println(“Filter Instantiated”);
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
That concludes the implementation of the interceptor itself. The next step is to install the interceptor with the
ORB. VisiBroker provides the facilities of factories to instantiate an interceptor when the ORB requires one.
VisiBroker defines interfaces for all three types of interceptors. In this case, you need to construct a
ServerInterceptorFactory to create a server interceptor when the ORB requires one. This is
accomplished in the code shown in Listing 20.3.
Listing 20.3 ServerFilterFactory Implementation
package Interceptor;
import com.visigenic.vbroker.interceptor.*;
import com.visigenic.vbroker.IOP.*;
package Interceptor;
import com.visigenic.vbroker.interceptor.*;
import com.visigenic.vbroker.orb.*;
import java.util.*;
serverFactory.add(new Interceptor.ServerFilterFactory());
}
catch(org.omg.CORBA.ORBPackage.InvalidName e){
System.out.println(e.toString());
}
}
}
The installer object essentially retrieves an object reference to the already installed chain of
ServerInterceptorFactory interfaces. This implies that there can be more than one interceptor
installed on the server. When the object reference is resolved, the factory class that was implemented
previously is registered to the chain of interceptor factories. This does not, however, mean that the interceptor
itself is initialized. That can occur only when a client makes an invocation on a server that is attached to an
interceptor. The server code is shown in Listing 20.5.
Listing 20.5 FilterServer Implementation for Server Interceptor
package Interceptor;
package Interceptor;
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Constructing a BindInterceptor
This section constructs a bind interceptor. The example is similar to ServerInterceptor, except that the
interceptor is invoked by the ORB when the bind() occurs.
For this example, the bind interceptor is implemented by extending the DefaultBindInterceptor class.
The code for the bind interceptor is shown in Listing 20.7.
Listing 20.7 BindFilter Implementation
package BindInterceptor;
import com.visigenic.vbroker.interceptor.*;
import com.visigenic.vbroker.IOP.IOR;
public class BindFilter extends DefaultBindInterceptor {
public BindFilter(){
System.out.println(“Bind Filter Instantiated”);
}
public boolean bind(IOR ior,
org.omg.CORBA.Object object,
Closure closure){
return false;
}
public void bind_succeeded(IOR ior,
org.omg.CORBA.Object object,
Closure closure) {
System.out.println(“Bind succeeded”);
}
This interceptor is very trivial. The only method that will be invoked in this interceptor is
bind_succeeded() to notify that the bind was successful.
For the bind interceptor example, an initialization class will be used to install the bind interceptor, rather than
the InterceptorInstaller class that was used in the ServerInterceptor example. The
initialization class is subclassed from VisiBroker’s ServiceInit class, and it will be executed during the
ORB’s initialization. The initialization is done by the ORB at runtime by using the ORB services option at
startup, -Dorbservices=xxxxx, where xxxxx is the name of the package that contains the initialization
class. The code for this class is shown in Listing 20.8.
Listing 20.8 Initialization Class for BindInterceptor
package BindInterceptor;
import com.visigenic.vbroker.interceptor.*;
import com.visigenic.vbroker.orb.*;
import java.util.*;
ChainBindInterceptor bind =
com.visigenic.vbroker.interceptor.ChainBindInterceptorHelper.narrow
(
orb.resolve_initial_references(“ChainBindInterceptor”));
bind.add(new BindFilter());
Because, in this example, the bind interceptor is not installed by the server, the server code is like standard
CORBA servers, as shown in Listing 20.9.
Listing 20.9 FilterServer Implementation for BindInterceptor
package BindInterceptor;
package BindInterceptor;
Summary
VisiBroker interceptor technology enables the creation of a filter to intervene before, after, and during
client/server interactions. This can be leveraged in all sorts of ways. It can be used for tracing interactions and
logging states of the server objects. Creation of load balancing and enforcing security are important areas
where interceptors can be used effectively.
VisiBroker defines three types of interceptors: server interceptors, client interceptors, and bind interceptors.
These three interceptors enable a designer to completely control client/server interactions.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Part V
CORBA Integration and Interfaces
In This Part
• CORBA and Java Servlets
• CORBA and Mobile Agents
• CORBA and Design Patterns
• CORBA Interface Repository
• CORBA Dynamic Invocation Interfaces
• Developing CORBABean Wrappers
Chapter 21
CORBA and Java Servlets
In This Chapter
• Why Use Servlets with CORBA?
• HTML as a Presentation Layer
• Building with Servlets
• A Short Introduction to the Servlet API
• A Simple CORBA Servlet
Java servlets provide a simple but effective mechanism for extending Web
servers. There are a number of problems currently evident with server-side
extensions on the World Wide Web, because the great majority of server-side
extensions are Common Gateway Interface (CGI)–based applications. It
should be noted that Perl-derived CGI have been evolving to answer some of
the problems that will be enumerated here. First, let’s define CGIs and
examine their problems.
CGI enables a developer to build applications on the server side that are
executed when an invocation arrives from the client side. As such, CGI-based
applications are a mechanism to extend a Web server. As it is conceived, the
World Wide Web (WWW) is a pure two-tier client/server application. The
user interface is “dumb”—just a veneer of presentation written in HTML. For
a simple document, hyperlinking this is sufficient. The server is also rather
dumb. Its only function is to serve out HTML pages. It contains no logic other
than this.
Very early in the evolution of the WWW, developers needed a mechanism to
add some “smarts” to the server side. These smarts were CGI. They are
per-request executables that are executed on the server side. For every request
that came in from the client, a new CGI instance was created and executed.
After the execution was completed, the CGI was unloaded from memory.
As a technology, CGI has all the hallmarks of brilliance. For one, it was simple
to implement; the vast majority of CGI scripts were written in Perl (I wonder if
Perl could be mapped to OMG IDL). It is a technology that is pervasive. In the
meantime, the WWW became insanely pervasive! With this pervasiveness, the
drawbacks of CGI became obvious. The biggest problem was scalability. As
more and more developers got online, CGI was being extended into oblivion!
The powers that be decided to “fix” the problem with their own ways of
extending CGI, but without its problems. Some of them are MS Active Server
Pages; Netscape has Server Side JavaScript. JavaSoft put out its own Web
server, the Java Web Server (JWS), and through it JavaSoft introduced Java
servlets as a mechanism of extending JWS. The servlets are written entirely in
Java, and as a technology, they have been adopted widely as a “Cross Web
Server extension” technology.
As a technology, the servlets sit on the server side, persistently loaded into
memory of the server for providing immediate response to a client request.
After they are loaded into memory, servlets continue to reside in memory until
they are explicitly unloaded. They are thus much faster than the classical forms
of CGI. Servlets as a technology also follow the rules of the JavaSoft
specification process, so they are as open a standard as the circumstances
allow. Because they use Java as the language for extending the server,
everything that is available in Java can be used within a servlet, including Java
implementations of CORBA.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
HTML as a Presentation Layer
N-tier client/server applications are fast becoming the norm for building applications. They are useful
because of the level of decoupling they bring to bear on the solution. Figure 21.1 illustrates what I am
talking about.
In any large enterprise, it is natural that a variety of heterogeneous solutions must coexist. In a large
organization, solutions exist, side by side, that use different languages, different platforms, and even
different operating systems. This makes them interesting and also a bother to maintain. CORBA provides
the capability of supporting all these variations by breaking up architectures into three tiers.
Three-tier client/server architecture enables you to isolate the dynamics of user interface requirements
from the underlying application requirements. In most architecture, the business logic and the process
logic can usually be separated.
This enables the same application layer to support many user interface requirements. HTML, in my view,
is another presentation layer. As such, if it could somehow support CORBA-based applications, existing
business logic (also known as legacy applications) and processes can be adapted to the Internet or
HTML-based intranet solutions.
The Java servlet is one such technology. Servlets can be used to add a new presentation layer over
existing solutions. For example, you could have a contact application that you would like to extend over
the Internet or an intranet. You could use CORBA to wrap these legacy applications and then deploy them
over the Internet or intranet using Java servlets. The converse is also true: Newer applications can be built
using CORBA, and servlet-based extensions can be used to add Internet support to them. When the
necessary bandwidth becomes available, the big-gun applications can be seamlessly deployed over the
Internet as well.
Building with Servlets
The example assumes that you are reasonably familiar with servlets and how they can be used in the
context of the Internet. I also assume that you have installed either the Java Web server or the Java Servlet
Development Kit. The example also assumes that you are adept at administering the Web server.
Servlets at their basic level are extremely simple to use. The simplest way to write a servlet is to subclass
the javax.servlet.GenericServlet class. At that point, you only have to override the service(ServletRequest,
ServletResponse) method. If you want to code an HTTP-based servlet, you can subclass the
javax.servlet.http.HttpServlet class and override the doGet(HttpServletRequest, HttpServletResponse) and
doPost(HttpServletRequest, HttpServletResponse) methods. HttpServlet is an extension of
GenericServlet. GenericServlet also implements the javax.servlet.Servlet and
javax.servlet.ServletConfig interfaces. The servlet interface contains the lifecycle methods
that can be overridden and implemented by your servlets.
In Listing 21.1, a simple servlet is built. This servlet will subclass the HttpServlet. When this servlet
is invoked, it displays Hello World on the client side.
Listing 21.1 A Hello World Servlet
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public
class HelloWorldServlet extends HttpServlet {
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
A Short Introduction to the Servlet API
The next sections use the HTTPServlet to demonstrate how CORBA clients can be wrapped as servlets. At
the core of how this class works are the doPost() and the doGet() methods. Both these methods have two
parameters: the HttpServletRequest parameter and the HTTPServletResponse parameter.
The HttpServletRequest object that is passed as a parameter represents the request coming in from an
HTTP client. As a class, HttpServletRequest contains all the details of an HTTP request protocol.
Various parameters, such as parameters being passed from the client and the client connection itself, can be
accessed through an instance of HttpServletRequest.
The HTTPServletResponse class provides a clean set of APIs for interacting with the HTTP response
API. The crucial method in this class is the getOutputStream(), which returns a stream object
representing the output buffer that you want to write a response to. For detailed information on these classes,
please refer to the Java servlets API documentation.
Note:
Reusing and extending an already existing application is the usually prescribed strategy for dealing with CORBA
applications.
CORBA applications’ architectures have always encouraged reuse. This essentially means that if you construct a
CORBA service, its potential power is evident only when it is reused in new contexts. For instance, consider
applications that do human resource management. It is useful if you can provide a solution to this problem using
classical client/server strategies. But if you were asked to extend the solution so that the same solution could be
used over the Internet/intranet, you would probably end up constructing a new application.
Using CORBA, you would first create a Common Object Model that is general to both the internal network
application and the intended Internet solution. On this foundation, you extend the solution to meet the
requirements of the internal solution. Further, if an Internet solution is required, you extend the Common Object
Model to provide that solution.
Critical to this solution is the Common Object Model, which can be reused. This essentially means that the
Internet/intranet should really be considered as an extension of the work that has already been done. You design
the components of the solution that can be reused either on the Internet or on the intranet without having to recode
or redesign existing solutions.
package idlservlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import org.omg.CORBA.*;
response.setContentType(“text/html”);
out = new PrintWriter (response.getOutputStream());
out.println(“<html>”);
out.println(“<head><title>GetInterface</title></head>”);
out.println(“<body>”);
out.println(“ ;<p>”);
try{
makeStructCall();
out.println(“<p>”);
getStructCall();
out.println(“<p>”);
makeSequenceCall();
out.println(“<p>”);
getSequenceCall();
out.println(“<p>”);
makeNumberCall();
out.println(“<p>”);
getNumberCall();
out.println(“<p>”);
makeStringCall();
out.println(“<p>”);
getStringCall();
out.println(“<p>”);
makeReferenceCall();
out.println(“<p>”);
getReferenceCall();
}
catch(Exception e){
System.out.println(e.toString());
out.println(“Tempad” + e.toString());
out.println(“<p>”);
}
out.println(“<p>”);
out.println(“</body>”);
out.println(“</body></html>”);
out.close();
}
public String getServletInfo() {
return “idlservlet.GetInterface Information”;
}
inStruct.age = 22;
inStruct.name = “Koolaire”;
Any In = request.add_in_arg();
Any Out = request.add_out_arg();
Any Inout = request.add_inout_arg();
Out.type(Dynamic.diiPackage.DyStructHelper.type());
Dynamic.diiPackage.DyStructHelper.insert(In,inStruct);
Dynamic.diiPackage.DyStructHelper.insert(Out,outStruct.value);
Dynamic.diiPackage.DyStructHelper.insert(Inout,inOutStruct.value);
request.invoke();
outStruct.value = Dynamic.diiPackage.DyStructHelper.extract(Out);
inOutStruct.value = Dynamic.diiPackage.DyStructHelper.extract(Inout);
out.println(outStruct.value.name + “\n”);
out.println(inOutStruct.value.name + “\n”);
}
Any In = request.add_in_arg();
Any Out = request.add_out_arg();
Any Inout = request.add_inout_arg();
Out.type(Dynamic.diiPackage.stringsHelper.type());
Inout.type(Dynamic.diiPackage.stringsHelper.type());
Dynamic.diiPackage.stringsHelper.insert(In,inSequence);
Dynamic.diiPackage.stringsHelper.insert(Out,outSequence.value);
Dynamic.diiPackage.stringsHelper.insert(Inout,inoutSequence.value);
request.invoke();
outSequence.value = Dynamic.diiPackage.stringsHelper.extract(Out);
inoutSequence.value = Dynamic.diiPackage.stringsHelper.extract(Inout);
request.set_return_type(orb.create_sequence_tc(0,
orb.get_primitive_tc(org.omg.CORBA.TCKind.tk_string)));
request.invoke();
String[] result;
result =
Dynamic.diiPackage.stringsHelper.extract(request.return_value());
ins.insert_long(inNumber);
inouts.insert_long(inoutNumber.value);
outs.insert_long(outNumber.value);
request.invoke();
outNumber.value = outs.extract_long();
inoutNumber.value = inouts.extract_long();
out.println(“INOUT PRIMITIVE: “ + new Integer(inoutNumber.value) +
“\n”);
out.println(“OUT PRIMITIVE: ” + new Integer(outNumber.value) + “\n”);
}
Request request =
DynamicObject._request(“takereference”);
Any ins = request.add_in_arg();
Any outs = request.add_out_arg();
Any inouts = request.add_inout_arg();
outs.type(Dynamic.diiHelper.type());
Dynamic.diiHelper.insert(ins, inReference);
Dynamic.diiHelper.insert(outs, outReference.value);
Dynamic.diiHelper.insert(inouts, inoutReference.value);
request.invoke();
outReference.value = Dynamic.diiHelper.extract(outs);
inoutReference.value =
Dynamic.diiHelper.extract(inouts);
out.println(“OUT REFERENCE : ” +
outReference.value.toString() + ”\n”);
out.println(“<p>”);
out.println(“INOUT REFERENCE : ” +
inoutReference.value.toString() + ”\n”);
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
This servlet is very simple in the way it works. It essentially does exactly the same as the DII. The only
difference is that the output is now redirected to a HttpServletResponse stream. The client code is
just the same as before. The only slight changes have been the insertion of an HTML tag <p> to make the
output a little more presentable.
To use the servlet, you must also construct an HTML client to invoke it. Here is the code for the HTML
page:
<HTML>
<HEAD>
<META HTTP-EQUIV=”Content-Type”
CONTENT=”text/html;
charset=iso-8859-1”>
<TITLE>
Corba Servlet
</TITLE>
</HEAD>
<BODY>
<FORM action=http://galileo.oa.nl:8080/servlet/idlservlet.GetInterface
method=POST>
<BR><BR> Press Submit to Invoke the CORBA Servlet GetInterface
<BR><BR><input type=submit><input type=reset></form>
</BODY>
</HTML>
The rendering of the page as output is shown in Figure 21.2.
On pressing the submit button, a POST request is sent to the servlets. The POST is then handled by the
following public void method:
Summary
Currently, the bandwidth problems on the Internet mean that, for the majority of CORBA applications,
using Java would have extremely long download times. This also means that fully fledged GUI
applications on the client side using Java are not the best options if the chances are that the user has low
bandwidth. HTML under these circumstances is still the best solution.
HTTP and the underlying infrastructure of the Internet are not really equipped to build the kinds of
solutions we are trying to build now, especially in the e-commerce space. The ideal architecture for such a
solution is a three-tier client/server architecture.
CORBA is ideal for building highly scalable multitier architectures. On top of all this, CORBA is
cross-platform and multilingual. Servlets enable a clean solution for painlessly integrating such diverse
solution spaces into a coherent Internet/intranet package.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 22
CORBA and Mobile Agents
In This Chapter
• Voyager—An Agent Orb for Java
• Implementing an Agent
• The Aircraft Implementation
• The Agents.Airport Implementation
The concept of mobile agents is an emerging paradigm shift. As a technology,
it perhaps stands at the extreme of distributed computing. Over the last twenty
years, information technology has been shifting to greater decentralized
systems. Perhaps this shift mirrors and is a consequence of the great
decentralization process that is taking place in society.
The first thing that struck me when using mobile agents was a strange sense of
dislocation. Danny Lange, one of the preeminent practitioners of agent
technology soon put it right for me. In an email to me, he wrote, “The norms
of client server relations are no longer valid with mobile agents.” Mobile
agents are pieces of logic that have the capability of transferring over the
network from one node to another. After it arrives at the target site, an agent
executes its program. Theoretically, this logic can be anything; after the agent
finishes its execution, most implementations enable the retraction or
deactivation of the agent at the host.
Mobile agents are best modeled on peer-to-peer schemas. That means that
there are essentially no servers or clients. Every agent is a quantum of logic
that is usually ascribed a single task. Applications are built using an “ecology”
of agents cooperating to get a task done. The ecology can be spread over any
number of hosts, both spatially and temporally.
Let’s look at a typical scenario. Suppose you want to buy an airplane ticket.
The process of doing so involves first dispatching a search agent to a directory
server. There, the search agent could interact with other agents to locate an
appropriate agent that is willing to sell airplane tickets at the right price (for
instance). When this is accomplished, the search agent could invoke the
services of a purchase agent to buy the actual ticket. It does so by interfacing
with a product agent—in our case, a ticketing agent. When the sale is
complete, the purchasing agent could dispatch a payment agent to conclude the
sale by paying for the ticket. When all this is accomplished, the purchasing
agent comes home with the ticket.
Currently, the problem facing agent technology is not the mechanism of
mobility. That part has largely been solved using Java as a platform. Java in
that respect has been a great boon to implementers; the Java virtual machine
guarantees a homogeneous execution environment that shields an agent from
the underlying host environment. Agents can also be considered as an alien
piece of code that executes in the host. The world being the world, it is
probable that at one time or another some of that code will be akin to a
malignant tumor, a network-borne virus. Java’s built-in security provides a
good immune system in preventing most of these potential attacks.
The threat from virus aside, mobile agents have the potential for radically
changing the way in which distributed systems are designed. First, there are no
clients and servers. An agent is dispatched to the remote host and it executes
when it arrives there. If a more classical approach to client/server is required,
an agent is dispatched to the remote host, where it will try to interact with
“resident” agents to acquire some service.
At any rate, the fundamental problem that mobile agent technology faces is
interagent communications. There is yet no standard lingua franca for agent
communications. It is consequently one of the most intensely researched areas
of agent technology. OMG has realized the potential of agents. It is currently
under the process of standardizing The Mobile Agent Facility specification.
After the standard is formally accepted, all current implementations of agents
are expected to comply.
Currently, there are a number of agent implementations in the market. As far
as I have been able to ascertain, none of them conforms to MAF yet.
Nevertheless, a careful look at these technologies could be worthwhile. I have
worked with a number of agent technologies. Some of the more interesting
ones are IBM Aglets, Oddessey, and Concordia. These can be downloaded
from the URLs given at the end of the chapter. I must confess that my favorite
is the Aglets because it is an implementation that is closest to the mobile agent
spirit.
This book will not demonstrate how agents can be used in their natural
domain. That is best left to books specializing in agent technology. Because
this is a book on CORBA, it will demonstrate how agents can be integrated
with CORBA applications. One of the ways in which agents can be used in the
context of CORBA is that a CORBA client can be constructed as an agent.
This agent can then be dispatched to a host. On arriving at the host, the agent
can then accomplish a normal bind to a server object at the host. After the
connection is made, it can then communicate using the normal CORBA
distribution mechanism. One of the reasons you might choose to use such a
technique is to minimize IIOP traffic over the network.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Voyager—An Agent Orb for Java
Voyager is a fine implementation of mobile agent technology, from among a fine crop of mobile agent
technologies. I use a number of agent technologies, and I could have used any one of the agent technologies
out there—for example, IBM Japan’s Aglet Technology or General Magic’s Odyssey. I chose Voyager for
this book because it also contains a tidy implementation of CORBA.
Voyager is used to illustrate how mobility can be used in conjunction with classical object reference base
communication. Let’s look at the steps for using Voyager with CORBA:
1. Define and implement a CORBA component.
The following interface demonstrates how Voyager can be used:
module Agents{
interface Hangar{
struct aircraft{
long id;
string name;
};
void CheckInAirCraft(in aircraft t_aircraft);
void CheckOutAirCraft(in long id);
};
};
The application will use VisiBroker for the CORBA part and Voyager for the mobility. Compile the
interface using the following syntax:
package Agents;
public AgentCorba() {
super();
}
package Agents;
The server is named AgentServer. Other than that, this implementation is no different than the
other servers that we have built. After the server is built, you need to build a agent host server that will
serve as a target for a remotely dispatched agent. Take the following steps to accomplish this:
• Define a Java class named Store that represents a store and generate a virtual version of
Store so it can be constructed remotely.
• Choose a class for the registry and generate a virtual version of this class.
• Compile the Server programs.
• Start the Voyager Server to hold the persistent remote objects.
The class that is built in the last step is given in Listing 22.1.
Listing 22.1 Agent Server Implementation
package Agents;
import com.objectspace.voyager.*;
public class AgentServer {
public AgentServer() {
try {
Voyager.setExtendedStackTrace( true );
VCorbaServer corba = new VCorbaServer(“galileo.oa.nl:2001/Hangar”);
Thread corbathread = new Thread(corba);
corbathread.start();
System.out.println(“AgentServer is “ + corba );
Voyager.shutdown();
}
catch( VoyagerException exception ){
System.err.println( exception );
}
}
org.omg.CORBA.ObjectImpl
org.omg.CORBA.Skeleton
Agents._HangarImplBase
Agents.AgentCorba
Agents.CorbaServer
Execute the vcc.exe using the following command:
prompt$> vcc.exe Agents.AirCraft
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Repeat the command for these files. After this has been done, you will see that the virtualized classes
have been created in the current directory. Move these to the appropriate package subdirectory. After
this has been done, run the Java compiler on the files to create the bytecode. The application is now
ready to be deployed. To execute the application, two things have to be done: Voyager has to be started
and then the AgentServer can be executed. The two applications are shown in Figure 22.1.
Implementing an Agent
You use the following steps to build an agent in this chapter:
1. Define and implement a class named Aircraft that represents an Aircraft agent that is to be
moved from one hangar to another.
2. Implement a class named Airport to instantiate and launch an instance of Aircraft.
3. Create a virtual version of Aircraft.
4. Compile all the files created earlier in this chapter.
5. Restart the Voyager servers.
6. Run Agents.Airport to launch the Aircraft.
package Agents;
import com.objectspace.voyager.*;
import com.objectspace.voyager.agent.*;
package Agents;
import com.objectspace.voyager.*;
public HangarClient() {
try{
VCorbaServer Hangar =
(VCorbaServer)VObject.forObjectAt(“galileo.oa.nl:2001/Hangar”);
Aircraft aircraft = new Aircraft();
aircraft.Depart(Hangar);
}
catch( VoyagerException exception ){
System.err.println( exception );
}
}
VCorbaServer Hangar =
(VCorbaServer)VObject.forObjectAt(“galileo.oa.nl:2001/Hangar”);
Aircraft aircraft = new Aircraft();
aircraft.Depart(Hangar);
The first line of code creates the remote object that is running at port 2001. In this case, this is the virtual
CorbaServer. The Aircraft is then instantiated and the Depart() method is then invoked on
the Aircraft object. This causes the Aircraft to move to the remote server and execute the
callback function after it arrives there. The execution of the three runtimes is shown in Figure 22.2.
As you can see from the illustration, the gent did move to the remote server and execute the callback
function. As was shown before, the callback function executes the CORBA code. The aircraft get
checked into the hangar, and then the aircraft gets checked out.
Summary
Mobile agents perhaps constitute a radical departure from current technology for enabling distributed
computing. A technology enables a piece of code to be dispatched physically to a remote site where, on
its arrival, it continues to function autonomously. As technology goes, mobility can be the foundation of
many interesting and innovative solutions. Extremely dynamic and responsive workflow environments
can be built. Components with mobility over the next few years will give rise to solutions suites being
built on demand—just in time.
More importantly, many outstanding issues at the heart of AI, such as natural language processing and
complex adaptive systems, should once again take the forefront of redoubled research. Whatever
directions agents take, information technology will perhaps never be the same again.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 23
CORBA and Design Patterns
In This Chapter
• The Command Pattern
• The OpenConnection Interface Implementation
• The Proxy Pattern
• The Abstract Factory Pattern
The preface to Design Patterns, Elements Of Reusable Object-Oriented by
Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, hereafter
referred to as the Gang Of Four (GoF), defines patterns as “…simple and
elegant solutions to specific problems in object oriented software design.”
Since the publication of the above- mentioned book by the GoF, there has been
an explosive growth in both the interest in design patterns and their application
to the problems of design. The designs of technologies such as CORBA and
Java can be best understood if studied from the application of design patterns.
The problems of design are fundamental to the “quality” of the result of
software engineering projects or products. These can be defined as processes
that take place along the lines of an industrial process; massive amounts of
resources are allocated to see it through. It has been well documented that one
of the main problems with object-oriented software engineering is the process
of requirements acquisitions. All popular methodologies accept this drawback
and devote considerable intelligence to this problem. During the phase in
which a product of analysis moves to design, I have found certain vagueness.
Engineering is about trade-offs; it is also a pragmatic science, because it
accepts the fact that resources are always finite—hence the ideal product can
never be built. So if you accept that engineering is about trade-offs, design is a
process where you make those trade-offs. However, a methodology can never
tell you what trade-offs to make. I am not faulting any method here; the
problem space for design is much too large for any one method to anticipate all
requirements.
It has been “discovered” in the context of design patterns that there is a
recurrent nature inherent in problems of software design. Corresponding to
these recurring problems are associated solutions that, once mastered, can be
applied to the problem almost without thought. Design patterns therefore
constitute a template that can be applied to many areas to solve many
problems.
Design patterns do not teach you how to make a design trade-off, however.
Trade-offs are usually made in terms of the relative cost of implementing
various alternatives. For instance, in the context of using CORBA, despite the
fact that IIOP is comparatively an efficient protocol, there is nevertheless a
finite overhead when a client makes a remote invocation. Because the network
bandwidth is also finite, it is probable that at some point a bottleneck can
occur. How can system design anticipate this?
For example, in CORBA applications a recurrent solution to design systems
involves a client making an arbitrary number of calls to transform one object
into another. Between each of these transformations, the client makes an
invocation on the transformed object. Each such transformation and the
subsequent invocation on the transformed object might prove costly under
certain circumstances.
There are other situations when an existing application should be modified.
Complete reengineering is not an option with most of these problems. Under
these circumstances, your only option is to wrap the “legacy” solution.
There are many such situations. Opting between these options means the
difference between just a solution and the solution. In his groundbreaking book
Object-Oriented Design Measurements, Whitmore proposed to call design a
hypothesis. Ultimately, a hypothesis would need to be proved by experiment.
Because software construction is so expensive, such experiments can only be
simulated. Such a simulation will involve analyzing many variants of a
solution to choose the optimal solution. This problem is tackled in the process
known as design metrics.
You will find that applying the right design pattern to the right problem will
constitute a better design. The trick is to know where to apply what and why.
There are, unfortunately, no hard and fast rules except a set of idioms and
heuristics for those who are not mathematically adept.
In this chapter, you look at only three design patterns. Although this might
seem unfair, given the body of knowledge on design patterns and the sheer
number of patterns out there, this is a book on CORBA programming and not
one on designing CORBA solutions.
The patterns you will look at—command, proxy, and abstract factory—are
taken from the GoF book.
The command pattern is extremely useful in reducing the number of calls
made over the network. It does not not eliminate them, but it does reduce them
when compared to some alternative designs. In my view, the application of this
pattern to the problem keeps with the spirit of the intention of this pattern,
which was stated in the book. Essentially, a command pattern can be used to
transform a request into an object. Each of these requests can encapsulate a
certain implementation of a command. The request objects can then be passed
as parameters into a server where they can be executed.
This is sometimes handy when the execution needs to take place at the server;
but from a design perspective, it would be unwise for the server to know
anything about the operation itself. Consider an operation on the database.
Under “normal” circumstances, the server implements an object wrapper over
the underlying database API behind a standard interface. The client then makes
the appropriate invocation on the server. Such a solution, though adequately
satisfying the requirements, introduces undue interdependence between the
client and server. The client has to know the various interface operations to
make on the server that are specific to the operation that the client would like
to perform on the server.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The Command Pattern
The command pattern could be used to encapsulate such operations requested as command objects. The
benefit of such a design is that interdependence between the client and the server is at the level of the
command interface and nothing more. The client could construct the command object and pass the object to
the server. Because the server knows the interface to the command object, all it needs to do is invoke the
appropriate method on the command object to cause its execution.
I have found the command object to be extremely useful to design a related set of operations within a single
interface as a set of command objects that are passed to the server. The server then makes the invocation on
the command interface to execute the operations. These have proved extremely useful in cutting down the
number of invocations to the server, as well as reducing the dependencies between client and server.
Consider the interface shown in Listing 23.1.
Listing 23.1 A Command Interface
module Patterns{
interface Command{
void execute();
};
struct db{
string user;
string pwd;
};
package Patterns;
public class iCommand extends Patterns._CommandImplBase {
public iCommand(java.lang.String name) {
super(name);
}
public iCommand() {
super();
}
public void execute() {
// IMPLEMENT: Operation
}
}
All the implementation is done with the subclasses of the Command interface. Let us take a look at them,
one at a time.
package Patterns;
public class iOpenConnection extends Patterns._OpenConnectionImplBase {
Patterns.db databse;
The intent of this interface is to allow a client to pass any valid SQL statement as a string. When that has
been accomplished, a client could then call the execute method on the concrete implementation of the
OperateOnDatabse interface. In this case, it is implemented along the lines shown in Listing 23.4.
Listing 23.4 The OperateOnDatabase Class
package Patterns;
public class iOperateOnDatabse extends Patterns._OperateOnDatabseImplBase
{
String SQLCommand;
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The CloseConnection Interface Implementation
As with iOperateOnDatabse, the code shown in Listing 23.5 is also not very realistic, because
iCloseConnection does not contain any information about the database that was opened. When a client
calls the execute() method, a realistic implementation would close the active data connection.
Listing 23.5 The CloseConnect Class
package Patterns;
public class iCloseConnection extends Patterns._CloseConnectionImplBase {
public iCloseConnection(java.lang.String name) {
super(name);
}
public iCloseConnection() {
super();
}
public void execute() {
System.out.println(“Closing Connection to the Databse.”);
}
}
Thus we have the implementation of the three command classes that together could be used to act on a
database. All that remains is to implement the DataOperations interface.
As mentioned before, DataOperations acts as a container for related sets of command objects that act on
databases. A client constructs a command object and calls the add() method on the interface. When all the
command objects are added, a client could call the execute() on this object. On doing so, the
DataOperations implementation will iterate over all the command objects that it is holding and invoke
the execute() method on each of them in turn. Listing 23.6 shows the implementation of the interface.
Listing 23.6 The DataOperations Class
package Patterns;
import java.util.*;
public iDataOperations() {
super();
}
The command server that hosts the three command objects is, as shown in Listing 23.7, similar to the many
other CORBA servers that we have built.
Listing 23.7 The Command Pattern Server
package Patterns;
public CommandServer() {
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init();
org.omg.CORBA.BOA boa = orb.BOA_init();
boa.obj_is_ready(oops);
boa.obj_is_ready(sql);
boa.obj_is_ready(cops);
boa.obj_is_ready(dops);
System.out.println(oops + “ is ready.”);
System.out.println(sql + “ is ready.”);
System.out.println(cops + “ is ready.”);
System.out.println(dops + “ is ready.”);
boa.impl_is_ready();
}
package Patterns;
public CommandClient() {
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init();
org.omg.CORBA.BOA boa = orb.BOA_init();
open.OpenConnection(database);
cmd.Add(open);
cmd.execute();
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The Proxy Pattern
The proxy pattern is a structural pattern. In the GoF book, it is defined as a placeholder for another object
to control access to it. Proxy patterns are especially useful when you need to wrap non–object-oriented
code in an object-oriented wrapper. Such a strategy is especially useful when you are dealing with legacy
systems or data access systems.
In fact, CORBA uses the proxy pattern. On the client side, the stubs that are generated by the IDL
pre-compiler are acually a proxy implementation. As a proxy, the intention is to act as a placeholder for a
remote object reference. This enables a number of benefits; for one, it shields the underlying complexity
of the IIOP protocol marshaling and demarshaling.
The for_var variables are also proxies in a sense. They encapsulate the underlying foo_ptr as smart
pointers to automatically manage reference counting. Most of the interceptor/filter technology discussed
in earlier chapters also serve as proxies. However, as proxies, they are very transparent to both the client
and the server.
The following code illustrates how proxies can be used to augment an existing implementation—in this
case, wrapping:
#include “command.idl”
module Patterns {
interface proxy:DataOperations{
exception proxyException{string reason;};
};
};
The preceding interface inherits from the DataOperations interface that was defined in
command.idl. The object of this exercise is to implement the proxy interface to act as a go-between for
the client and the component. The implementation is shown in the following section. Compile the
interface using the following command:
package Patterns;
public class iproxy extends Patterns._proxyImplBase {
The proxy server, as shown in Listing 23.10, contains only a minor modification to the one implemented
for the command objects. Instead of the iDataOperations object, an instance of the proxy object is
initialized.
Listing 23.10 The Command Server With Proxy
package Patterns;
public CommandServer() {
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init();
org.omg.CORBA.BOA boa = orb.BOA_init();
boa.obj_is_ready(oops);
boa.obj_is_ready(sql);
boa.obj_is_ready(cops);
boa.obj_is_ready(proxy);
System.out.println(oops + “ is ready.”);
System.out.println(sql + “ is ready.”);
System.out.println(cops + “ is ready.”);
System.out.println(proxy + “ is ready.”);
boa.impl_is_ready();
}
The client does not require any modifications; it will function as before without any change. The code is
given in Listing 23.11.
Listing 23.11 The CommandClient Class
package Patterns;
public CommandClient() {
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init();
org.omg.CORBA.BOA boa = orb.BOA_init();
open.OpenConnection(database);
cmd.Add(open);
cmd.execute();
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The Abstract Factory Pattern
The abstract factory pattern is one of my favorite patterns. One of the main consequences of using this pattern
is that you can almost totally decouple the construction of complex objects from the client. When designing
CORBA applications, it is inevitable that a client would require the services of a reasonable number of object
references to get the job done. Under normal circumstances, it is also usual that the client is given
responsibility for constructing each one of these object references in some server and invoking the required
interface protocols on them.
The abstract factory pattern enables a designer to hide a lot of this behind a Factory interface. This enables
you to give a rather simple picture to the client while the implementation of the abstractFactory does all
the hard work.
The abstract factory pattern can be extremely useful if used in conjunction with Orbix filters or VisiBroker
interceptors. Under these mechanisms, when a client request comes in, the filter or interceptor could construct
the appropriate object reference and pass it back to the client. This enables the fullfillment of one of the areas
that the GoF book lists in which the pattern can be used. The specific area is the design of a system that is
independent of how the objects on the server side are “created, composed and represented.”
Let’s use the abstract factory pattern in a similar fashion. Primarily, let’s use VisiBroker to implement a
factory to mediate between a client and the server to construct an object reference and return to the client. The
example uses the following interface:
module patterns {
interface abstractFactory{
Object create();
void destroy();
};
interface concreteFactory:abstractFactory{
struct Struct{
short number;
};
readonly attribute long identifier;
Struct getStruct();
};
};
The patterns module specifies two interfaces. The abstractFactory interface serves as an abstraction
for subinterfaces. This enables you to pass back subclasses to the client by utilizing polymorphism.
The concreteFactory:abstractFactory interface specifies two operations: one to create an object
reference and the other to destroy it. The implementations of these interfaces are shown in the next sections.
The first thing we have to do is implement the Factory service.
package Patterns;
public class concreteFactory_i extends Patterns._concreteFactoryImplBase {
private Patterns.concreteFactoryPackage.Struct a_struct = new
Patterns.concreteFactoryPackage.Struct();
private org.omg.CORBA.BOA boa_ = null;
package Patterns;
public FactoryServer() {
orb_ = org.omg.CORBA.ORB.init();
boa_ = orb_.BOA_init();
Patterns.concreteFactory cf = new
Patterns.concreteFactory_i(“Factory”,boa_);
try{
boa_.obj_is_ready(cf);
System.out.println(cf + “ is ready.”);
boa_.impl_is_ready();
boa_.deactivate_obj(cf);
}
catch(org.omg.CORBA.SystemException ex){
System.out.println(ex.toString());
}
}
Patterns.concreteFactory cf = new
Patterns.concreteFactory_i(“Factory”,boa_);
When the concreteFactory instance is created, the BOA goes into a blocked mode awaiting requests
from the client. The implementation of the client is shown in Listing 23.14.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Listing 23.14 The FactoryClient Class
package Patterns;
public class FactoryClient {
public FactoryClient() {
try{
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init();
Patterns.concreteFactory cf =
Patterns.concreteFactoryHelper.bind(orb, “Factory”);
org.omg.CORBA.Object obj = cf.create();
Patterns.concreteFactory factory =
Patterns.concreteFactoryHelper.narrow(obj);
Patterns.concreteFactoryPackage.Struct struct = factory.getStruct();
System.out.println(new Integer(struct.number));
}
catch(org.omg.CORBA.SystemException ex){
System.out.println(ex.toString());
}
}
Patterns.concreteFactory cf =
Patterns.concreteFactoryHelper.bind(orb, “Factory”);
When the instance of the factory is returned, it can then be used to create objects. A fully blown
implementation of the Factory service would use the Naming Service to register itself so that the client
could use a standard CORBA service to acquire the Factory object reference.
When the Factory object reference is acquired, it can be used to construct the object references that are
required by the client to function. When the reference is acquired, it is used to demonstrate how a Factory
interface can be used to construct an object reference:
org.omg.CORBA.Object obj = cf.create();
This is the second mechanism that can be used to construct an object reference. This time, let’s use the
method on the concreteFactory. Invoking the create() method returns an instance of
CORBA.Object. This reference has to be narrowed to get hold of the reference to the
concreteFactory interface. This is accomplished by narrowing the general object reference
CORBA.Object to a more specific one:
Patterns.concreteFactory factory =
Patterns.concreteFactoryHelper.narrow(obj);
This enables you to retrieve the interface. When this has been done successfully, you can invoke methods on
the interface as you normally do.
The abstract factory pattern can be a powerful tool to decouple the creation of an object from the use of it.
This can be helpful when you have to isolate complex routines for creating an object from the client. This
might be necessary when the client need not have the knowledge of how to construct the object.
Summary
Design patterns are a fundamental shift in transforming software development to software engineering.
Patterns encapsulate the best practices that characterize good software. When you are developing distributed
systems, design, more than anything else, is the most critical aspect. The onus on analysis is, to an extent, the
same for distributed systems as it has been for classical systems. But the design of distributed systems is
different.
Many issues that are nonessential to standalone applications are important when considering distributed
applications. Issues of network bandwidth, availability, fault tolerance, and perhaps the aspect of CORBA
being essentially a peer-to-peer technology place a strong emphasis on good design.
Design patterns help focus these principles of good design and bring them to bear on CORBA applications.
Three-tier client/server applications promise better maintainability and efficient adaptation to changing
requirements. With design patterns, that promise can be redeemed.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 24
CORBA Interface Repository
In This Chapter
• The Structure of the Interface Repository
• The ModuleDef Interface
• The InterfaceDef Interface
• The OperationDef Interface
• Using the Interface Repository
• Reading information from the IR
The Interface Repository (IR) is a runtime database that holds a component’s
interface definition in a machine-readable format. The IR potentially has many
uses. Because it essentially holds a component’s IDL information, this
information can be used to make a dynamic invocation on the component.
Remember that the IR contains only interface information; it does not contain
information about the runtime parameters of the component itself. That
information is held in the Implementation Repository.
The interface definitions are at the heart of what makes CORBA tick. A client
communicates with a server component through a “published” interface. In recent
years, there has been a growth of complex architectures being built using
CORBA. These can consist of tens of thousands of components. At some point,
the management of these interfaces could pose a problem. It is at that point that
the IR can become truly useful. A client could introspect the IR to find the right
interface definitions. If the client is designed to use dynamic invocations, the
various parameters of an interface could be introspected to assist in the invocation
of an operation. If, on the other hand, static invocations are being used, the IDL
pre-compiler could be modified to directly generate all the required files directly
from the IR.
As more visual builders become available for developing CORBA applications
(ComponentBroker, IBM VisualAge, VisiBroker, C++ Builder, and so forth), the
IR will be at the center of the visual toolkit. The application builder’s toolkit will
allow the import of component interfaces directly from the IR. This enables a
developer to use drag and drop to construct applications. Nevertheless, we have
some way to go before the IR becomes pervasive.
module Dynamic {
interface dii {
struct DyStruct {
string name;
short age;
};
dii::DyStruct getstuct();
void takesequence(
in strings ins,
out strings outs,
inout strings inouts
);
strings getsequence();
void takenumber(
in long ins,
out long outs,
inout long inouts
);
long getnumber();
void takestring(
in string ins,
out string outs,
inout string inouts
);
void takestring(
in string ins,
out string outs,
inout string inouts
)
string getstring();
void takereference(
in Dynamic::dii ins,
out Dynamic::dii outs,
inout Dynamic::dii inouts
);
Dynamic::dii getreference();
};
};
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
There are a number of logical divisions in the preceding module. These are shown
in Figure 24.1.
The Interface Repository API is structured as definitions. Every element in IDL has
its corresponding definition. You might think that it would have been simple just to
leave IDL undefined in a repository. But Defs, as I call them, serve a higher
purpose: They isolate the semantics of the IDL structure. That way, you don’t have
to construct a parser to figure out what the IDL file contains. Every aspect of IDL is
defined—all the way from modules to exceptions. They all have Defs.
If you consider the previous example, the Dynamic.idl contains five Defs: the
ModuleDef, the InterfaceDef, the StructDef, the SequenceDef, and
the OperationDef. At the root of all the Defs stands the Repository.
Further still, if you take an operation, the IR API also defines methods to figure out
an operation’s parameters, the direction in which a parameter is passed, and its
type. It also specifies how to look up the details of a return type. Furthermore, if an
operation raises any exception, you can use the ExceptionDef to gain
knowledge of that as well.
All these Defs are fine, but they are pretty much useless unless you have a
mechanism of navigating a repository. That is where the next set of abstract entities
comes in: IRObject, Contained objects, and Container objects. The
relationship is depicted in Figure 24.2.
interface IRObject{
readonly attribute DefinitionKind def_kind;
void destroy();
};
typedef sequence<Contained> ContainedSeq;
interface Container:IRObject {
struct Description{
Contained contained_object;
Definition kind;
any value;
};
interface Contained:IRObject{
struct Description {
DefinitionKind kind;
any value;
};
Description describe();
.......
.......
};
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
When the Container object has returned either a
Container:DescriptionSeq or a Container:ContainedSeq
sequence, the Contained interface protocol can be used to drill down to the
details of the IRObject that is contained.
The describe() is the key operation to invoke on a Contained object.
Every IRObject has a description attached to it if it is a Contained object.
Again, you can use the kind (_dk_operation, _dk_module, and so forth)
to figure out how to extract the value that is a CORBA.Any type.
Along with describe(), the various accessor methods for the attribute
provide you with enough information to determine what the Contained
object is.
Essentially, all the components of a repository are subclasses of either
Container and/or Contained. The relationship is the same as shown in
Figure 24.2.
We will only look at ModuleDef, InterfaceDef, and OperationDef
for brevity. For more information on the various API, consult either the
CORBA 2.x specification or your favorite ORB reference manual.
interface InterfaceDef:Container,Contained,IDLType{
struct FullInterfaceDescrition{
Identifier name;
RepositoryId id;
RepositoryId defined_in;
VersionSpec version;
OpDescriptionSeq operations;
AttrDescriptionSeq attributes;
RepositoryIdSeq base_interfaces;
TypeCode type;
};
FullInterfaceDescription describe_interface();
. . . .
. . . .
};
Within its protocol, it specifies an operation named
describe_interface(). It returns a struct
FullInterfaceDescription. It also provides you with a sequence of
operations that are Contained within the interface as well as attributes.
interface OperationDef:Contained{
struct ParameterDescription{
identifier name;
TypeCode type;
IDLType type_def;
ParameterMode mode;
};
The identifier gives you the name of the parameter, the TypeCode to the
underlying TypeCode, and TCKind to specify the type of parameter; the
ParameterMode gives you the direction in which the parameter is passed.
Because the OperationDef is also a Contained, you can treat it as one
by invoking Contained.Describe on it. It will return a Description
struct with the following information that is represented as a struct. The
kind will be dk_operation, and the value will hold a CORBA.Any
pointing to an OperationDescription struct:
struct OperationDescription{
Identifier name;
RepositoryId id;
RepositoryId defined_in;
VersionSpec version;
TypeCode result;
OperationMode mode;
ContextIdSeq contexts;
ParDescriptionSeq parameters;
ExcDescriptionSeq exceptions;
};
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The idl2ir can be used to update a specific IR by specifying a name for the IR. If the repository already
contains the interface, an exception will be thrown. If you want to replace a particular interface, use the
-replace flag. Updating the repository with dii is shown here:
package Dynamic;
import org.omg.CORBA.*;
import org.omg.CORBA.InterfaceDefPackage.*;
import org.omg.CORBA.TypeCodePackage.*;
import java.util.*;
import org.omg.CORBA.Repository;
FullInterfaceDescription InterfaceDescription;
org.omg.CORBA.Repository repository;
org.omg.CORBA.ORB orb;
private int _indent = 0;
orb = org.omg.CORBA.ORB.init();
repository = org.omg.CORBA.RepositoryHelper.bind(orb);
if(idef==null){
System.out.println(“Defenition Not Found”);
System.exit(1);
}
InterfaceDescription = idef.describe_interface();
ReadInterface();
}
switch(kind.kind().value()){
case TCKind._tk_void:
ret = “void”;
break;
case TCKind._tk_short:
ret = “short”;
break;
case TCKind._tk_long:
ret = “long”;
break;
case TCKind._tk_float:
ret = “float”;
break;
case TCKind._tk_double:
ret = “double”;
break;
case TCKind._tk_objref:
try{
ret = kind.name();
break;
}
catch(BadKind bk){break;}
case TCKind._tk_struct:
try{
ret = kind.name();
break;
}
catch(BadKind bk){break;}
case TCKind._tk_string:
ret = “string”;
break;
case TCKind._tk_sequence:
try{
ret = kind.name();
break;
}
catch(BadKind bk){break;}
default:
break;
}
return ret;
}
}
When the reference to the Repository is retrieved using the bind call, the constructor for the class uses the
following line to construct an instance of InterfaceDef:
InterfaceDefHelper idef =
InterfaceDefHelper.narrow(repository.lookup(args[0]));
The name that is passed into the command line must be a fully qualified name to an interface. So if you want
to look up the dii interface defined in the Dynamic module, it can be done by invoking the class with the
following syntax:
struct FullInterfaceDescrition{
Identifier name;
RepositoryId id;
RepositoryId defined_in;
VersionSpec version;
OpDescriptionSeq operations;
AttrDescriptionSeq attributes;
RepositoryIdSeq base_interfaces;
TypeCode type;
};
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The first member of the struct provides us with the name of the interface. The
attributes and the operations members encapsulate a sequence of operations and attributes
that are defined in the interface.
Because we are dealing only with interfaces, it is not necessary to consider the module.
For this reason, the first for loop just iterates over the length of the operation sequence:
Summary
The Interface Repository is a runtime database that can contain interface definitions in
machine-readable format. Any aspect of an interface can be recorded in a Repository.
When recorded, a client can use standard IR API to read and/or modify various parts of
an interface.
One way such a technology can be used is by next-generation CORBA application
builders that can enable developers to build drag-and-drop CORBA applications. It is
also useful for Dynamic Invocation Interfaces. A DII client can use the IR to introspect
the interface and dynamically construct an invocation in memory and make this
invocation. A word of caution, though. IR does not contain meta-interface information.
By that, I mean that the IR does not contain any information that describes the semantics
of an interface. That is something that probably will be available in the next couple of
decades.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 25
CORBA Dynamic Invocation Interfaces
In This Chapter
• Using Dynamic Invocation Interfaces
• Implementing the dii Interface
• Implementing the Dynamic Server
• The Dynamic Client Implementation
• Invoking struct Operations Dynamically
• Invoking Sequence Operations Dynamically
• Invoking String and Primitive Operations Dynamically
• Invoking Object Reference Operations Dynamically
CORBA has always been about compromises. For an organization as large as
Object Management Group (OMG), compromises are the only way such a body can
go forward with the standardization process for distributed systems architectures.
The core CORBA specification is perhaps the most striking feature of such a
process. The C in CORBA stands for Common; as such, it is a compromise that
involves CORBA supporting two types of distributed object interactions: direct, or
static, and indirect, or dynamic.
Static invocations are generally the most common way in which CORBA
applications are built. This type of invocation involves a client being statically
linked to a client-side stub. This type of invocation is generally faster because the
application knows the methods that it needs to invoke on the server side. However,
even the best-designed distributed object applications suffer from one drawback.
This can be characterized as a coupling of interfaces. Object-orientation, at the
most basic level, is about representing a model in terms of interfaces (read
encapsulation). I call it the interface representation paradigm. Objects are different
from the older ways of building systems. The old paradigm can be thought of as
data representation. The two paradigms are related in that the complexity of data
representation systems led to efforts to manage this complexity through data
hiding—encapsulation, to be more precise (see Figure 25.1). Over time, this led to
a full-blown paradigm known as object-oriented systems.
module Dynamic {
interface dii {
struct DyStruct {
string name;
short age;
};
void takestruct(
in DyStruct ins,
out DyStruct outs,inout DyStruct inouts);
dii::DyStruct getstruct();
void takesequence(
in strings ins,
out strings outs,
inout strings inouts
);
strings getsequence();
void takenumber(
in long ins,
out long outs,
inout long inouts
);
long getnumber();
void takestring(
in string ins,
out string outs,
inout string inouts
);
void takestring(
in string ins,
out string outs,
inout string inouts
)
string getstring();
void takereference(
in Dynamic::dii ins,
out Dynamic::dii outs,
inout Dynamic::dii inouts
);
Dynamic::dii getreference();
};
};
The implementation uses VisiBroker For Java 3.3. The interface is compiled using
the -portable flag:
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Implementing the dii Interface
The fact that the client uses DII has no implication for how the dii interface is constructed.
The server in this case (see Listing 25.1) can be implemented as in Chapter 5, “Java/IDL
Mapping.”
Listing 25.1 Dynamic Object Implementation
package Dynamic;
import java.io.*;
import java.util.*;
public dynamic() {
}
inouts.value.name = retname;
inouts.value.age = (short)retage;
}
public Dynamic.diiPackage.DyStruct getstuct() {
String message = “getstuct() Called”;
ios.println(new String(“[Logfile]:Dynamic ” + message));
return new Dynamic.diiPackage.DyStruct(“
Return Name”,(short)44);
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The code has no complexity whatsoever. The only novel feature is that instead of outputting a client
request to the system output terminal, the application uses a log file that is constructed with the
constructor. The following code snippet initializes the log file:
package Dynamic;n
struct DyStruct {
string name;
short age;
};
• Sequence operations—The sequence operation does the same as the struct in the following
operation:
void takestring(
in string ins,
out string outs,
inout string inouts
)
string getstring();
• Primitive operations—Just as with the string operations, the code for dynamically handling
any primitive is pretty much the same:
void takenumber(
in long ins,
out long outs,
inout long inouts
);
long getnumber();
• Object reference operations—These operations pass and return object references:
void takereference(
in Dynamic::dii ins,
out Dynamic::dii outs,
inout Dynamic::dii inouts
);
Dynamic::dii getreference();
Let’s construct a client. Because the client differs very little from other CORBA clients, I shall show
the initialization code just once (see Listing 25.3).
Listing 25.3 DII Client
package Dynamic;
import org.omg.CORBA.*;
static{
orb = org.omg.CORBA.ORB.init();
DynamicObject = orb.bind(“IDL:Dynamic/dii:1.0”,
“DynamicServer”, null, null);
}
public DynamicClient() {
//The implementations to be placed here.
}
}
The first thing the implementation should do is acquire an instance of CORBA::Object representing
the remote object. The following code does this by using bind():
DynamicObject = orb.bind(“IDL:Dynamic/dii:1.0”,
“DynamicServer”, null, null);
The ten operations (two of each type) can be placed in the client’s constructor. I have chosen to isolate
them in separate methods:
//Struct Operations
makeStructCall();
getStructCall();
//Sequence Operations
makeSequenceCall();
getSequenceCall();
//Primitive Operations
makeNumberCall();
getNumberCall();
//String Operations
makeStringCall();
getStringCall();
//Object reference Operations
makeReferenceCall();
getReferenceCall();
The first thing all these methods should do is acquire an instance of the Request class. There are
three ways the request object may be retrieved. All three are specified on the CORBA::Object
interface. The CORBA::Object has been initialized statically:
import org.omg.CORBA.Request;
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The three operations enable three ways to construct a request. The first creates a request
by passing a string. This string represents the name of the operation on the server-side
interface. After the request has been initialized in this manner, all the arguments and
returns, if any, must be initialized before the request can be made.
The other two methods in the previous code snippet work with NVList and
NamedValues. The NVList represents the parameter list of the operation, and the
NamedValues contains the return types. If the last two operations are used, the
NVLIst and the NamedValue must be initialized before the Request object is
constructed. The NVList and the NamedValue objects do not have constructors, so
they cannot be constructed in the usual manner. Invoking the appropriate method in the
ORB can initialize these objects. These are ORB.create_list and
ORB.create_operation_list for NVList and ORB.create_named_
value for NamedValue.
For the purposes of this chapter, let’s use the first mechanism to create a Request
object. As mentioned, after the Request object has been created, the parameters have to
be initialized. Parameters, when passed dynamically, have to be converted to
org.omg.CORBA.Any. The Any type does not have a constructor. The CORBA::Any
type can be created by invoking ORB.create_any().
Dynamic.diiPackage.DyStruct inStruct =
new Dynamic.diiPackage.DyStruct();
Dynamic.diiPackage.DyStructHolder inOutStruct =
new Dynamic.diiPackage.DyStructHolder();
Dynamic.diiPackage.DyStructHolder outStruct =
new Dynamic.diiPackage.DyStructHolder();
inStruct.age = 22;
inStruct.name = “Koolaire”;
Any In = request.add_in_arg();
Any Out = request.add_out_arg();
Any Inout = request.add_inout_arg();
Out.type(Dynamic.diiPackage.DyStructHelper.type());
Dynamic.diiPackage.DyStructHelper.insert(Out,
outStruct.value);
Dynamic.diiPackage.DyStructHelper.insert(Inout,
inOutStruct.value);
Dynamic.diiPackage.DyStructHelper.insert(In,inStruct);
request.invoke();
outStruct.value =
Dynamic.diiPackage.DyStructHelper.extract(Out);
inOutStruct.value =
Dynamic.diiPackage.DyStructHelper.extract(Inout);
System.out.println(outStruct.value.name);
System.out.println(inOutStruct.value.name);
}
The Result object is constructed by invoking the _request on an instance of
org.omg.CORBA.Object:
Dynamic.diiHelper.insert(ins, inReference);
Dynamic.diiHelper.insert(outs, outReference.value);
Dynamic.diiHelper.insert(inouts, inoutReference.value);
All the Helper classes have these functions. They convert a type into CORBA.Any. In
the preceding code, the inReference, outReference.value, and
inoutReference.value types are converted into CORBA.Anys.
If you think about it, the server will face a subtle problem with just this code. The out
parameter is initialized on the server side and passed back to the client. The struct that
is passed contains a string. The size of this string can be determined only at runtime,
because internally the string object is a pointer to a character array.
The server will receive only a CORBA.Any, which is a void pointer. Other than this, the
server has no information to determine the type it is expected to initialize and pass back.
It could have the void pointer, in theory, point to any location in memory.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
In essence, the CORBA.Any is not just a void pointer; it contains a crucial piece of information called
the CORBA.Any.TypeCode that unambiguously represents the type of CORBA.Any that is being
passed around. CORBA.Any and CORBA.Any.TypeCode are provided in the appropriate Helper
classes. For our purposes, the TypeCode support is found in the
Dynamic.diiPackage.DyStructHelper class. The Any placeholder has to contain the
TypeCode as well. This is done in the following line of code:
Out.type(Dynamic.diiPackage.DyStructHelper.type());
As for the in and inout parameters, because the client has initialized them, the server has the required
information to determine what to pass back.
CORBA.Any is now completely initialized and ready to be passed into the server dynamically. At this
point, the Request object is ready to be invoked. The operation has been named, and all the
parameters have been initialized. The actual invocation is made on the following line:
request.invoke();
The request is dynamically marshaled and passed into the server object for execution. Now there is the
small matter of trying to figure out what the server has passed back in the out and inout over to the
client. The types are now CORBA.Any. They need to be converted back to the appropriate types. This is
accomplished by using the very same Helper class that was used to convert the type into a
CORBA.Any. The code is shown here:
outStruct.value =
Dynamic.diiPackage.DyStructHelper.extract(Out);
inOutStruct.value =
Dynamic.diiPackage.DyStructHelper.extract(Inout);
Essentially, that is all there is to dynamic invocations. The rest of the implementation will not be
discussed in as much depth, except when something new needs to be explained.
The second operation that is to be implemented is the getStructCall() method. This method needs
to dynamically handle a struct that is being returned as a result of the invocation:
}
The code is very similar to the implementation that was detailed in the makeStructCall() method.
In the preceding code, the CORBA.Any that is being returned is set to the right TypeCode in the
following line:
request.set_return_type(Dynamic.diiPackage.DyStructHelper.type());
java.lang.String[] inSequence;
Dynamic.diiPackage.stringsHolder outSequence;
Dynamic.diiPackage.stringsHolder inoutSequence;
Any In = request.add_in_arg();
Any Out = request.add_out_arg();
Any Inout = request.add_inout_arg();
Out.type(Dynamic.diiPackage.stringsHelper.type());
Inout.type(Dynamic.diiPackage.stringsHelper.type());
Dynamic.diiPackage.stringsHelper.insert(In,inSequence);
Dynamic.diiPackage.stringsHelper.insert(Out,
outSequence.value);
Dynamic.diiPackage.stringsHelper.insert(Inout,
inoutSequence.value);
request.invoke();
outSequence.value =
Dynamic.diiPackage.stringsHelper.extract(Out);
inoutSequence.value =
Dynamic.diiPackage.stringsHelper.extract(Inout);
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The singular difference in this implementation is the setting of the return type on the Request
object:
request.set_return_type(orb.create_sequence_tc(0,
orb.get_primitive_tc(
org.omg.CORBA.TCKind.tk_string)));
The return type is in essence an array of String objects. The string object is considered to be a
primitive, so the implementation does not have to use a holder class to construct the return
CORBA.Any or its TypeCode.
org.omg.CORBA.Request request =
DynamicObject._request(“takenumber”);
Any ins = request.add_in_arg();
Any outs = request.add_out_arg();
Any inouts = request.add_inout_arg();
ins.insert_long(inNumber);
inouts.insert_long(inoutNumber.value);
outs.insert_long(outNumber.value);
request.invoke();
outNumber.value = outs.extract_long();
inoutNumber.value = inouts.extract_long();
System.out.println(“INOUT PRIMITIVE: ” +
new Integer(inoutNumber.value));
System.out.println(“OUT PRIMITIVE: ” +
new Integer(outNumber.value));
}
request.invoke();
int ReturnPrimitive = 0;
ReturnPrimitive = request.return_value().extract_long();
System.out.println(“RETURN PRIMITIVE: ” +
new Integer(ReturnPrimitive));
}
outs.type(orb.get_primitive_tc
(org.omg.CORBA.TCKind.tk_string));
ins.insert_string(inString);
outs.insert_string(outString.value);
inouts.insert_string(inoutString.value);
request.invoke();
outString.value = outs.extract_string();
inoutString.value = inouts.extract_string();
System.out.println(“INOUT STRING : ” +
inoutString.value);
System.out.println(“OUT STRING: ” + outString.value);
}
ins.insert_long(inNumber);
inouts.insert_long(inoutNumber.value);
outs.insert_long(outNumber.value);
In this instance, CORBA.Any.TypeCode is not required, because the amount of memory
required for CORBA.Long is specified, and the associated marshaling can be unambiguously
handled by the code generated for DII.
After the arguments are constructed and initialized, the invocation is made with
request.invoke(). The out and inout parameters are retrieved by the following code:
outNumber.value = outs.extract_long();
inoutNumber.value = inouts.extract_long();
The implementation for makePrimitiveCall() follows along similar lines. The difference
is that CORBA.Any.TypeCode needs to be added for the out parameter, because the server
needs to know how to handle this type correctly.
In the getStringCall(), the implementation enables the client to dynamically invoke the
corresponding operation getstring() on the Dynamic.dii interface. The code is standard
as for other implementations. The difference is in how the return type is set up. Because the
return type is CORBA.Any, the correct CORBA.Any.TypeCode is passed on so that the
cast from CORBA.Any to java.lang.String happens correctly:
request.set_return_type(orb.get_primitive_tc(
org.omg.CORBA.TCKind.tk_string));
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Invoking Object Reference Operations Dynamically
Operations involving object references can also be constructed dynamically. These
operations can have object references passed in all three directions—in, out, and
inout—and can handle return types. The two methods dealing with object references
are makeReferenceCall() and getReferenceCall(). The issues related to
dynamic operations that involve object references are very similar to implementations
dealing with parameters that involve allocation of memory that can only be determined
during runtime. Because parameters are passed as CORBA.Any, the associated Helper
classes are used to convert an object reference type to CORBA.Any and back. These
classes handle the allocation of memory correctly for you.
The implementations for the two methods are illustrated in Listing 25.8.
Listing 25.8 Constructing References Dynamically
Request request =
DynamicObject._request(“takereference”);
Dynamic.diiHelper.insert(ins, inReference);
Dynamic.diiHelper.insert(outs, outReference.value);
Dynamic.diiHelper.insert(inouts, inoutReference.value);
request.invoke();
System.out.println(“OUT REFERENCE : ”
+ outReference.toString());
System.out.println(“INOUT REFERENCE : ”
+ inoutReference.toString());
}
Request request =
DynamicObject._request(“getreference”);
request.set_return_type(Dynamic.diiHelper.type());
request.invoke();
Dynamic.dii returnReference;
returnReference =
Dynamic.diiHelper.extract(request.return_value());
System.out.println(“RETURN REFERENCE : ”
+ returnReference.toString());
}
The code in the makeReferenceCall() is very similar to the code for handling
struct parameters dynamically. The Request object is constructed. Calling the
appropriate Request interface operation sets the CORBA.Any placeholders for the
parameters. If an out parameter cannot be correctly identified by the server for runtime
memeory allocation, the TypeCode is set. If you are dealing with native IDL types then,
the CORBA.Any.Typecode is formally specified. If they are user-defined types, as is
in the case of out, the correct TypeCode should be set for the CORBA.Any. The
setting of the TypeCode is shown here:
outs.type(Dynamic.diiHelper.type());
The getReferenceCall() needs to correctly convert the returning CORBA.Any to
the right type. This is accomplished as you would handle an out parameter:
request.set_return_type(Dynamic.diiHelper.type());
With the ten methods laid out in this chapter, you should be able to handle any
combination of parameters dynamically and synchronously.
Summary
Dynamic Invocation Interfaces (DII) are the other half of the CORBA way of doing
things—compromises. Together with Static Invocation Interfaces, you have the Common
in CORBA. As more and more complex distributed systems are built, the property that
has been the strength of object-oriented technology, encapsulation, will become strained.
Encapsulation is the mechanism by which interdependencies between the components
that make up a system are kept to a minimum.
When the minimum is not enough, meaning when the complexity of a solution grows
beyond the confines of the original problem into a matrix client/server problem, clients
should be capable of making dynamic requests on the server. This enables a server-side
interface to be changed without disrupting existing solutions.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Chapter 26
Developing CORBABean Wrappers
In This Chapter
• The Simple JavaBeans Component Model
• JavaBean Properties and Editors
• JavaBean Event Model
• Using the CorbaBean
• The BeanInfo Class
• Implementing a BeanInfo Class for the CoffeeMaker
• Property Editors and Property Sheets
Object-oriented (OO) systems are in the process of maturing. There was a time
when you dealt with OO by dealing with issues such as classes and objects.
Over the last few years, the paradigm has matured into a more robust
worldview.The worldview is very simple and elegant. It is based on the
premise that complex software systems can be built like a child building
something using LEGO bricks, through a process of assembling blocks with a
standard interface. Such a worldview is known as component-based
application development. The main difference between a normal object and a
component is that the former is just a runtime instance of some class. A
component can also be an object that is an instance of some class, but it differs
in one essential manner: Components are usually restricted to a standardized
convention in describing the interface.
Like your LEGO bricks, a component model also has a standard interface that
enables the rest of the world to interact with it in a standard manner. Such
interactions could involve GUI-based workbenches that enable a developer to
drag and drop components from a pallet to construct an application. They
could be one of those expensive CASE tools that enables animation of a model
using prebuilt components to display the various interactions.
There are two popular component models in the industry right now: MS
Component Object Model–based components and JavaSoft’s JavaBeans
component model. At one level, both are competing platforms for developing
components, but both vendors have technology that enables you to integrate
one in the other. In this chapter, we shall consider only the JavaBeans model
for building components. CORBA as a solution is used mainly to solve
problems that involve integrating systems across operating systems and
platforms, such as an NT-based client being serviced by a UNIX server and so
on. Java is inherently a multiplatform technology; this enables Java solutions
to have both source code portability as well as bytecode portability. This
makes Java ideal for building CORBA solutions.
In that context, it is also beneficial if systems architects could rely on a
multiplatform component model as well. Hence, JavaBeans makes the ideal
component model for building CORBA components. Before we look into the
relationship between CORBA and JavaBeans, let’s take a brief tour of the
JavaBeans component model.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Let’s look at the client-side beans model as it is aimed at IDEs. IDEs are critical to CORBA if it is
to survive the sustained attack it will come under from other distributed component models.
CORBA components wrapped as JavaBeans enable developers to use CORBA in the
drag-and-drop, click-and-connect environments that JavaBeans are capable of providing. This
enables you to add JavaBean-wrapped CORBA objects to a component pallet to allow a developer
to simply drag and drop these components to build a GUI.
The following example will help illustrate how a simple JavaBean component can be built. We are
going to take a simple CORBA interface and wrap it inside a JavaBean. The following IDL
interface is used as an example:
module CorbaBean{
interface CoffeeMachine{
void setSwitchOn(in boolean value);
void setCoffee(in string brew);
void setTimer(in long time);
void brew();
};
};
The three operations could have been specified as read-only operations. In that case, the resulting
Java interface would have been incompatible with the JavaBean model. Therefore, the IDL interface
was specified so that the resulting interface would be JavaBean-compatible. In the example, the
three methods, setSwitchOn(), setCoffee(), and setTimer() correspond to three
modifier methods for three encapsulated properties of a bean. get and set methods result from
specifying attributes in your interfaces. Unfortunately, the accessor and modifier functions mapped
from attributes are not JavaBean-compatible. You have to specify the accessor and modifier
operations directly in your interface itself. For example, consider the following IDL interface:
interface Date{
attribute string date;
};
This interface will generate the following Java code when it is run through a JavaIDL pre-compiler:
package CorbaBean;
public class CoffeeMaker extends CorbaBean._CoffeeMachineImplBase {
boolean SwitchOn;
String Coffee;
int Timer;
public CoffeeMaker(java.lang.String name) {
super(name);
}
public CoffeeMaker() {
super();
}
public void setSwitchOn(
boolean value
) {
if(SwitchOn != value)
SwitchOn = value;
}
public void setCoffee(
java.lang.String brew
) {
if(!SwitchOn)
Coffee = brew;
}
public void setTimer(
int time
) {
if(!SwitchOn)
Timer = time;
}
public void brew() {
Syste.out.println(Coffee “ is brewing for “ + Timer + “ Seconds”);
}
}
At this point, there is nothing special about our component. The next step is to add support for
beans. This has been partially done though the accessor methods that our component already
supports. This support can be augumented by adding support for editors and the Beaninfo class to
the component.
Simple Property
Simple properties are the ones that are directly accessible through the get and set methods that
you provide. In the preceding example, the CorbaBean.CoffeeMaker supports three simple
properties: SwitchOn, Coffee, and Timer. These three are only provided with modifier
methods that a client can use.
The JavaBean model specifies additional ways of exposing properties.
Indexed Properties
Indexed properties are really like simple properties except that there is a sequence of such
properties. In this case, the index is used to refer to the correct property element within the
sequence. Such a property mechanism can be used when a JavaBean component refers to a whole
series of properties. The following is a snippet of code that shows how such a set of properties could
be used:
String[] getindexedProperty();
void setindexedProperty(String[] indexedProperty);
The preceding are get and set methods for the entire array of properties. When the developer
needs to access or modify just one property from an index, the following methods can be used:
Bound Properties
Bound properties are properties that generate events when the states of these properties are altered.
Such a mechanism can be used for a bean with bound properties to notify other beans when a
property is changed. For such a bean to generate events when a bound property changes, it must
implement the Java.beans.PropertyChangeListener interface.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Bound properties are useful if you want to tie state-dependent subcomponents together. This
means that when one component changes its state, it triggers the change of state in a dependent
object and so on. In the preceding interface implementation, the Timer can be considered as a
bound property. When the Timer’s state violates a certain constraint—in our case, when the
timer expires—the bean should trigger an event to notify interested parties that the timer has
expired.
Constrained Properties
Constrained properties are an extension of bound properties. Like the bound properties, a
constrained property also notifies interested listeners that the state of a component has changed.
The listener, in turn, can raise an exception, vetoing a potential change. Such an architecture
enables you to couple an arbitrary number of components together purely in terms of their internal
state transitions.
The event that is generated by a component is the vetoableChange event. On receiving the
event, if the listener needs to veto the change of state, the listener can throw the
Java.beans.PropertyVetoException to veto the change of event.
We will not try to build a fully fledged JavaBean component. Keet in mind that JavaBeans is a
client-side component model. As such, supporting the entire JavaBean model might be
unnecessary for CORBA components. But aspects of the model can be used to make the
components you build a bit simpler to understand. This simplicity enables us to use CORBA
components inside an IDE. At the most basic level, properties are the only things that a bean needs
to support. Beyond that, anything that you provide is a bonus. I always try to hook up events to
properties. They are a much better way for components to modify or access properties than with
get and set methods.
package CorbaBean;
import java.util.*;
package CorbaBean;
import java.util.*;
boolean SwitchOn;
String Coffee;
int Timer;
private transient Vector brewListeners;
fireBrewing(event);
}
public void setCoffee(
java.lang.String brew
) {
if(!SwitchOn){
Coffee = brew;
BrewEvent event = new BrewEvent(this,brew +
“ Coffee Has been Selected”);
fireBrewing(event);
}
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
In the preceding implementation, the component fires an event whenever the remote client makes an
invocation on the server object. In our context, the GUI will acknowledge the events by displaying them in
some widget.
The class can fire an event whenever it chooses. To do so, the class first constructs an event object. It does
so as shown in the following code snippet:
public CoffeeFrame() {
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
}
void coffeeMakerComponent_Brewing(BrewEvent e) {
EventListerDisplay.append(e.toString() + “\n”);
}
void StartButton_mouseClicked(MouseEvent e) {
CorbaBeanServer server = new CorbaBeanServer(
EventListerDisplay,coffeeMakerComponent);
Thread thread = new Thread(server);
thread.start();
}
class CorbaBeanServer implements Runnable{
try{
boa.impl_is_ready();
}
catch(org.omg.CORBA.SystemException e){
display.append(e.toString() + “\n”);
}
}
}
}
This listing consists of two classes. The first is the GUI class itself, which is named CoffeeFrame and is
a subclass of JFrame. The second class is the CORBA class that is implemented as a thread.
CoffeeFrame is mostly user interface–related. However, in the next sections, I discuss how the GUI can
be coupled with the CORBA bean.
The GUI uses Java inner classes to construct the BrewListener interface:
coffeeMakerComponent.addBrewListener(
new CorbaBean.BrewListener() {
public void Brewing(BrewEvent e) {
coffeeMakerComponent_Brewing(e);
}
}
);
The callback interface is implemented in the following manner:
void coffeeMakerComponent_Brewing(BrewEvent e) {
EventListerDisplay.append(e.toString() + “\n”);
}
The method just updates the display when a remote client alters the state of the CORBA component. The
GUI is also the CORBA server. Because the server blocks when the impl_is_ready() method is
called, it is run on a thread. The server implementation is shown in Listing 26.6 for completion.
Listing 26.6 CORBABeanServer Implementation
try{
boa.impl_is_ready();
}
catch(org.omg.CORBA.SystemException e){
display.append(e.toString() + “\n”);
}
}
}
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
The server code executes on a thread. The server itself is launched by the GUI in response to a mouse click on a
button. The bean model is not used with the client. Therefore, the client is very similar to the ones that we have
built before, as shown in Listing 26.7.
Listing 26.7 CorbaBean Client Implementation
package CorbaBean;
public CorbaBeanClient() {
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init();
CoffeeMachine coffee = CoffeeMachineHelper.bind(orb,”CoffeeMaker”);
coffee.switchOn(true);
coffee.selectCoffee(“Java”);
coffee.setTimer(5001);
coffee.brew();
coffee.switchOn(false);
}
public static void main(String[] args) {
CorbaBeanClient corbaBeanClient = new CorbaBeanClient();
}
}
The client is a straightforward implementation. It sequentially invokes the server interface. What should be
happening on the server side is that when an invocation comes into the component, the component generates an
event that is delivered to the GUI component. In Figure 26.1, that is precisely what is happening.
package CorbaBean;
import java.beans.*;
public CoffeeMakerBeanInfo() {
}
PropertyDescriptor _switchOn =
new PropertyDescriptor(“switchOn”, beanClass, null, “setSwitchOn”);
_switchOn.setDisplayName(“Power”);
PropertyDescriptor _timer = new PropertyDescriptor(
“timer”, beanClass, null, “setTimer”);
_timer.setDisplayName(“Timer”);
PropertyDescriptor[] pds = new PropertyDescriptor[] {
_coffee,
_switchOn,
_timer,
};
return pds;
}
catch (IntrospectionException ex) {
ex.printStackTrace();
return null;
}
}
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
-----------
Listing 26.9 CoffeeMaker Property Editor Implementation
package CorbaBean;
import java.beans.*;
public CoffeeList() {
}
Summary
The JavaBean component model is a crucial breakthrough in the evolution of object- oriented systems to
the next level of abstraction, which is based on components instead of classes. The JavaBean component
model is inherently simpler than the older component models that came before it, because it does not
take a lot of work to turn an existing Java class into a component.
One of the ways in which the JavaBean model can be leveraged by CORBA development is to use
JavaBean as a mechanism to integrate in a cohesive manner the requirements of a GUI on the server
side. This can be accomplished in a clean and nonintrusive manner by using various structural design
patterns that promote decoupling. The JavaBean model, at its heart, is a set of design patterns, with the
added benefit of the model being a standard as well.
As much as possible, design your server components with the plugs that enable it to be integrated into a
GUI using the JavaBean model. Such a strategy will ultimately promote cleaner layers of abstractions
that should ease your maintenance problems.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
Table of Contents
-----------
PART VI
Appendixes
In This Part
• CORBA API Reference
• TIE and BOA
• Orbix and MFC
Appendix A
CORBA API Reference
In This Appendix
• The BOA API Reference
• The CORBA::Object API
• CORBA::ORB
• CORBA::Request
• CORBA::Principal
The core API that is exposed by an ORB implementation is explained in this appendix. Only
CORBA-compliant APIs are explained. For the proprietary extensions various vendors make to an ORB, it
is best to refer to the appropriate manuals.
CORBA::BOA::change_implementation()
CORBA::BOA::create()
CORBA::BOA::create() can be used to create a new object reference. This function does not create an
object. This means that a call to this method assumes that the object already exists in a server.
CORBA::BOA::deactivate_impl()
When a server is ready to receive invocations from the client, it goes into a blocked call that is initialized
by the impl_is_ready() method.
When the server is ready to shut down, it can deactivate the resources that it initialized by the
impl_is_ready() call. The server can accomplish the deactivation by calling deactivate_impl()
and passing the server name in the parameter impl. Invoking the deactivate_impl() causes
impl_is_ready() to return from its blocked state.
CORBA::BOA::deactivate_obj()
As with the BOA, an implementation can notify the BOA that an object reference is ready for interaction
with a client. This occurs when an implementation requires informing the BOA that it has completed the
initialization of the implementation. When the server needs to deinitialize the implementation, it can call
the deactivate_obj to remove the object from the request pool.
CORBA::BOA::dispose()
CORBA::BOA::get_id()
CORBA::BOA::get_id() returns the identification information of the object obj that was initialized
when the CORBA::BOA::create() was called.
CORBA::BOA::get_principal()
CORBA::BOA::impl_is_ready()
The impl_is_ready() method is at the heart of CORBA. This is the main method that is invoked
when all the initialization is completed. This method blocks until the deactivatate_ impl() is
invoked that causes this method to return. When this method is invoked, it informs the ORB that the
implemention is ready to receive invocations from the client.
This method blocks the server until an event occurs, handles the event, and reblocks the server to await
another event. The impl_is_ready() function returns only under the following circumstances:
• A timeout occurs.
• An exception occurs while waiting for or processing an event.
• The function CORBA::BOA::deactivate_impl() is called.
CORBA::BOA::obj_is_ready()
The obj_is_ready() method is called when the initialization of the object is completed and it is ready
to receive requests from the client. As with imp_is_ready, this method enables a service to remain
active as long as the following conditions do not occur:
• It calls CORBA::BOA::deactivate_obj().
• The call to obj_is_ready() times out.
• An exception is raised.
interface Object {
boolean is_nil();
Object duplicate();
void release();
ImplementationDef get_implementation();
InterfaceDef get_interface();
Status create_request(
in Context ctx,
in Identifier operation,
in NVList arg_list,
in NamedValue result,
out Request request,
in Flags req_flags);
};
CORBA::Object::_duplicate()
CORBA::Object::_get_implementation()
CORBA::Object::_get_interface()
CORBA::Object::_hash()
Every object reference that is constructed by an application has an object identifier associated with it. This
identifier remains unique throughout the lifetime of the object reference. When using this method, it is
conceivable that two separate and different object references may provide the same hashed value. If this is
the actual case, the two object references in question are different objects.
CORBA::Object::_is_a()
CORBA::Object::_is_equivalent()
CORBA::Object::_nil()
CORBA::Object::_non_existent()
CORBA::Object::_request()
CORBA::ORB
CORBA::ORB provides a set of functions that enable you to control an ORB from both the client side and
the server. Some of these operations include support for an application to convert object references to a
string (IOR) and back to an object reference. The full interface that is CORBA-compliant follows. After the
interface, I discuss briefly the more important APIs of this interface:
// Pseudo IDL
// In module CORBA
pseudo interface ORB (
typedef sequence<Request> RequestSeq;
typedef sequence<string> arg_list;
typedef string OAid;
BOA BOA_init(inout arg_list argv,in OAid boa_identifier);
string object_to_string(in Object obj);
Object string_to_object(in string str);
Status create_list(in long count,out NVList new_list);
Status create_operation_list(in OperationDef oper, out NVList new_list);
Status get_default_context(out Context ctx);
Status create_environment(out Environment new_env);
Status send_multiple_requests_oneway(in RequestSeq req);
Status send_multiple_requests_deferred(in RequestSeq req);
boolean poll_next_response();
Status get_next_response(out Request req);
};
string object_to_string(in Object obj) converts an object reference into a string. This
string is the IOR for an object.
When an IOR is constructed, it can be converted back to an object reference using this operation. The
string_to_object() returns an instance of CORBA::Object. This object reference has to be
narrowed to the required object.
CORBA::Request
The interface CORBA::Request provides all the support required for Dynamic Invocation Interface
(DII). DII enables an application to send requests to an interface without actually knowing anything about
the interface during compile-time. The pseudo interface for this object follows. The request object is
constructed by the client and issued to the ORB to make a remote invocation on a server object:
Status invoke()
Status invoke() instructs the ORB to make a request on a target object using DII. This method
assumes that the operation itself has been properly constructed before the invocation can be made.
Status send_oneway()
Status send_oneway() enables the construction of an operation that can be invoked on the target as
a oneway operation. A oneway operation enables a client to make an asynchronous operation on the
server object. Like the invoke() method, the operation itself must be constructed properly before the
invocation is made.
Status send_deferred()
Status send_deferred() enables the construction of an operation that can be invoked on the target
as a oneway operation. A oneway operation enables a client to make an asynchronous operation on the
server object. Like the invoke() method, the operation itself must be constructed properly before the
invocation is made.
Status get_response()
Status get_response() enables the client to determine the outcome of an operation that has been
made on the server object. It returns only when the request has completed.
boolean poll_response()
If the client made an invocation on the server object using send_deferred(), boolean
poll_response() can be used to determine the outcome. The function returns immediately. If the
operation has completed, the result is available in the Request.
CORBA::Principal
Class CORBA::Principal implements the IDL pseudo interface Principal, which represents
information about principals that are the client. The information encapsulated in the Principal object
can be used to provide authentication and access control.
Table of Contents
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
Table of Contents
-----------
Appendix B
TIE and BOA
In This Appendix
• The BOA Approach
• The TIE Approach
You should understand the essential difference between TIE and Basic Object Adapter (BOA), because it has
consequence on the overall design and extensibility of a solution. The role of these components is to relate an
IDL interface to its implementation. Most implementations of CORBA provide the BOA approach as well as
the TIE approach. The interesting thing about these mechanisms is that they can be mixed so that an
implementation can use both TIE and BOA.
BOA approaches are by far the most favored approach taken by most implementers. I believe this is mainly
because most documentation and examples seem to deal exclusively with BOA-based approaches.
module Patterns {
interface abstractFactory{
Object create();
void destroy();
};
interface concreteFactory:abstractFactory{
struct Struct{
short number;
};
readonly attribute long identifier;
Struct getStruct();
};
};
After the interface has been pre-compiled, it has to be implemented. If you choose to use the BOA method,
you have to subclass from the [Name Of the interface]ImplBase. This class is generated by the
pre-compiler. In the case of the Factory interface, there are two ImplBase classes that are generated, one
for each interface in the module:
package Patterns;
public class concreteFactory_i extends Patterns._concreteFactoryImplBase {
package Patterns;
Table of Contents
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
Table of Contents
-----------
Appendix C
Orbix and MFC
In This Appendix
• Starting an Orbix/MFC Project
• The Server-Side Interface Implementation
• Building the Client
In this appendix, you look at how to use Orbix and the Visual C++ environment together so that they can be
used productively.
module mfc{
interface authenticator{
struct user {
string name;
string pwd;
};
};
};
This interface naturally consists of two projects: one for the server side and the other for the client side. The
server side uses a normal console application. The client side uses MFC to implement the application.
#include <iostream.h>
#include “authn.hpp”
cout<<aUser.name<<endl;
cout<<aUser.pwd<<endl;
}
The logic of the implementation is limited to just outputting the parameter to the standard I/O. The server
implementation is also very straightforward:
#include <iostream.h>
#include “authn.hpp”
void main(){
mfc::authenticator_var authenticate;
try {
CORBA::Orbix.impl_is_ready(“Signer”);
}
catch (CORBA::SystemException &sysEx) {
cerr << “Unexpected system exception” << endl;
cerr << &sysEx;
exit(1);
} catch (...) {
cout << “Unexpected exception” << endl;
exit(1);
}
}
The server code consists of initializing the mfc::authenticator class and informing the BOA that the
implementation is ready to receive invocations.
Register the server using the Orbix Server Manager or the putit utility. When registered, the server runs as
shown in Figure C.1.
The application will not use all the fancy options that MFC provides. For instance, you can add support for
multidocument interface–based UI or add support for databased. For the present exercise, we will just stick to
the basics. Therefore, all the other options are turned off.
When the required files have been generated, switch to the resource pane. Select the dialog folder to open the
IDD_ORBIXMD_DIALOG resource. Add two text box and two button controls to the form so that the end
result looks like Figure C.6. The object is to allow someone to use the form as a password requestor dialog for
an application.
The second TextField is a password field so that when the user enters some information, the text is
replaced with asterisks.
Modification to the application is made to the COrbixMDApp class. Therein, the following code is added to
the InitInstance() method:
BOOL COrbixMFCApp::InitInstance()
{
COrbixMFCDlg dlg;
int nResponse = dlg.DoModal();
try{
mfc::authenticator_var remoteObject =
mfc::authenticator::_bind(“:Signer”);
mfc::authenticator::user_var User;
if (nResponse == IDOK){
User->name = dlg.m_name;
User->pwd = dlg.m_pwd;
remoteObject->authenticate(User);
}
}
return FALSE;
}
This implementation is not radically different from other client implementations except that the
implementation is encapsulated in an MFC method.
After the code is entered, all you have to do is compile and link it.
Summary
Orbix 2.3 has taken another step toward closer integration with Microsoft Visual C++ with the addition of
improved support for MFC. MFC is, by far, the most-used class library on the market. Despite CORBA’s
cross-platform support, it seems likely that CORBA is going to be most heavily adopted in Microsoft
environments.
Therefore, it seems necessary to have MFC integration as painless and effortless as possible. As the example
in this appendix illustrates, this is now possible.
Table of Contents
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
Table of Contents
-----------
Index
A
abstract factory pattern interface implementation, listings
concreteFactory, 467
FactoryClient, 469-470
FactoryServer, 468
overview, 466-467
ACID characteristics of transactions, 236
funds transfer example, 237
activation modes
overview, 274
per-method activation, 291
persistent activation, 292
shared activation
C++ server implementation, 284
Java server implementation, 284-285
overview, 282
per-client process shared activation, 283, 286
per-client shared activation, 283
types, 275
unshared activation client
C++ implementation, 289
Java implementation, 290
unshared activation mode
C++ implementation, 287
Java implementation, 288
overview, 287
agents, mobile, 442-443
Voyager, 443-450
amazon.com Web site, 37
APIs (application programming interfaces)
BOA API, 536-538
Generic Security Services Application Program Interface (GSS
API), 262, 270
interceptor, 409-421
Java servlets, 430-431
applications
Java2iiop, 370-373
launching
options, 72
SOAR, 74
architectures, infrastructural components, 12
array IDL types, 146-154
array memory management, client-side and server-side, 177
asynchronous server implementation, 358
attaching. See installing
attachPost() method, 306
attachPre() method, 306
authentication, 261
authorization, 261
B
banking example of transactional programming, 241
LittleBank embedded SQL, 244-245
LittleBank interface, 242
LittleBank interface header, 243
LittleBank interface implementation, 246
LittleBank transaction client, 249-250
LittleBankServer interface implementation, 247
basic IDL types, 136
with IDL operations, 157
Basic Object Adapter (BOA) approach, 544-546
API, 536-538
compared to Portable Object Adapter (POA), 104
BeanInfo class, 528-530
bind interceptors, 409
bind method, 187
BindFilter interface implementation, 418
binding names, 187-188
BindInterceptor class, 411
initialization class, 419
BindInterceptor
implementations
BindFilter, 418
FilterClient, 421
FilterServer, 420
initialization class, 419
bind_context method, 187
bind_new_context method, 187
BOA (Basic Object Adapter) approach, 544-546
API, 536-538
compared to Portable Object Adapter (POA), 104
BOA BOA_init(inout arg_list argv,in OAid boa_identifier) function,
540
boolean poll_response() function, 542
bound JavaBean properties, 519-520
bridge concept, 9
bridges, IIOP, 10
C
C++
classes, mapping object references to, 124
OTS (Object Transaction Service), 240
C++ implementations
loaders, 329
memory management
client-side for IDL strings, 172
heuristics, 179
IDL arrays, 176
IDL operations, 166-167
IDL sequences, 172-174
IDL strings, 169-171
overview, 166
parameter types, 178
overview, 166
server, of shared activation mode, 284
unshared client implementation, 289
unshared server implementation, 287
Caffeine, 368
2iiop compiler, 368-370
Java2idl compiler, 368
Java2iiop
applications, 370-373
clients, 377-379
compiler, 368
servers, 373-377
URL naming, 368
callbacks
asynchronous, defining, 357-364
client user interface, 361-364
defining, 348
chat server application, 349-356
description, 348
server implementation, 359
server user interface, 360-361
uses, 348
cbooks.com Web site, 37
CGI (Common Gateway Interface), 426
chat server application, 349
chatParty client/server, 350-354
chatServer, 354-355
chatServer runtime implementation, 355-356
chatParty client/server interface, 349-354
classes
BeanInfo, 528-530
BindInterceptor, 411-412
ClientInterceptor, 410
Filter, 297
methods, 297-298
Holder, 156-157
IORTransmitter, 21
IORTuner, IORR server implementations, 26
Java, 136
objectFilter, 304
Offer, 203
OrbixIORTuner, 28-32
ProcessFilter, 300
Property Editor, 531
Repository, 204
ReservationEvent, code, 45
ServerInterceptor, 409-410
client interceptors, 409
client-side event handlers, 396-398
compared to interceptors, 400
installing, 400
registering, 398, 400
client-side memory management
arrays, 177
sequences, 175-176
strings, 172
ClientInterceptor class, 410
clients
CORBA servlets, 432-439
filters, installing
per-object, 306-307
per-process, 302
Hangar client implementation, 193-195
invocations, exception handling, 14
IORR transmitter, 19
IORR server implementations, 23
Java2iiop, 377-379
OTS (Object Transaction Service), developing
banking example, 241
Current interface, 240
LittleBank embedded SQL, 244-245
LittleBank interface, 242
LittleBank interface header, 243
LittleBank interface implementation, 246
LittleBank transaction client, 249-250
LittleBankServer interface implementation, 247
transactional C++ and Java, 240
register() method, IORR, 17
SOAR system, interacting with servers, 75
clientstubonly switch, 27
code, process activation mode interface, 275-276
code listings, filters
constructing, 299-300
per-object
clients, 306-307
defining, 303-304
servers, 305
per-process
client filters, 302
server filters, 301
command patterns
benefits, 456
listings, 456-457
CloseConnection interface, 459
command client, 462
command server, 461
DataOperations interface, 460-461
OpenConnection interface, 458
OperateOnDatabase interface, 458
Patterns::Command interface, 457
overview, 455
commands, idl, -F switch, 303
Common Gateway Interface (CGI), 426
Common Secure Interoperability (CSI), 266
levels of conformance, 266
compiling IDL, 15-19
component-based application development, 514
components
callbacks, 348
Trader Service, 199
connection-oriented transports, GIOP (General Inter-ORB
Protocol), 9-10
constancy of interfaces, power nine problem, 212
constants, 154-155
constrained JavaBean properties, 520
constraints, SOAR system, 38
constructed IDL types, 136, 140-141
DII (Dynamic Invocation Interface), 501-504
with IDL operations, 157-162
consumer-driven event model, Event Service, 214
containers, description, 167
contexts, NamingContexts, 186
continueProcess() method, OrbixIORTuner class, 31
cookies, stateless servers, 37
CORBA Java servlets, 431
clients, 430-439
presentation layers, 428-429
reasons for usage, 427
CORBA 1.0
ORB vendors, 8
protocols, 10
CORBA 2.0
IIOP, 10
vendors, 8
CORBA, String var type, memory management, 170
CORBA, events-based applications
typed push models
push consumer, 231-233
push supplier, 225-230
untyped push models, 217
push event client implementation, 222-224
push supplier, 217-221
CorbaBean applications, 524-528
CORBASEC (CORBA Security Specification)
elements, 260
authentication and authorization, 261
domain, 262
policy, 261
principal, 260
privileges, 261
session/context, 262
goals, 260
interfaces
accessing, 267
HTTP tunneling, 268
IIOP proxy servers and firewalls, 268
Java security, 269
levels of compliance, 266
Level 1, 266
Level 2, 267
overview, 260
CORBAServices, infrastructural components, 12
CORBA functions
_duplicate(), 538
_get_implementation(), 538
_get_interface(), 539
_hash(), 539
_is_a(), 539
_is_equivalent(), 539
_nil(), 539
_non_existent(), 539
_request(), 539
change_implementation(), 536
create(), 536
deactivate_impl(), 536-537
deactivate_obj(), 537
dispose(), 537
get_id(), 537
get_principal(), 537
impl_is_ready(), 537
obj_is_ready(), 538
Object\Principal interface, 542
Request interface, 541-542
cryptology, adding, 265
Secure Inter-ORB Protocol (SECIOP), 266
Secure Socket Layer (SSL), 265
current interface
CORBASEC and ORB integration, 267
HTTP tunneling, 268
IIOP proxy servers and firewalls, 268
Java security, 269
OTS (Object Transaction Service), defining, 240
CUTrader interface example, 200-201
client-side CUTrader service, 209-210
CUTrader exporter, 206
CUTrader importer, 208-210
CUTrader service, 207-208
implementing, 201-204
server-side implementation, 206
using, 206
D
data representation paradigm, 490
delegated event model, Event Channel, 214
semantics, 215-216
deleting names, 188
dependencies of interfaces, reducing, 212
design metrics, 455
design patterns
abstract factory pattern implementation, listings,
concreteFactory interface , 467
FactoryClient interface, 469-470
FactoryServer interface, 468
overview, 466-467
benefits, 455
command patterns, listings, 456-457
benefits, 456
CloseConnection interface, 459
command client, 462
command interface, 457
command server, 461
DataOperations interface, 460-461
OpenConnection interface, 458
OperateOnDatabase interface, 458
overview, 455
overview, 454
proxy pattern implementation, listings
overview, 463
proxy client, 465-466
proxy interface, 463-464
proxy server, 464
design problems, 454
DII (Dynamic Invocation Interface), 491-493, 541
implementation
client, 497-500
interface, 493-497
server, 497
object references, 509-511
operations
primitive, 506-509
sequence, 504-506
string, 506-509
struct, 501-504
distributed middleware, iBus, 13
distributed security issues
constantly changing implementions, 264
invocation chaining, 264
overview, 263
server location, 263
Distributed Transaction Process (DTP), 238
implicit/explicit propagation, 239
transaction context, 239
Transaction Manager, 239
domains
description, 262
ORBs, 9
Dynamic Invocation Interface (DII), 541
E
e-commerce example of transactional programming, 250-251
OnlineBookStore interface, 251-253
OnlineBookStore transaction client, 253-254
EJB (Enterprise JavaBeans) model, 516
embedded SQL for transactions, 244-245
Enterprise JavaBeans model (EJB), 516
enumerated IDL types, 141-142
environments, uses of CORBA, 166
ESIOP (Environment-Specific Inter-ORB Protocol), 10
Event Channel, delegated event model, 214
semantics, 215-216
event handlers, 396
client-side, 396-398
compared to Interceptors, 400
installing, 400
registering, 398-400
server-side, 400, 403-404
Event Service, 184
consumer-driven event model, 214
CORBA events-based application
push event client implementation, 222-224
typed push models, 225-233
untyped push models, 217-221
event types, 216
typed, 217
typed, example, 225-233
untyped, 216
untyped, example, 217-224
reducing interface dependencies, 212
supplier-driven event model, 213
events
client-side, 396
JavaBean, 520-524
server-side, 396
exporting services, Trader Service, 198
F
F switch (idl command), 303
Factory component, 62-63
Factory interface implementation, SOAR system, 61-62
FactoryServer
interface implementation, SOAR system, 63-66
listings
interface, 65-66
thread, 63-64
Filter class, 297
methods, 297-298
FilterClient interface implementation, listings
BindInterceptor, 421
ServerInterceptor, 417
FilterObject interface implementation
listing, 412
SOAR system, 62-63
filters (Orbix), 296. See also interceptors
monitor points, 296-297
per-object implementation, 296, 303-305
client, 306-307
server, 305-306
per-process filters
defined, 296
implementing, 298-301
installing, 301-302
methods, 297-298
FilterServer interface implementation, listings
BindInterceptor, 420
ServerInterceptor, 417
firewalls, overview, 268
flags, ior.idl, 15
flat transactions, 256
Flight interface implementation
listing, 225
server-side, 46-47
SOAR system, 40
FlightConsumer implementation, 223-224
funds transfer transaction scenario, 237
G
Gang of Four (GoF), 454
General Inter-ORB Protocol (GIOP), 9-10
Generic Security Services Application Program Interface (GSS
API), 262, 270
getReferenceCall() function, 509-511
getSequenceCall() function, 504-506
getStructCall() function, 503-504
getStubClass() method, 385
GIOP (General Inter-ORB Protocol), 9-10
global handlers, 399
H
half bridges, ESIOP (Environment-Specific Inter-ORB Protocol), 10
Hangar deactivation class, POA application, 112-114
Hangar interface, 188-189
implementations, 108-110
client, 193-195
interface, 189-190
server, 190-193
POA application, 107
Hangar Server GUI, POA application, 115-117
HangarClient, POA application, 118-119
HangarServer interface, POA application implementation, 110-111
hashcode() method, JGL Hashset, 17
header file defining IDL interfaces, 243
heuristics, memory management, 179
Holder classes, 156-157
hosting GUI objects
InternalFrames, using, 73-74
HTTP tunneling, 268
I
iBus, 13
client listeners, 23
IORR transmitter, 19-21
IORReceiver class, 24
IDL
arrays, memory management, 176
exceptions, 162-164
interfaces, 138
internal construction, 139-140
IORR, 14
compiling, 15-19
mapping, Java, reserved names, 136
modules, 137-138
operations, 155-156
classes, Holder, 156-157
memory management, 166-167
with types, 157-162
pre-compiler files generated, 15
typedefs and constants, 154-155
idl command, -F switch, 303
IDL memory management, 169-170
sequences, 172-173
in sequence, 174
inout sequence, 174
out sequence, 174
strings
client-side, 172
CORBA, String var type, 170
stringmap operation, 171
IDL pre-compilers
BOA (Basic Object Adapter) approach, 544-546
per-object filters, supporting, 303
seat as structure representing class, 43
TIE approach, 546-547
IDL types
array, 146-154
basic with IDL operations, 157
constructed, 140-141
DII, 501-504
with IDL operations, 157-162
enumerated, 141-142
mapping to Java, 136
primitive, DII, 506-509
sequence, DII, 504-509
template
sequence, 145-154
string, 143-145
union, 142-143
IIOP, 10
compliant stubs, skeletons, and support files, Java2iiop compiler,
368-370
proxy servers and firewalls, 268
implicit propagation, 239
in array (IDL), memory management, 176
in parameters, CORBA memory ownership principle, 167
in sequence (IDL), mem- ory management, 174
in strings, memory management, 171
incrementing reference counts, object references
comparing IONA’s T ptr and T var, 129-130
on the client, 129
on the server, 128
on the server and client, 129
indexed JavaBean properties, 519
information model, IIOP, 10
infrastructural components, 12
inout parameter, 18
CORBA memory ownership principle, 169
inout sequence (IDL), memory management, 174
inout strings, memory management, 171
inReplyFailure() method, 298
inReplyPostMarshal() method, 298
inReplyPreMarshal() method, 298
inRequestPostMarshal() method, 297
inRequestPreMarshal() method, 297
threading strategy, 315
installing
event handlers, client-side, 400
per-object filters
client objects, 306-307
server objects, 305-306
per-process filters, 301
client processes, 302
server processes, 301
ServerInterceptor
FilterClient implementation, 417
FilterServer implementation, 417
InterceptorInstaller implementation, 416
ServerFilterFactory implementation, 415
interceptor APIs, 409
BindInterceptor class, 411-412
BindInterceptor, constructing, 418-421
ClientInterceptor class, 410
ServerInterceptor class, 409-410
ServerInterceptor
constructing, 412-414
installing, 415-417
InterceptorInstaller interface implementation, 416
interceptors. See also filters
compared to SmartStubs, 382-383
event handlers, client-side, 400
Interface Repository. See IR (Interface Repository)
interface representation paradigm, 490
InterfaceDef interface, 480-481
InternalFrames, hosting GUI objects, 73-74
interoperability, 8
GIOP (General Inter-ORB Protocol), 9
OMG’s reasons for, 8
problems with, 12
invocation chaining, security issues, 264
Invoice Client interface, 96-97
Invoice GUI, SOAR system, 96-97
IORR (IOR Registry), 12-13
IDL, 14
compiling, 15-19
server implementations, 21-23
client side, 23
IORReceiver class, 24-28
transmitter, 19-21
IORReceiver class
IORR server implementations, 24-28
OrbixIORTuner class, 30
IORs (Interoperable Object References), 11-12
IORTransmitter class, 21
IORTransmitter object, server implementations, 23
IORTuner class, IORR server implementations, 26
IP addresses, IORTransmitter class, 21
IP Multicast
iBus, IORR transmitter, 19
IORR server implementations, 27
IR (Interface Repository), 474-480
interfaces
InterfaceDef, 480-481
ModuleDef, 480
OperationDef, 481-482
using, 483-488
VisiBroker Repository, 482-483
IRObject object model, 477-480
J
Java
classes
basicJavaType, 136
type, 136
comparing to CORBA, 36
iBus, 13
implementation
loaders, 330
pam module, 278-279
pam::nactivation, 280-281
shared activation mode, 284-285
unshared client, 290
unshared server, 288
inout parameter, 18
mapping IDL types to Java, 136-137
array, 146-154
basic, 157
constructed, 140-141, 157-162
enumerated, 141-142
reserved names, 136
sequence, 145-154
string, 143-145
typedefs and constants, 154-155
union, 142-143
OTS (Object Transaction Service), 240
property files, IORR transmitter, 19
security, 269
servlets, 431
APIs, 430-431
CORBA clients, 432-439
developing, 429-430
presentation layers, 428-429
reasons for usage with CORBA, 427
Java AWT, CardLayout, SearchClient GUI (SOAR), 76, 80-81
Java2idl compiler, 368
Java2iiop
applications, 370-373
clients, 377-379
compiler, 368-370
servers, 373-377
JavaBean
components
model, 514-518
property editors and sheets, 530-531
events, 520-524
properties
bound, 519-520
constrained, 520
indexed, 519
simple, 519
JGL HashSet
Offer class, 203
Registry entries, 17
Repository class, 204
K-L
Kerberos (security system), 262, 270
launching applications
options for, 72
SOAR, 74
LifeCycle Service, 184
listeners, IORR client-side implementation, 23
listings
Agent Server implementation, 445-446
Aircraft Server, 449
asynchronous server implementation, 358
BeanInfo class implementation, 529-530
BindInterceptor initialization class, 419
BOA Factory Component, 545-546
BrewEvent Class, 521
BrewListener interface, 521
callback
client interface, 361-362
client user interface, 363-364
server implementation, 359
server user interface, 360-361
chatParty
client/server interface, 350-354
interface, 349
chatServer
interface, 354-355
runtime implementation, 355-356
CloseConnection interface implementation, 459
CoffeeMachine implementation, 518
CoffeeMaker Implementation with Events, 522-523
CoffeeMaker Property Editor, 531
command interface, 456-457
command pattern
client, 462
implementation, 457
server, 461
concreteFactory interface implementation, 467
CORBABeanClient implementation, 527-528
CORBABeanServer implementation, 527
CUTrader
client using, 209-210
interface, 200-201
interface implementation, 201-203
service, using, 207-208
DataOperations interface implementation, 460-461
embedded SQL for transactions, 244-245
event handlers
client-side, 397-400
client-side, registering, 398-399
server-side, 404
Factory component, 62-63
FactoryClient interface implementation, 469-470
FactoryServer
interface, 65-66
interface implementation, 468
thread, 63-64
FilterClient interface
implementation, 418
implementation for BindInterceptor, 421
implementation for ServerInterceptor, 417
FilterObject interface implementation, 412
FilterServer interface implementation, 403-404
BindInterceptor, 420
ServerInterceptor, 417
Flight
interface implementation, 225
server, 46-47
FlightConsumer implementation, 223-224
Hangar
client implementation, 193-195
interface implementation, 190
Server GUI (POA), 115-117
server implementation, 191-192
HangarClient (POA), 118-119
HangarImpl interface implementation, 108-110
HangarServer interface implementation, 110-111
header file defining IDL interfaces, 243
InterceptorInstaller interface implementation, 416
Invoice Client interface, 96-97
IORReceiver class, 24
IORRegistry transmitter, 19
IR (Interface Repository)
Dynamic.idl, 475-476
Reader Client, 483-486
Java implementation
pam module, 278-279
pam::nactivation, 280-281
Java2iiop
client, 377
component, 371-372
server 373-377
JavaBean user interface, 524-526
LittleBank
interface implementation, 246
transaction client, 249-250
LittleBankServer interface implementation, 247
loaders
C++ implementation, 329
Java implementation, 330
memory management
arrays, client- and server-side, 177
sequences, client- and server-side, 175-176
strings, server-side, 171
Object Header file, 130-131
object module
client-side, 133-134
implementation, 131-132
server implementation, 133
OnlineBookStore
interface, 251-253
transaction client, 253-254
OpenConnection interface implementation, 458
OperateOnDatabase interface implementation, 458
Orbix C++ implementation of pam module, 276-278
OrbixIORTuner class, 28
PaymentClient interface, 90-95
Payments component, 67-69
PaymentServer
interface, 71-72
thread, 69-70
Proxy implementations
client, 465-466
interface, 386-387, 463-464
Proxy Smart Client, 391-392
Proxy SmartStub, 388-390
server, 464
push event client implementation, 222
Registry interface, 15
Repository class, 204
reservation requirements, SOAR system, 40-43
ReservationsClient interface, 82-89
ReservationServer interface, 49
Search component, 52-54
SearchClient interface, 76-80
SearchServer interface, 58-60
server hosts, 22
server object, 401, 403
ServerFilter interface implementation, 413-414
ServerFilterFactory interface implementation, 415
servlets
CORBA implementation, 432-437
Hello World, 430
shared activation mode
C++ server implementation, 284
Java server implementation, 284-285
SmartStub Server, 387-392
SOAR Client
applet, 98-100
HTML page, 100-101
SOARServerWindow, 73-74
Terminator class (POA), 112-114
thread pools
request client implementation, 322-324
Request interface, 320
request server implementation, 321
thread-per-client
client implementation, 316-318
header file, 312-313
implementation file, 313-315
server implementation, 315
TIE Factory Component, 546
TimeTableServer thread, 57-58
TransactionalObject, constructing, 242
typed event client, 231-233
typed supplier implementation, 228-229
unshared client mode
C++ implementation, 289
Java implementation, 290
unshared server mode
C++ implementation, 287
Java implementation, 288
untyped event supplier implementation, 219-221
LittleBank embedded SQL, 244-245
LittleBank interface
implementation, 246-247
OTS, 242-243
LittleBank transaction client, 249-250
load balancing, designing interceptors, 408
M
makeNumberCall() function, 506-509
makeReferenceCall() function, 509-511
makeSequenceCall() function, 504-506
makeStringCall() function, 506-509
makeStructCall() function, 501-503
mapping
IDL types to Java, 136-137
array, 146-154
basic, 157
constructed, 140-141, 157-162
enumerated, 141-142
Java reserved names, 136
sequence, 145-154
string, 143, 145
typedefs and constants, 154-155
union, 142-143
object references to C++ classes, 124
marshaling, 296. See filters
memory management
C++
client-side for IDL strings, 172
heuristics, 179
IDL arrays, 176
IDL operations, 166-167
IDL sequences, 172-174
IDL strings, 169-171
overview, 166
parameter types, 178
object references, 125
principles, 167-169
message-oriented middleware (MOM), 256
messages, GIOP (General Inter-ORB Protocol), 9
methods
attachPost(), 306
attachPre(), 306
bind, 187
bind_context, 187
bind_new_context, 187
continueProcess(), OrbixIORTuner class, 31
getStubClass(), 385
inReplyFailure(), 298
inReplyPostMarshal(), 298
inReplyPreMarshal(), 298
inRequestPostMarshal(), 297
inRequestPreMarshal(), 297
narrow, 188, 195
outReplyFailure(), 298
outReplyPostMarshal(), 297
outReplyPreMarshal(), 297
outRequestPostMarshal(), 297
outRequestPreMarshal(), 297
principal(), 300
rebind, 188
rebind_context, 188
resetStub(), 385
resolve, 195
resolve_initial_references, 192
retrieveAndDeleteAircraft, 190
setStubClass(), 384
unbind, 188
MFC/Win32 Console Orbix Wizard, 552-554
mobile agents, 442-443
Voyager, 443-450
Model View Controller pattern, 373
modeling systems on state transitions, 212
ModuleDef interface, 480
mutual authentication, 261
N
NameComponent structure, 185-186
names. See also Naming Service
binding, 187-188
contexts
creating, 186
deleting, 188
naming hierarchy, 186
deleting, 188
NameComponent structure, 185-186
reserved, Java IDL mapping, 136
resolving, 188
Naming Service, 184. See also names
advantages, 185
comparing to Trader Service, 198
Hangar implementation
client, 193-195
interface, 188-190
server, 190-193
NameComponent structure, 185-186
NamingContexts
binding names, 187-188
creating, 186
deleting, 188
deleting names, 188
naming hierarchies, 186
resolving names, 188
narrow method, 188, 195
nested transactions, 256
networked file systems, IIOR, 13
nonrepudiation, 296
O
Object Header file, 130-131
Object Management Group. See OMG
object modules, implementations, 130-132
client, 133-134
server, 133
object references
as parameters, 126
passing from client to server, 126
passing inout, 127
passing server to client, 127
DII, 509, 511
mapping to C++ classes, 124
memory management for, 125
overview, 124
reference counts, 127
comparing IONA’s T__ptr and T__var, 129-130
incrementing on the server and client, 128-129
object string_to_object(in string str) function, 540
Object Transaction Service. See OTS
object-oriented analysis and design (OOA/D), SOAR system, 38
object-oriented approaches, systems with state transitions, 212
objectFilter class, 304
objects
location, GIOP (General Inter-ORB Protocol), 9
per-object filters, 303
client implemetation, 306-307
defined, 296
defining, 303-305
server implemetation, 305-306
reservations, constructing, 48
Offer class, 203
OMG (Object Management Group), 490
interoperability, 8
OnlineBookStore
interface (OTS), 251, 253
transaction client (OTS), 253-254
OperationDef interface, 481-482
ORB class, IORs, 11
ORB integration, CORBASEC interfaces, 267
HTTP tunneling, 268
IIOP servers and firewalls, 268
Java security, 269
Orbix
C++ implementation of pam module, 276-278
filters, 296
monitor points, 296-297
per-object filters, 296, 303-307
per-process filters, 296-302
thread-per-client
client implementation, 316-318
header file, 312-313
implementation file, 313-315
server implementation, 315
Orbix/MFC projects
implementations
client-side, 551-554
server-side, 550-551
starting, 550
OrbixIORTuner class implementation, 28-32
OrbixWeb implementations
applications, asynchronous callbacks, 358
Callback client, 361-362
Callback client user interface, 363-364
Callback server interface, 358-359
Callback server user interface, 360-361
pam module, 278-279
pam::nactivation, 280-281
ORBs, 9
CORBA 1.0, 8
GIOP (General Inter-ORB Protocol), 9
IIOP, 10
server implementations, 23
OTS (Object Transaction Service)
banking example, 241
LittleBank embedded SQL, 244-245
LittleBank interface, 242
LittleBank interface header, 243
LittleBank interface implementation, 246
LittleBank transaction client, 249-250
LittleBankServer interface implementation, 247
clients
banking example, 241
Current interface, 240
transactional C++ and Java, 240
Distributed Transaction Process (DTP), 238
implicit/explicit propagation, 239
transaction context, 239
Transaction Manager, 239
e-commerce example, 250-251
OnlineBookStore interface, 251-253
OnlineBookStore transaction client, 253-254
message-oriented middleware, 256
nested transactions, 256
overview, 236
transactions
commit and rollback operations, 236
definition, 236
funds transfer example, 237
two-phase commit protocol, 238
Transaction Processing Monitors, 256
out parameters, CORBA memory ownership principle, 167
out sequence (IDL), memory management, 174
out strings, memory management, 171
outReplyFailure() method, 298
outReplyPostMarshal() method, 297
outReplyPreMarshal() method, 297
outRequestPostMarshal() method, 297
outRequestPreMarshal() method, 297
ownership of memory, CORBA memory management principles,
167-169
P-Q
pam module
code for, 276
Java implementation, 278-279
Orbix C++ implementation, 276-278
pam::nactivation, Java implementation, 280-281
parameter passing, memory management
C++ parameter types, 178
client-side for IDL strings, 172
CORBA memory ownership principle, 167-169
heuristics, 179
IDL
arrays, 176
operations, 166-167
sequences, 172-174
strings, 169-171
parameters, object references as, 126
passing from client to server, 126
passing from server to client, 127
passing inout, 127
patterns, description, 454
Payment interface implementation, SOAR system, 67-69
Payment server, SOAR system, 66-67
PaymentClient GUI, SOAR system, 90-95
PaymentClient interface, 90-95
Payments component, 67-69
PaymentServer
interface implementation, SOAR system, 69-72
interface, 71-72
thread, 69-70
peer-to-peer technology, 9
per-client process shared activation
overview, 283
registering servers, 286
per-method activation, 291
per-object filters, 303
defined, 296, 303-305
implementations
client, 306-307
server, 305-306
per-process filters
defined, 296
implementing, 298-301
installation processes, 301
client, 302
server, 301
methods, 297-298
persistent activation, 292
pervasive security standards
Distributed Computing Environment (DEC) Security Services,
270
Kerberos, 270
POA (Portable Object Adapter)
acquiring and activating, 106-107
application, implementing, 107
Hangar deactivation class, 112-114
Hangar interface, 108-110
Hangar Server GUI, 115-117
HangarClient, 118-119
HangarServer interface, 110-111
architecture, 105
DAIS J2 examples, 105
overview, 104
rationale for, 104
root POA default policies, 106
steps for using, 106
power nine problem, 212
pre-compilers, IDL, files generated, 15
presentation layers, Java servlets, 428-429
primitive IDL types, DII, 506-509
principal, description, 260
principal() method, 300
privileges, description, 261
process activation modes (PAM), 274, 281. See also activation modes
processes, per-process filters
defined, 296
implementing, 298-301
installing, 301-302
methods, 297-298
monitor points, 296-297
ProcessFilter class, 300
programming by contract, 408
properties, JavaBean
bound, 519-520
constrained, 520
indexed, 519
simple, 519
Property Editor class, 531
property editors and sheets, 530-531
property files, IORR transmitter, 19
proto-e-commerce, 37
protocols
ESIOP (Environment-Specific Inter-ORB Protocol), 10
GIOP (General Inter-ORB Protocol), 9-10
IIOP, 10
proxy pattern
overview, 463
implementations
proxy client, 465-466
proxy interface, 463-464
proxy server, 464
pseudo object IDL types, 136
pulling events, PullSupplier interface, 214
PullSupplier interface, 214
push event client implementation, untyped push model,
CORBAEvents-based applications, 222-224
push supplier, untyped push model, CORBAEvents-based
applications, 217-221
PushConsumer interface, 213
pushing events, PushConsumer interface, 213
R
rebind method, 188
rebind_context method, 188
reference counts
object references, 127
comparing IONA’s T__ptr and T__var, 129-130
incrementing on the client, 129
incrementing on the server, 128
incrementing on the server and client, 129
references, IORs, 11
Register interface example, 205
register() method. IORR, 17
registering
event handlers, client-side, 398, 400
servers, 285
shared per-client activation, 286
Registry
IOR, 13
IORR, 14
OrbixIORTuner class implementation, 32
reliability, GIOP (General Inter-ORB Protocol), 10
RemoveEntry() method, TimeTable interface implementation, 55
Repository class, 204
Reservation interface implementation, SOAR system, 43-44
reserveSeat() method, 44
reservation requirements, SOAR system, 39-43
ReservationEvent interface implementation, SOAR system, 44-45
ReservationListener interface implementation, SOAR system, 51
reservations object, 48
ReservationsClient GUI, SOAR system, 82-89
ReservationsClient interface, 82-89
ReservationServer interface implementation, SOAR system, 46-50
reserved names, Java IDL mapping, 136
resetStub() method, 385
resolve method, 195
resolve_initial_references method, 192
resolving names, 188
retrieveAndDeleteAircraft method, 190
S
scalability
activation modes, 274
shared activation servers, 310
Trader Service, 199
Schedule class, TimeTable interface implementation, 52-54
Search component, 52-54
search engine, implementing, SOAR system, 52
SearchClient interface, 76-80
SearchClientGUI, SOAR system, 76-81
SearchServer interface implementation, SOAR system, 58-60
Secure Inter-ORB Protocol (SECIOP), 266
Secure Socket Layer (SSL), 265
secure transport
overview, 265
Secure Inter-ORB Protocol (SECIOP), 266
Secure Socket Layer (SSL), 265
security issues. See distributed security issues; pervasive security
standards
Security Service, 185
Security Specification. See CORBASEC
security systems, types, 262
semantics
delegated event models, 215-216
semantic representation paradigm, 490-491
semaphores, 311
sequence (template) IDL types, 145-154
DII, 504-506
sequence memory management
client-side, 175-176
server-side, 175
server interceptors, 409
server location, security issues, 263
Server Manager, 285
server object, calling, 246
server-side events, 396
handlers, 400, 403-404
server-side memory management
arrays, 177
sequences, 175
strings, 171
in strings, 171
inout strings, 171
listing, 171
out strings, 171
ServerFilter interface implementation, 413-414
ServerFilterFactory interface implementation, 415
ServerInterceptor class, 409-410
ServerInterceptor, constructing, implementations, 412
FilterObject, 412
ServerFilter, 413-414
ServerInterceptor, installing, implementations
FilterClient, 417
FilterServer, 417
InterceptorInstaller, 416
ServerFilterFactory, 415
servers
activation modes
per-method , 291
persistent, 292
degree of state, 274
description, 274
filters, installing
per-object, 305-306
per-process, 301
shared , 282-286
Hangar server implementation, 190-193
hosting transitional objects, 248
IORR implementations, 21, 23
client side, 23
IORReceiver class, 24-28
Java2iiop, 373-377
registering, 285
shared per-client activation, 286
shared versus shared per-client, 285
unshared activation client, implementations
C++ , 289
Java, 290
unshared activation mode, implementations, 287
C++ , 287
Java, 288
services, 184
Event Service, 184
LifeCycle Service, 184
Naming Service, 184
advantages, 185
binding names, 187-188
deleting names, 188
Hangar client implementation, 193-195
Hangar interface implementation, 188-190
Hangar server implementation, 190-193
NameComponent structure, 185-186
NamingContexts, 186
resolving names, 188
nonrepudiation, 296
Security Service, 185
Trading Service, 184
Transaction Service, 185
servlets (Java), 431
APIs, 430-431
CORBA clients, 432-439
developing, 429-430
presentation layers, 428-429
reasons for usage with CORBA, 427
SESAME (security system), 262, 270
session (security), 262
setStubClass() method, 384
shared activation modes
overview, 282
per-client process shared activation, 283
registering, 286
per-client shared activation, 283
server implementations
C++, 284
consequences, 310
Java, 284-285
registering, 286
simple JavaBean properties, 519
SimpleBeanInfo methods, 528-529
SmartStubs, 382
compared to interceptors, 382-383
developing, 385-386
implementations, 388
clients, 388-393
servers, 386-388
internals, 384-385
uses, 383-384
SOAR system
analysis and design
constraints, 38
object-oriented analysis and design (OOA/D), 38
client, interacting with servers, 75
Factory interface implementation, 61-62
Factory::Object interface implementation, 62-63
FactoryServer interface implementation, 63-66
Flight interface implementation, 40
Invoice GUI, 96-97
Payment
interface implementation, 67-69
server, 66-67
PaymentClient GUI, 90-95
PaymentServer interface implementation, 69-72
prototype for wall-to-wall solutions, 38
Reservation interface implementation, 43-44
reserveSeat() method, 44
reservation requirements, 39-43
ReservationEvent interface implementation, 44-45
ReservationListener interface implementation, 51
ReservationsClient GUI, 82-89
ReservationServer interface implementation, 46-50
search engine, implementing, 52
SearchClient GUI, 76-81
SearchServer interface implementation, 58-60
SOAR.infrastructure.transmitter package, IORR transmitter, 19
SOAR Client applet
executing in browser, 101
running, 100-101
SOAR Client GUI, 98-100
SOAR Client HTML page, 100-101
SOARServerWindow, 73-74
subsolutions, 39
TimeTable interface implementation, 52-54
RemoveEntry() method, 55
TimeTableEvent interface implementation, 55-56
TimeTableServer interface implementation, 57-60
stateless servers, cookies, 37
Status create_environment(out Environment new_env) function,
541
Status create_list(in long count,out NVList new_list) function, 541
Status create_operation_list(in OperationDef oper, out NVList
new_list) function, 541
Status get_default_context(out Context ctx) function, 541
Status get_response() function, 542
Status invoke() function, 542
Status send_deferred() function, 542
Status send_oneway() function, 542
string (template) IDL types, 143-145
DII, 506-509
string memory management on server-side, 171
string object_to_string(in Object obj) function, 540
stringified object references, 11
stringmap operation, memory management, 171
in string, 171
inout string, 171
out string, 171
structures, NameComponent, 185-186
stubs, SmartStubs, 382
compared to Interceptors, 382-383
developing, 385-386
implementations, 388
clients, 388-393
servers, 386-388
internals, 384-385
uses, 383-384
subactivation modes, 275
subsolutions, SOAR system, 39
supplier-driven event model:Event Service, 213
system exceptions, 162-164
T
takestruct() function, 501-503
TCP/IP (Transmission Control Protocol/Internet Protocol), IIOP,
10
template IDL types, 136
memory management, 167
sequence, 145-154
string, 143, 145
Terminator class (POA), 112-114
threads
classes, OrbixIORTuner, 30-31
implementations, 311
thread pools, 319
thread pools request client, 322-324
thread pools Request interface, 320
thread pools request server, 321
thread-per-client, 312-313
thread-per-client client, 316-318
thread-per-client file, 313-315
thread-per-client server, 315
managing, 310
strategies
thread pools, 311
thread-per-object, 311
thread-per-session, 310
using, 274
TIE approach, 546-547
TimeTable interface implementation, SOAR system, 52-54
RemoveEntry() method, 55
TimeTableEvent interface implementation, SOAR system, 55-56
TimeTableServer interface implementation, SOAR system, , 57-60
trade-offs in engineering and design, 454
Trader Service
components, 199
implementations, examples
client using CUTrader service, 209-210
CUTrader exporter, 206
CUTrader importer, 208-210
CUTrader interface, 200-204
CUTrader server, 206
CUTrader service, 207-208
CUTrader, using, 206
Register interface, 205
state, 200
importing and exporting, 198
overview, 198
scalability, 199
Trading Service, 184
transactions
commit and rollback operations, 236
context, 239
definition, 236
Distributed Transaction Process (DTP), 238
implicit/explicit propagation, 239
transaction context, 239
Transaction Manager, 239
flat versus nested, 256
funds transfer example, 237
two-phase commit protocol, 238
Transaction Manager, 239
Transaction Processing Monitors, 256
Transaction Service, 185
transactional programming, 236. See also OTS
banking example, 241
TransactionalObject, 242
transport protocols, GIOP (General Inter-ORB Protocol), 10
two-phase commit protocol, transactions, 238
typed events
client, 231-233
Event Service, 217
interface, typed push model, 225-226
typed push models
Event Service, 225-233
typed push consumer, 231-233
typed push supplier, 227-230
Typed Push Server, 227
typedefs, 154-155
U
unbind method, 188
union IDL types, 142-143
unshared activation client, implementations
C++, 289
Java, 290
unshared activation mode, implementations
C++, 287
Java, 288
overview, 287
untyped event supplier implementation, 219-221
untyped events, Event Service, 216
untyped push models, Event Service, 217-224
URL naming, 368
user-defined exceptions, 163-164
V
VisiBroker thread pools, 319
implementations
request client, 322-324
Request interface, 320
request server, 321
VisiBroker interceptors
benefits, 408-409
description, 408
interceptor APIs, 409
BindInterceptor class, 411
BindInterceptor, constructing, 418-421
ClientInterceptor class, 410
ServerInterceptor class, 409-410
ServerInterceptor, constructing, 412-414
ServerInterceptor, installing, 415-417
types, 409
VisiBroker Repository, 482-483
VisiBroker URL naming, 48
chatServer runtime implementation, 355
Visigenic servers, IORR object, 21
Voyager, 443-450
W-Z
wall-to-wall solutions
description, 37
SOAR as prototype, 38
Web sites
amazon.com, 37
cbooks.com, 37
wizards, MFC/Win32 Console Orbix, 552-554
worker threads, 310
Table of Contents
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.