الاثنين، 25 يوليو 2011

Component Object Model

Component Object Model (COM) is a binary-interface standard for software componentry introduced by Microsoft in 1993. It is used to enable interprocess communication and dynamic object creation in a large range of programming languages. The term COM is often used in the Microsoft software development industry as an umbrella term that encompasses the OLE, OLE Automation, ActiveX, COM+ and DCOM technologies.

The essence of COM is a language-neutral way of implementing objects that can be used in environments different from the one in which they were created, even across machine boundaries. For well-authored components, COM allows reuse of objects with no knowledge of their internal implementation, as it forces component implementers to provide well-defined interfaces that are separate from the implementation. The different allocation semantics of languages are accommodated by making objects responsible for their own creation and destruction through reference-counting. Casting between different interfaces of an object is achieved through the QueryInterface() function. The preferred method of inheritance within COM is the creation of sub-objects to which method calls are delegated.
COM is an interface technology defined and implemented as standard only on Microsoft Windows and Apple's Core Foundation 1.3 and later plug-in API,[1] that in any case implement only a subset of the whole COM interface.[2] For some applications, COM has been replaced at least to some extent by the Microsoft .NET framework, and support for Web Services through the Windows Communication Foundation (WCF). However, COM objects can be used with all .NET languages through .NET COM Interop.
Networked DCOM uses binary proprietary formats, while WCF encourages the use of XML-based SOAP messaging. COM is very similar to other component software interface technologies, such as CORBA and Java Beans, although each has its own strengths and weaknesses.

COM programmers build their software using COM-aware components. Different component types are identified by class IDs (CLSIDs), which are Globally Unique Identifiers (GUIDs). Each COM component exposes its functionality through one or more interfaces. The different interfaces supported by a component are distinguished from each other using interface IDs (IIDs), which are GUIDs too.
COM interfaces have bindings in several languages, such as C, C++, Visual Basic, Delphi, and several of the scripting languages implemented on the Windows platform. All access to components is done through the methods of the interfaces. This allows techniques such as inter-process, or even inter-computer programming (the latter using the support of DCOM).
[edit]Interfaces
All COM components must (at the very least) implement the standard IUnknown interface, and thus all COM interfaces are derived from IUnknown. The IUnknown interface consists of three methods: AddRef() and Release(), which implement reference counting and controls the lifetime of interfaces; and QueryInterface(), which by specifying an IID allows a caller to retrieve references to the different interfaces the component implements. The effect of QueryInterface() is similar to dynamic_cast<> in C++ or casts in Java and C#.
A COM component's interfaces are required to exhibit the reflexive, symmetric, and transitive properties. The reflexive property refers to the ability for the QueryInterface() call on a given interface with the interface's ID to return the same instance of the interface. The symmetric property requires that when interface B is retrieved from interface A via QueryInterface(), interface A is retrievable from interface B as well. The transitive property requires that if interface B is obtainable from interface A and interface C is obtainable from interface B, then interface C should be retrievable from interface A.
An interface consists of a pointer to a virtual function table that contains a list of pointers to the functions that implement the functions declared in the interface, in the same order that they are declared in the interface. This technique of passing structures of function pointers is very similar to the one used by OLE 1.0 to communicate with its system libraries.
COM specifies many other standard interfaces used to allow inter-component communication. For example, one such interface is IStream, which is exposed by components that have data stream semantics (e.g. a FileStream component used to read or write files). It has the expected Read and Write methods to perform stream reads and writes. Another standard interface is IOleObject, which is exposed by components that expect to be linked or embedded into a container. IOleObject contains methods that allow callers to determine the size of the component's bounding rectangle, whether the component supports operations like 'Open', 'Save' and so on.

A class is COM's language-independent way of defining a class in the object-oriented sense.
A class can be a group of similar objects or a class is simply a representation of a type of object; it should be thought of as a blueprint that describes the object.
A coclass supplies concrete implementation(s) of one or more interfaces. In COM, such concrete implementations can be written in any programming language that supports COM component development, e.g. Delphi, C++, Visual Basic, etc.
One of COM's major contributions to the world of Windows development is the awareness of the concept of separation of interface from implementation. An extension of this fundamental concept is the notion of one interface, multiple implementations. This means that at runtime, an application can choose to instantiate an interface from one of many different concrete implementations.

Type libraries contain metadata that represent COM types. However, these types must first be described using Microsoft Interface Definition Language.
This is the common practice in the development of a COM component, i.e. to start with the definition of types using IDL. An IDL file is what COM provides that allows developers to define object-oriented classes, interfaces, structures, enumerations and other user-defined types in a language independent manner. COM IDL is similar in appearance to C/C++ declarations with the addition of keywords such as "interface" and "library" for defining interfaces and collections of classes, respectively. IDL also requires the use of bracketed attributes before declarations to provide additional information, such as the GUIDs of interfaces and the relationships between pointer parameters and length fields.
The IDL file is compiled by the MIDL compiler into a pair of forms for consumption from various languages. For C/C++, the MIDL compiler generates a compiler-independent header file containing struct definitions to match the vtbls of the declared interfaces and a C file containing declarations of the interface GUIDs. C++ source code for a proxy module can also be generated by the MIDL compiler. This proxy contains method stubs for converting COM calls into Remote Procedure Calls, thus enabling DCOM.
An IDL file may also be compiled by the MIDL compiler into a type library (.TLB file). The binary metadata contained within the type library is meant to be processed by language compilers and runtime environments (e.g. VB, Delphi, the .NET CLR etc.). The end result of such TLB processing is that language-specific constructs are produced that represent the COM class defined in the .TLB (and ultimately that which was defined in the originating IDL file).
[edit]COM as an object framework
The fundamental principles of COM have their roots in Object-Oriented philosophies. It is a platform for the realization of Object-Oriented Development and Deployment.
Because COM is a runtime framework, types have to be individually identifiable and specifiable at runtime. To achieve this, globally unique identifiers (GUIDs) are used. Each COM type is designated its own GUID for identification at runtime (versus compile time).
In order for information on COM types to be accessible at both compile time and runtime, COM uses type libraries. It is through the effective use of type libraries that COM achieves its capabilities as a dynamic framework for the interaction of objects.
Consider the following example coclass definition in an IDL :
coclass CSomeObject
{
[default] interface ISomeInterface;
[default, source] dispinterface _IMyObjectEvents;
};
The above code fragment declares a COM class named CSomeObject which must implement an interface named ISomeInterface and which supports (not implements) the event interface _IMyObjectEvents.
Ignoring the event interface bit, this is conceptually equivalent to defining a C++ class like this:
class CSomeObject : public ISomeInterface
{
...
...
...
};
where ISomeInterface is a C++ pure virtual class.
Referring once again to the MyObject COM class: once a coclass definition for it has been formalized in an IDL, and a Type Library compiled from it, the onus is on the individual language compiler to read and appropriately interpret this Type Library and then produce whatever code (in the specific compiler's language) necessary for a developer to implement and ultimately produce the binary executable code which can be deemed by COM to be of coclass MyObject.
Once an implementation of a COM coclass is built and is available in the system, next comes the question of how to instantiate it. In languages like C++, we can use the CoCreateInstance() API in which we specify the CLSID (CLSID_CSomeObject) of the coclass as well as the interface (specified by the IID IID_ISomeInterface) from that coclass that we want to use to interact with that coclass. Calling CoCreateInstance() like this:
CoCreateInstance(CLSID_CSomeObject,
NULL,
CLSCTX_INPROC_SERVER,
IID_ISomeInterface,
(void**)&pISomeInterface);
is conceptually equivalent to the following C++ code:
ISomeInterface* pISomeInterface = new CSomeObject();
In the second case, the COM sub-system is used to obtain a pointer to an object that implements the ISomeInterface interface and coclass CLSID_CSomeObject's particular implementation of this interface is required. In the first case, an instance of a C++ class CSomeObject that implements the interface ISomeInterface is created.
A coclass, then, is an object-oriented class in the COM world. The main feature of the coclass is that it is (1) binary in nature and consequently (2) programming language-independent.
[edit]Registry
Main article: Windows Registry
In Windows, COM classes, interfaces and type libraries are listed by GUIDs in the registry, under HKEY_CLASSES_ROOT\CLSID for classes and HKEY_CLASSES_ROOT\Interface for interfaces. The COM libraries use the registry to locate either the correct local libraries for each COM object or the network location for a remote service.
Under the key HKCR\clsid, the following are specified:
-> Inprocserver32 = object is to be
loaded into a process +
Path to file/object and readable name
HKCR\interface:
example: ISTREAM, IRPCSTUB, IMESSAGEFILTER
connects to a CLSID. You can specify
NUMMETHODS and PROXYSTUB(if web-object)
HKCR\typelib
One or more CLSID can be grouped into type library.
it contains parameters for linking in COM.
The rest of the info in the COM parts of the
REGISTRY, is to give an application/object
a CLSID.
[edit]Reference counting
The most fundamental COM interface of all, IUnknown (from which all COM interfaces must be derived), supports two main concepts: feature exploration through the QueryInterface method, and object lifetime management by including AddRef() and Release(). Reference counts and feature exploration apply to objects (not to each interface on an object) and thus must have a centralized implementation.
The COM specifications require a technique called reference counting to ensure that individual objects remain alive as long as there are clients which have acquired access to one or more of its interfaces and, conversely, that the same object is properly disposed of when all code that used the object have finished with it and no longer require it. A COM object is responsible for freeing its own memory once its reference count drops to zero.
For its implementation, a COM Object usually maintains an integer value that is used for reference counting. When AddRef() is called via any of object's interfaces, this integer value is incremented. When Release() is called, this integer is decremented. AddRef() and Release() are the only means by which a client of a COM object is able to influence its lifetime. The internal integer value remains a private member of the COM object and will never be directly accessible.
The purpose of AddRef() is to indicate to the COM object that an additional reference to itself has been affected and hence it is necessary to remain alive as long as this reference is still valid. Conversely, the purpose of Release() is to indicate to the COM object that a client (or a part of the client's code) has no further need for it and hence if this reference count has dropped to zero, it may be time to destroy itself.
Certain languages (e.g. Visual Basic) provide automatic reference counting so that COM object developers need not explicitly maintain any internal reference counter in their source codes. Using COM in C, explicit reference counting is needed. In C++, a coder may write the reference counting code or use a smart pointer that will manage all the reference counting.
The following is a general guideline calling AddRef() and Release() to facilitate proper reference counting in COM object:
Functions (whether object methods or global functions) that return interface references (via return value or via "out" parameter) should increment the reference count of the underlying object before returning. Hence internally within the function or method, AddRef() is called on the interface reference (to be returned). An example of this is the QueryInterface() method of the IUnknown interface. Hence it is imperative that developers be aware that the returned interface reference has already been reference count incremented and not call AddRef() on the returned interface reference yet another time.
Release() must be called on an interface reference before that interface's pointer is overwritten or goes out of scope.
If a copy is made on an interface reference pointer, AddRef() should be called on that pointer. After all, in this case, we are actually creating another reference on the underlying object.
AddRef() and Release() must be called on the specific interface which is being referenced since an object may implement per-interface reference counts in order to allocate internal resources only for the interfaces which are being referenced.
Extra calls to these functions are not sent out to remote objects over the wire; a proxy keeps only one reference on the remote object and maintains its own local reference count.
To facilitate and promote COM development, Microsoft introduced ATL (Active Template Library) for C++ developers. ATL provides for a higher-level COM development paradigm. It also shields COM client application developers from the need to directly maintain reference counting, by providing smart pointer objects.
Other libraries and languages that are COM-aware include the Microsoft Foundation Classes, the VC Compiler COM Support, VBScript, Visual Basic, ECMAScript (JavaScript) and Borland Delphi.
[edit]Instantiation
COM standardizes the instantiation (i.e. creation) process of COM objects by requiring the use of Class Factories. In order for a COM object to be created, two associated items must exist:
A Class ID.
A Class Factory.
Each COM Class or CoClass must be associated with a unique Class ID (a GUID). It must also be associated with its own Class Factory (that is achieved by using a centralized registry). A Class Factory is itself a COM object. It is an object that must expose the IClassFactory or IClassFactory2 (the latter with licensing support) interface. The responsibility of such an object is to create other objects.
A class factory object is usually contained within the same executable code (i.e. the server code) as the COM object itself. When a class factory is called upon to create a target object, this target object's class id must be provided. This is how the class factory knows which class of object to instantiate.
A single class factory object may create objects of more than one class. That is, two objects of different class ids may be created by the same class factory object. However, this is transparent to the COM system.
By delegating the responsibility of object creation into a separate object, a greater level of abstraction is promoted, and the developer is given greater flexibility. For example, implementation of the Singleton and other creation patterns is facilitated. Also, the calling application is shielded from the COM object's memory allocation semantics by the factory object.
In order for client applications to be able to acquire class factory objects, COM servers must properly expose them. A class factory is exposed differently, depending on the nature of the server code. A server which is DLL-based must export a DllGetClassObject() global function. A server which is EXE-based registers the class factory at runtime via the CoRegisterClassObject() Windows API function.
The following is a general outline of the sequence of object creation via its class factory:
The object's class factory is obtained via the CoGetClassObject() API (a standard Windows API).
As part of the call to CoGetClassObject(), the Class ID of the object (to be created) must be supplied. The following C++ code demonstrates this:
IClassFactory* pIClassFactory = NULL;

CoGetClassObject(CLSID_SomeObject,
CLSCTX_ALL,
NULL,
IID_IClassFactory,
(LPVOID*)&pIClassFactory);
The above code indicates that the Class Factory object of a COM object, which is identified by the class id CLSID_SomeObject, is required. This class factory object is returned by way of its IClassFactory interface.
The returned class factory object is then requested to create an instance of the originally intended COM object. The following C++ code demonstrates this:
ISomeObject* pISomeObject = NULL;

if (pIClassFactory)
{
pIClassFactory->CreateInstance (NULL,
IID_ISomeObject,
(LPVOID*)&pISomeObject);

pIClassFactory->Release();

pIClassFactory = NULL;
}
The above code indicates the use of the Class Factory object's CreateInstance() method to create an object which exposes an interface identified by the IID_ISomeObject GUID. A pointer to the ISomeObject interface of this object is returned. Also note that because the class factory object is itself a COM object, it needs to be released when it is no longer required (i.e. its Release() method must be called).
The above demonstrates, at the most basic level, the use of a class factory to instantiate an object. Higher level constructs are also available, some of which do not even involve direct use of the Windows APIs.
For example, the CoCreateInstance() API can be used by an application to directly create a COM object without acquiring the object's class factory. However, internally, the CoCreateInstance() API itself will invoke the CoGetClassObject() API to obtain the object's class factory and then use the class factory's CreateInstance() method to create the COM object.
VBScript supplies the New keyword as well as the CreateObject() global function for object instantiation. These language constructs encapsulate the acquisition of the class factory object of the target object (via the CoGetClassObject() API) followed by the invocation of the IClassFactory::CreateInstance() method.
Other languages, e.g. PowerBuilder's PowerScript may also provide their own high-level object creation constructs. However, CoGetClassObject() and the IClassFactory interface remain the most fundamental object creation technique.

At the time of the inception of COM technologies, the only way for a client to find out what features an object would offer was to actually create one instance and call into its QueryInterface method (part of the required IUnknown interface). This way of exploration became awkward for many applications, including the selection of appropriate components for a certain task, and tools to help a developer understand how to use methods provided by an object.
As a result, COM Type Libraries were introduced, through which components can describe themselves. A type library contains information such as the CLSID of a component, the IIDs of the interfaces the component implements, and descriptions of each of the methods of those interfaces. Type libraries are typically used by Rapid Application Development (RAD) environments such as Visual Basic or Visual Studio to assist developers of client applications.

COM is a binary standard (also said to be language agnostic) and may be developed in any programming language capable of understanding and implementing its binary defined data types and interfaces.
Runtime libraries (in extreme situations, the programmers) are responsible for entering and leaving the COM environment, instantiating and reference counting COM objects, querying objects for version information, coding to take advantage of advanced object versions, and coding graceful degradation of function when newer versions are not available.

COM objects may be instantiated and referenced from within a process, across process boundaries within a computer, and across a network, using the DCOM technology. Out-of-process and remote objects may use marshalling to send method calls and return values back and forth. The marshalling is invisible to the object and the code using the object.

In COM, threading issues are addressed by a concept known as "apartment models". Here the term "apartment" refers to an execution context wherein a single thread or a group of threads is associated with one or more COM objects.
Apartments stipulate the following general guidelines for participating threads and objects:
Each COM object is associated with one and only one apartment. This is decided at the time the object is created at runtime. After this initial setup, the object remains in that apartment throughout its lifetime.
A COM thread (i.e., a thread in which COM objects are created or COM method calls are made) is also associated with an apartment. Like COM objects, the apartment with which a thread is associated is also decided at initialization time. Each COM thread also remains in its designated apartment until it terminates.
Threads and objects which belong to the same apartment are said to follow the same thread access rules. Method calls which are made inside the same apartment are performed directly without any assistance from COM.
Threads and objects from different apartments are said to play by different thread access rules. Method calls made across apartments are achieved via marshalling. This requires the use of proxies and stubs.
There are three types of Apartment Models in the COM world: Single-Threaded Apartment (STA), Multi-Threaded Apartment (MTA), and Neutral Apartment. Each apartment represents one mechanism whereby an object's internal state may be synchronized across multiple threads.
The Single-Threaded Apartment (STA) model is a very commonly used model. Here, a COM object stands in a position similar to a desktop application's user interface. In an STA model, a single thread is dedicated to drive an object's methods, i.e. a single thread is always used to execute the methods of the object. In such an arrangement, method calls from threads outside of the apartment are marshalled and automatically queued by the system (via a standard Windows message queue). Thus, there is no worry about race conditions or lack of synchronicity because each method call of an object is always executed to completion before another is invoked.
If the COM object's methods perform their own synchronization, multiple threads dedicated to calling methods on the COM object are permitted. This is termed the Multiple Threaded Apartment (MTA). Calls to an MTA object from a thread in an STA are also marshaled. A process can consist of multiple COM objects, some of which may use STA and others of which may use MTA. The Thread Neutral Apartment allows different threads, none of which is necessarily dedicated to calling methods on the object, to make such calls. The only provision is that all methods on the object must be serially reentrant.


Since COM has a fairly complex implementation, programmers can be distracted by some of the "plumbing" issues.

When an STA is initialized it creates a hidden window that is used for inter-apartment and inter-process message routing. This window must have its message queue regularly pumped. This construct is known as a message pump. On earlier versions of Windows, failure to do so could cause system-wide deadlocks. This problem is complicated by some Windows APIs that initialize COM as part of their implementation, which causes a leak of implementation details.

Reference counting within COM may cause problems if two or more objects are circularly referenced. The design of an application must take this into account so that objects are not left orphaned.
Objects may also be left with active reference counts if the COM "event sink" model is used. Since the object that fires the event needs a reference to the object reacting to the event, the object's reference count will never reach zero.
Reference cycles are typically broken using either out-of-band termination or split identities. In the out of band termination technique, an object exposes a method which, when called, forces it to drop its references to other objects, thereby breaking the cycle. In the split identity technique, a single implementation exposes two separate COM objects (also known as identities). This creates a weak reference between the COM objects, preventing a reference cycle.

Because COM components are usually implemented in DLL files and registration allows only single version of a DLL they are subject of "DLL hell" effect. Registration-free COM capability eliminates the problem.


RegFree COM (or Registration-Free COM) is a technology introduced with Windows XP that allows Component Object Model (COM) components to store activation metadata and CLSID (Class ID) for the component without using the registry. Instead, the metadata and CLSIDs of the classes implemented in the component are declared in an assembly manifest (described using XML), stored either as a resource in the executable or as a separate file installed with the component. This allows multiple versions of the same component to be installed in different directories, described by their own manifests, as well as XCOPY deployment.[8] This technique has limited support for EXE COM server and cannot be used for system-wide components such as MDAC, MSXML, DirectX or Internet Explorer.
During application loading, the Windows loader searches for the manifest.[10] If it is present, the loader adds information from it to the activation context When the COM class factory tries to instantiate a class, the activation context is first checked to see if an implementation for the CLSID can be found. Only if the lookup fails is the registry scanned

Overview of CORBA


Distributed Applications

CORBA products provide a framework for the development and execution of distributed applications. But why would one want to develop a distributed application in the first place? As you will see later, distribution introduces a whole new set of difficult issues. However, sometimes there is no choice; some applications by their very nature are distributed across multiple computers because of one or more of the following reasons:
  • The data used by the application are distributed
  • The computation is distributed
  • The users of the application are distributed

Data are Distributed

Some applications must execute on multiple computers because the data that the application must access exist on multiple computers for administrative and ownership reasons. The owner may permit the data to be accessed remotely but not stored locally. Or perhaps the data cannot be co-located and must exist on multiple heterogeneous systems for historical reasons.

Computation is Distributed

Some applications execute on multiple computers in order to take advantage of multiple processors computing in parallel to solve some problem. Other applications may execute on multiple computers in order to take advantage of some unique feature of a particular system. Distributed applications can take advantage of the scalability and heterogeneity of the distributed system.

Users are Distributed

Some applications execute on multiple computers because users of the application communicate and interact with each other via the application. Each user executes a piece of the distributed application on his or her computer, and shared objects, typically execute on one or more servers. A typical architecture for this kind of application is illustrated below.
Prior to designing a distributed application, it is essential to understand some of the fundamental realities of the distributed system on which it will execute.

Fundamental Realities of Distributed Systems

Distributed application developers must address a number of issues that can be taken for granted in a local program where all logic executes in the same operating system process. The following table summarizes some of the basic differences between objects that are co-located in the same process, and objects that interact across process or machine boundaries.
Co-locatedDistributed
CommunicationFastSlow
FailuresObjects fail togetherObjects fail separately
Network can partition
Concurrent accessOnly with multiple threadsYes
SecureYesNo
The communication between objects in the same process is orders of magnitude faster than communication between objects on different machines. The implication of this is that you should avoid designing distributed applications in which two or more distributed objects have very tight interactions. If they do have tight interactions, they should be co-located.
When two objects are co-located, they fail together; if the process in which they execute fails, both objects fail. The designer of the objects need not be concerned with the behavior of the application if one of the objects is available and the other one is not. But if two objects are distributed across process boundaries, the objects can fail independently. In this case, the designer of the objects must be concerned with each of the object's behavior in the event the other object has failed. Similarly, in a distributed system the network can partition and both objects can execute independently assuming the other has failed.
The default mode for most local programs is to operate with a single thread of control. Single threaded programming is easy. Objects are accessed in a well-defined sequential order according to the program's algorithms, and you need not be concerned with concurrent access.
If you decide to introduce multiple threads of control within a local program, you must consider the possible orderings of access to objects and use synchronization mechanisms to control concurrent access to shared objects. But at least you have a choice of introducing multiple threads of control. In a distributed application, there are necessarily multiple threads of control. Each distributed object is operating in a different thread of control. A distributed object may have multiple concurrent clients. As the developer of the object and the developer of the clients, you must consider this concurrent access to objects and use the necessary synchronization mechanisms.
When two objects are co-located in the same process, you need not be concerned about security. When the objects are on different machines, you need to use security mechanisms to authenticate the identity of the other object.

Distributed Object Systems

Distributed object systems are distributed systems in which all entities are modeled as objects. Distributed object systems are a popular paradigm for object-oriented distributed applications. Since the application is modeled as a set of cooperating objects, it maps very naturally to the services of the distributed system.
In spite of the natural mapping from object-oriented modeling to distributed object systems, do not forget the realities of distributed systems described above. Process boundaries really do matter and they will impact your design.
That said, the next section of this course discusses the CORBA standard for distributed object systems.

What is CORBA?

CORBA, or Common Object Request Broker Architecture, is a standard architecture for distributed object systems. It allows a distributed, heterogeneous collection of objects to interoperate.

The OMG

The Object Management Group (OMG) is responsible for defining CORBA. The OMG comprises over 700 companies and organizations, including almost all the major vendors and developers of distributed object technology, including platform, database, and application vendors as well as software tool and corporate developers.

CORBA Architecture

CORBA defines an architecture for distributed objects. The basic CORBA paradigm is that of a request for services of a distributed object. Everything else defined by the OMG is in terms of this basic paradigm.
The services that an object provides are given by its interface. Interfaces are defined in OMG's Interface Definition Language (IDL). Distributed objects are identified by object references, which are typed by IDL interfaces.
The figure below graphically depicts a request. A client holds an object reference to a distributed object. The object reference is typed by an interface. In the figure below the object reference is typed by the Rabbit interface. The Object Request Broker, or ORB, delivers the request to the object and returns any results to the client. In the figure, a jump request returns an object reference typed by the AnotherObject interface.

The ORB

The ORB is the distributed service that implements the request to the remote object. It locates the remote object on the network, communicates the request to the object, waits for the results and when available communicates those results back to the client.
The ORB implements location transparency. Exactly the same request mechanism is used by the client and the CORBA object regardless of where the object is located. It might be in the same process with the client, down the hall or across the planet. The client cannot tell the difference.
The ORB implements programming language independence for the request. The client issuing the request can be written in a different programming language from the implementation of the CORBA object. The ORB does the necessary translation between programming languages. Language bindings are defined for all popular programming languages.

CORBA as a Standard for Distributed Objects

One of the goals of the CORBA specification is that clients and object implementations are portable. The CORBA specification defines an application programmer's interface (API) for clients of a distributed object as well as an API for the implementation of a CORBA object. This means that code written for one vendor's CORBA product could, with a minimum of effort, be rewritten to work with a different vendor's product. However, the reality of CORBA products on the market today is that CORBA clients are portable but object implementations need some rework to port from one CORBA product to another.
CORBA 2.0 added interoperability as a goal in the specification. In particular, CORBA 2.0 defines a network protocol, called IIOP (Internet Inter-ORB Protocol), that allows clients using a CORBA product from any vendor to communicate with objects using a CORBA product from any other vendor. IIOP works across the Internet, or more precisely, across any TCP/IP implementation.


Interoperability is more important in a distributed system than portability. IIOP is used in other systems that do not even attempt to provide the CORBA API. In particular, IIOP is used as the transport protocol for a version of Java RMI (so called "RMI over IIOP"). Since EJB is defined in terms of RMI, it too can use IIOP. Various application servers available on the market use IIOP but do not expose the entire CORBA API. Because they all use IIOP, programs written to these different API's can interoperate with each other and with programs written to the CORBA API.

CORBA Services

Another important part of the CORBA standard is the definition of a set of distributed services to support the integration and interoperation of distributed objects. As depicted in the graphic below, the services, known as CORBA Services or COS, are defined on top of the ORB. That is, they are defined as standard CORBA objects with IDL interfaces, sometimes referred to as "Object Services."
There are several CORBA services. The popular ones are described in detail in another module of this course. Below is a brief description of each:
ServiceDescription
Object life cycleDefines how CORBA objects are created, removed, moved, and copied
NamingDefines how CORBA objects can have friendly symbolic names
EventsDecouples the communication between distributed objects
RelationshipsProvides arbitrary typed n-ary relationships between CORBA objects
ExternalizationCoordinates the transformation of CORBA objects to and from external media
TransactionsCoordinates atomic access to CORBA objects
Concurrency ControlProvides a locking service for CORBA objects in order to ensure serializable access
PropertySupports the association of name-value pairs with CORBA objects
TraderSupports the finding of CORBA objects based on properties describing the service offered by the object
QuerySupports queries on objects

CORBA Products

CORBA is a specification; it is a guide for implementing products. Several vendors provide CORBA products for various programming languages. The CORBA products that support the Java programming language include:
ORBDescription
The Java 2 ORBThe Java 2 ORB comes with Sun's Java 2 SDK. It is missing several features.
VisiBroker for JavaA popular Java ORB from Inprise Corporation. VisiBroker is also embedded in other products. For example, it is the ORB that is embedded in the Netscape Communicator browser.
OrbixWebA popular Java ORB from Iona Technologies.
WebSphereA popular application server with an ORB from IBM.
Netscape CommunicatorNetscape browsers have a version of VisiBroker embedded in them. Applets can issue request on CORBA objects without downloading ORB classes into the browser. They are already there.
Various free or shareware ORBsCORBA implementations for various languages are available for download on the web from various sources.
Providing detailed information about all of these products is beyond the scope of this introductory course. This course will just use examples from both Sun's Java 2 ORB and Inprise's VisiBroker 3.x for Java products.

The Stock Application

The stock trading application is a distributed application that illustrates the Java programming language and CORBA. In this introductory module only a small simple subset of the application is used. Feel free to expand upon the application to enhance it once you are more comfortable with CORBA.
The stock application allows multiple users to watch the activity of stocks. The user is presented with a list of available stocks identified by their stock symbols. The user can select a stock and then press the "view" button.
Selecting the "view" button results in a report about the stock, indicating the name of the company, the stock symbol, the current price, the last time it was updated, the trading volume, and a graph that shows the stock price over some interval. This report is automatically updated as new stock data becomes available.
The stock report also lets the user set an alarm by pressing the "Alarm" button. The alarm can be set to activate when the price of the stock falls below a certain price or when it exceeds a certain price.
When the price of the stock satisfies the alarm's condition, it activates and the user is notified.
Later the application could be extended to allow users to buy and sell stocks.

Some Objects in the Stock Application

From the above description, you can easily identify the following distributed objects in the application.
StockA distributed object that represents a particular stock.
StockPresentationA distributed object in the GUI that presents the stock data to the user for a particular stock.
AlarmA distributed object that represents the alarm set by the user.
AlarmPresentationA distributed object in the GUI that presents the alarm going off to the user.
The Stock object is now used to illustrate the CORBA distributed object model.

Implementing a CORBA Client

This section covers what you need to know to use CORBA objects from the Java programming language. It examines OMG IDL interfaces, the Java programming language binding for IDL interfaces, object references, and requests, how to obtain object references, and how, as a client, to create distributed objects. After reading this section and completing the exercises, you should be able to write a client using the Java programming language. Again, the stock example is used to illustrate the client's model of CORBA.

CORBA Objects are Described by IDL Interfaces

The OMG Interface Definition Language IDL supports the specification of object interfaces. An object interface indicates the operations the object supports, but not how they are implemented. That is, in IDL there is no way to declare object state and algorithms. The implementation of a CORBA object is provided in a standard programming language, such as the Java programming language or C++. An interface specifies the contract between code using the object and the code implementing the object. Clients only depend on the interface.
IDL interfaces are programming language neutral. IDL defines language bindings for many different programming languages. This allows an object implementor to choose the appropriate programming language for the object. Similarly, it allows the developer of the client to choose the appropriate and possibly different programming language for the client. Currently, the OMG has standardized on language bindings for the C, C++, Java, Ada, COBOL, Smalltalk, Objective C, and Lisp programming languages.
So by using OMG IDL, the following can be described without regards to any particular programming language:
  • Modularized object interfaces
  • Operations and attributes that an object supports
  • Exceptions raised by an operation
  • Data types of an operation return value, its parameters, and an object's attributes
The IDL data types are:
  • Basic data types (longshortstringfloat...)
  • Constructed data types (structunionenumsequence)
  • Typed object references
  • The any type, a dynamically typed value
Again, IDL says nothing about object implementations. Here's the IDL interface for the example stock objects:
module StockObjects {

  struct Quote {
    string symbol;
    long at_time;
    double price;
    long volume;
  };

  exception Unknown{};

  interface Stock {
    
    // Returns the current stock quote.
    Quote get_quote() raises(Unknown);

    // Sets the current stock quote.
    void set_quote(in Quote stock_quote);

    // Provides the stock description, 
    // e.g. company name.
    readonly attribute string description;
  };

  interface StockFactory {

    Stock create_stock(
      in string symbol,
      in string description
    );
  };
};

Note that the above example defines an IDL module named StockObjects, which contains the:
  • Data structure Quote
  • Exception Unknown
  • Interface Stock
  • Interface StockFactory
The module defines a scope for these names. Within the module, a data structure Quote and an exception Unknown are defined and then used in the Stock interface. The Stock interface is used in the definition of the StockFactory interface. Also note that the parameters to operations are tagged with the keywords inout, or inout. The in keyword indicates the data are passed from the client to the object. The out keyword indicates that the data are returned from the object to the client, and inout indicates that the data are passed from the client to the object and then returned to the client.
IDL declarations are compiled with an IDL compiler and converted to their associated representations in the target programming languages according to the standard language binding. (This course uses the Java language binding in all of the examples. Later you will see the Java binding in more depth.)

Object References and Requests

Clients issue a request on a CORBA object using an object reference. An object reference identifies the distributed object that will receive the request. Here's a Java programming language code fragment that obtains a Stock object reference and then it uses it to obtain the current price of the stock. Note that the code fragment does not directly use CORBA types; instead it uses the Java types that have been produced by the IDL to Java compiler.
Stock theStock = ...
try {
    Quote current_quote = 
              theStock.get_quote();
} catch (Throwable e) {
}

Object references can be passed around the distributed object system, i.e. as parameters to operations and returned as results of requests. For example, notice that the StockFactory interface defines a create() operation that returns an instance of a Stock. Here's a Java client code fragment that issues a request on the factory object and receives the resulting stock object reference.
StockFactory factory = ...
Stock theStock = ...
try {
   theStock = factory.create(
                "GII",
                "Global Industries Inc.");
} catch (Throwable e) {
}

Note that issuing a request on a CORBA object is not all that different from issuing a request on a Java object in a local program. The main difference is that the CORBA objects can be anywhere. The CORBA system provides location transparency, which implies that the client cannot tell if the request is to an object in the same process, on the same machine, down the hall, or across the planet.
Another difference from a local Java object is that the life time of the CORBA object is not tied to the process in which the client executes, nor to the process in which the CORBA object executes. Object references persist; they can be saved as a string and recreated from a string.
The following Java code converts the Stock object reference to a string:
String stockString = 
     orb.object_to_string(theStock);
The string can be stored or communicated outside of the distributed object system. Any client can convert the string back to an object reference and issue a request on the distributed object.
This Java code converts the string back to a Stock object reference:
org.omg.CORBA.Object obj = 
    orb.string_to_object(stockString);
Stock theStock = StockHelper.narrow(obj);
Note that the resulting type of the string_to_object() method is Object, not Stock. The second line narrows the type of the object reference from Object to Stock. IDL supports a hierarchy of interfaces; the narrow() method call is an operation on the hierarchy.

IDL Type System

IDL interfaces can be defined in terms of other IDL interfaces. You previously saw a Stock interface that represents the basic behavior of a stock object.
Consider another IDL module:
module ReportingObjects {

  exception EventChannelFailure{};

  interface Reporting {
    
    // Receive events in push mode
    CosEventComm::PushSupplier push_events(
        in CosEventComm::PushConsumer consumer)
      raises(EventChannelFailure);

    // Receive events in pull mode
    CosEventComm::PullSupplier pull_events(
        in CosEventComm::PullConsumer consumer)
      raises(EventChannelFailure);

  };

};

The Reporting interface supports the registration of interest in events. (Don't worry about the details of using the CORBA Event Service.)
Given the definition of the Stock interface and a Reporting interface, it is now possible to define a new ReportingStock interface in terms ofReporting and Stock.
interface ReportingStock: Reporting, Stock {
};
ReportingStock supports all of the operations and attributes defined by the Reporting interface as well as all of those defined by the Stockinterface. The ReportingStock interface inherits the Stock interface and the Reporting interface. Graphically this is represented as:
All CORBA interfaces implicitly inherit the Object interface. They all support the operations defined for Object. Inheritance of Object is implicit; there is no need to declare it.
Object references are typed by IDL interfaces. In a Java program you could type an object reference to be a ReportingStock.
ReportingStock theReportingStock;
Clients can pass this object reference to an operation expecting a supertype. For example assume there is an EventManager interface that has aregister operation that takes an object reference typed by the Reporting interface.
interface EventManager {
        :
    void register(in Reporting event_supplier);
        :
};

The following is a legal request because a ReportingStock is a Reporting.
EventManager manager = ...
ReportingStock theReportingStock = ...
manager->register(theReportingStock); 
// ok
However, the following is not a legal request because a Stock is not a Reporting:
EventManager manager = ...
Stock theStock = ...
manager->register(theStock); // type error
IDL Type Operations
Given that IDL interfaces can be arranged in a hierarchy, a small number of operations are defined on that hierarchy. The narrow() operation casts an object reference to a more specific type:
org.omg.CORBA.Object obj = ...
Stock theStock = StockHelper.narrow(obj);
The is_a() operation, determines if an object reference supports a particular interface:
if (obj._is_a(StockHelper.id()) ...
The id() operation defined on the helper class returns a repository id for the interface. The repository id is a string representing the interface. For the stock example, the repository id is:
IDL:StockObjects/Stock:1.0
Finally, it is possible to widen an object reference, that is cast it to a less specific interface:
Stock theStock = theReportingStock;
There are no special operations to widen an object reference. It is accomplished exactly as in the Java programming language.
Request Type Checking
The IDL compiler for Java programming language generates client-side stubs, which represent the CORBA object locally in the Java programming language. The generated code also represents in the Java programming language all of the IDL interfaces and data types used to issue requests. The client code thus depends on the generated Java code.
As you previously saw, passing an object reference typed by the Stock interface to the event manager would be illegal because the Stockinterface does not inherit the Reporting interface. The Java compiler, not the IDL compiler, would catch this error at compile time.

IDL to Java Binding

The Java binding for IDL maps the various IDL constructs to corresponding Java constructs. The following table shows how the IDL constructs are represented in the Java programming language. For comparison, the C++ binding is also shown.
IDLJavaC++
modulepackagenamespace
interfaceinterfaceabstract class
operationmethodmember function
attributepair of methodspair of functions
exceptionexceptionexception
Each of the IDL data types are represented in the Java programming language as follows:
IDL TypeJava Type
booleanboolean
char / wcharchar
octetbyte
short / unsigned shortshort
long / unsigned longint
long long / unsigned long longlong
floatfloat
doubledouble
string / wstringString
When discussing data type mapping, one term you run across frequently is marshaling. Marshaling is the conversion of a language-specific data structure into the CORBA IIOP streaming format. IIOP data can then be transmitted over a network to its destination, where it is then unmarshaled from IIOP back into a language-dependent data structure.

IDL to Java Compiler

CORBA products provide an IDL compiler that converts IDL into the Java programming language. The IDL compiler available for the Java 2 SDK is called idltojava. The IDL compiler that comes with VisiBroker for Java is called idl2java.
For the stock example, the command "idltojava Stock.idl" generates the files listed below. (The VisiBroker ORB generates the same files with the exception that the stub file is called _st_Stock.java, rather than _StockStub.java.)
Stock.javaThe IDL interface represented as a Java interface
StockHelper.javaImplements the type operations for the interface
StockHolder.javaUsed for out and inout parameters
_StockStub.javaImplements a local object representing the remote CORBA object. This object forwards all requests to the remote object. The client does not use this class directly.
The developer compiles the IDL using the IDL compiler and then compiles the generated code using the Java compiler. The compiled code must be on the classpath of the running Java program.

Obtaining Object References

You may have noticed that there are three fundamental mechanisms in which a piece of code can obtain an object reference:
  • It can be can be passed to it as a parameter
  • It can be returned as the result of issuing a request
  • It can be obtained by converting a string into an object
  • reference
These fundamental mechanisms are supported by the ORB. Using these mechanisms, it is possible to define higher level services for locating objects in the distributed object system.

The Client's Model of Object Creation

You may decide to export the ability to create an object to the distributed object system. You can accomplish this by defining a factory for the object. Factories are simply distributed objects that create other distributed objects.
There is nothing special about a factory. It is just another distributed object: It has an IDL interface, it is implemented in some programming language, and clients issue standard CORBA requests on factory objects.
There is no standard interface for a factory. Recall in the StockObjects example, the factory interface is:
interface StockFactory {
  Stock create_stock(
    in string stock_symbol,
    in string stock_description);
};

To create a stock object, a client simply issues a request on the factory.
Another object implementor could define an object factory differently.

Exceptions

As you have seen in the stock example, CORBA has a concept of exceptions that is very similar to that of the Java programming language; naturally, CORBA exceptions are mapped to Java exceptions. When you issue a CORBA request, you must use the Java programming language'stry and catch keywords.
There are two types of CORBA exceptions, System Exceptions and User Exceptions. System Exceptions are thrown when something goes wrong with the system--for instance, if you request a method that doesn't exist on the server, if there's a communication problem, or if the ORB hasn't been initialized correctly. The Java class SystemException extends RuntimeException, so the compiler won't complain if you forget to catch them. You need to explicitly wrap your CORBA calls in try...catch blocks in order to recover gracefully from System Exceptions.
CORBA System Exceptions can contain "minor codes" which may provide additional information about what went wrong. Unfortunately, these are vendor-specific, so you need to tailor your error recovery routines to the ORB you're using.
User Exceptions are generated if something goes wrong inside the execution of the remote method itself. These are declared inside the IDL definition for the object, and are automatically generated by the idltojava compiler. In the stock example, Unknown is a user exception.
Since User Exceptions are subclasses of java.lang.Exception, the compiler will complain if you forget to trap them (and this is as it should be).

Object Implementations

NOTE: the previous section discussed the client's view of CORBA, that is, how a Java client issues a request on a CORBA object. The client's view is standard across most CORBA products. Basically, the standard worked and there are only minor differences. Unfortunately, the same is not the case for the implementation view of CORBA. As such, some of the details given here might not match a particular CORBA product. Notes on different CORBA products appear as appendices.
This section describes what you need to know to implement a simple CORBA object in the Java programming language. It examines the Java server-side language binding for IDL, implementing objects and servers, implementation packaging issues, and CORBA object adaptors. After completing this section, you should be able to write a simple CORBA object and server in the Java programming language. Again, the stock example is used to illustrate the implementation model of CORBA.
CORBA object implementations are completely invisible to their clients. A client can only depend on the IDL interface. In the Java programming language, or C++, this is not the case. The user of an object declares variables by a class name; doing so makes the code depend on much more than just the interface. The client depends on the object implementation programming language, the name of the class, the implementation class hierarchy, and, in C++, even the object layout.
The complete encapsulation for CORBA objects means the object implementor has much more freedom. Object implementations can be provided in a number of supported programming languages. This is not necessarily the same one the clients are written in. (Of course, here everything is in the Java programming language, but CORBA does notrequire this.)
The same interface can be implemented in multiple ways. There is no limit. In the stock example, the following are possible implementations of the Stock interface:
  • A stock implementation class written in the Java programming language that obtains values from a commercial feed
  • A stock implementation class written in C++ that accesses a database on the Internet
  • A stock implementation written in Smalltalk that guesses stock prices

Providing an Implementation

Recall that given an IDL file, the IDL compiler generates various files for a CORBA client. In addition to the files generated for a client, it also generates a skeleton class for the object implementation. A skeleton is the entry point into the distributed object. It unmarshals the incoming data, calls the method implementing the operation being requested, and returns the marshaled results. The object developer need only compile the skeleton and not be concerned with the insides of it. The object developer can focus on providing the implementation of the IDL interface.
To implement a CORBA object in the Java programming language, the developer simply implements a Java class that extends the generated skeleton class and provides a method for each operation in the interface. In the example, the IDL compiler generates the skeleton class_StockImplBase for the Stock interface. A possible implementation of the Stock interface is:
public class StockImpl extends 
      StockObjects._StockImplBase {

  private Quote _quote=null;
  private String _description=null;

  public StockImpl(
    String name, String description) {
    super();
    _description = description;
  }

  public Quote get_quote() throws Unknown {
    if (_quote==null) throw new Unknown();
    return _quote;
  }

  public void set_quote(Quote quote) {
    _quote = quote;
  }

  public String description() {
    return _description;
  }
}

Interface versus Implementation Hierarchies

Notice that there are two separate hierarchies: an interface hierarchy and an implementation hierarchy. Recall that the interface hierarchy for the example of a ReportingStock is:
In IDL this is represented as:
interface ReportingStock: Reporting, Stock {
};
Now suppose there is an implementation of a ReportingStock, named ReportingStockImpl, that inherits the IDL generated skeletons_ReportingStockImplBase, delegates some of its stock methods to StockImpl, and implements the Reporting operations directly. Graphically:
In the Java programming language, this class hierarchy is represented as:
class ReportingStockImpl 
           implements ReportingStock 
  extends _ReportingStockImplBase {
    ...
}

Since the Java programming language only supports single inheritance of implementation classes, implementations often create an instance of another class and delegate to it. In the above example, the ReportingStockImpl delegates to the StockImpl class for the implementation of some of its methods.
Other class hierarchies implementing the same interface hierarchy are possible. Furthermore, if you need to change the class hierarchy of the implementation in some way, the clients are not affected.

Implementation Type Checking

Just as type checking is done at the client for the request to a distributed object, type checking is also done for the object implementation.
The IDL compiler for the Java programming language generates object skeletons and Java code to represent all of the IDL interfaces and data types used in the interface definition. The implementation code thus depends on the generated Java code.
If there are any type errors in the object implementation, the Java compiler, not the IDL compiler, catches the errors at compile time. Thus, in the example, suppose the developer erroneously implemented the get_quote() operation to return a double instead of the structure that is declared in the IDL:
Quote StockImpl.get_quote() {
  double price = ...;
  return price;
}
The Java compiler would detect this error at compile time.

Implementing a Server Using the Java 2 ORB

You previously saw how to provide an implementation of a CORBA object in the Java programming language. The remaining task is to define a server that when run makes the services of its objects available to clients. A server that will run with the Java 2 ORB needs to do the following:
  • Define a main method
  • Initialize the ORB
  • Instantiate at least one object
  • Connect each object to the orb
  • Wait for requests
The server must instantiate at least one object since objects are the only way to offer services in CORBA systems.
Here's an implementation of the stock objects server. This code depends on the Java 2 ORB.
public class theServer {
  public static void main(String[] args) {
    try {
      // Initialize the ORB.
      org.omg.CORBA.ORB orb = 
        org.omg.CORBA.ORB.init(args,null);

      // Create a stock object.
      StockImpl theStock = 
        new StockImpl("GII",
               "Global Industries Inc.");
            
      // Let the ORB know about the object
      orb.connect(theStock);

      // Write stringified object 
      //reference to a file
      PrintWriter out = 
         new PrintWriter(new BufferedWriter(
                   new FileWriter(args[0])));
      out.println( 
            orb.object_to_string(theStock) );
      out.close();

      // wait for invocations from clients
      java.lang.Object sync = 
                     new java.lang.Object();
      synchronized (sync) {
        sync.wait();
      }
    } catch (Exception e) {
      System.err.println(
            "Stock server error: " + e);
      e.printStackTrace(System.out);
    }
  }
}

Notice that the server does a new on the StockImpl class implementing the Stock interface and then passes it to the ORB using the connect()call, indicating that the object is ready to accept requests. Finally, the server waits for requests.

Implementing a Server Using VisiBroker 3.x

You previously saw how to provide a server using the Java 2 ORB. If you are using Inprise's VisiBroker 3.x for Java ORB you need to do the following:
  • Define a main method
  • Initialize the ORB and the BOA (basic object adapter)
  • Instantiate at least one object
  • Let the BOA know that the object is ready to provide service
  • Let the BOA know that the server is ready
The server must instantiate at least one object since objects are the only way to offer services in CORBA systems.
Here's an implementation of the stock objects server. This code depends on VisiBroker 3.x:.
public class theServer {
  public static void main(String[] args) {
    try {
      // Initialize the ORB.
      org.omg.CORBA.ORB orb = 
        org.omg.CORBA.ORB.init(args,null);

      // Initialize the BOA.
      org.omg.CORBA.BOA boa = 
        ((com.visigenic.vbroker.orb.ORB)orb)
                                .BOA_init();

      // Create a stock object.
      StockImpl theStock = 
          new StockImpl("
             GII","Global Industries Inc.");
            
      // Write stringified object 
      //reference to a file
      PrintWriter out = 
          new PrintWriter(new BufferedWriter(
                     new FileWriter(args[0])));
      out.println( 
           orb.object_to_string(theStock) );
      out.close();

      // Tell the BOA that the object 
      //is ready to 
      // receive requests.
      boa.obj_is_ready(theStock);
    
      // Tell the boa that the 
      //server is ready.  This 
      // call blocks.
      boa.impl_is_ready();
    } catch (Exception e) {
      System.err.println("
        Stock server error: " + e);
      e.printStackTrace(System.out);
    }
  }
}

Notice that the server does a new on the StockImpl class implementing the Stock interface and then passes it to the BOA, indicating that the object is ready to accept requests. Finally, the server calls the BOA to indicate that it is ready. At this point, the implementation will be called when requests arrive.

Differences Between Server Implementations

The following summarizes the differences between implementing a transient CORBA server using the Java 2 ORB and implementing a transient server using Inprise's VisiBroker 3.x:
Java 2 ORBVisiBroker 3.x for Java
InitializationJust initialize the ORBInitialize both the ORB and the BOA
Object export orb.connect(theStock)boa.obj_is_ready(theStock)
Indicate server ready for requestsSuspend main thread doing a wait()boa.impl_is_ready()
These are the only differences for transient object servers. There are further API differences of CORBA products due to persistence and automatic activation of servers.

Packaging Object Implementations

As illustrated above, you should separate the implementations of your objects from the implementation of the server. This allows you to mix and match object implementations in a server. The object implementation does not depend on the server. The server, of course depends on the object implementations that it contains.
Another advantage of carefully isolating object implementation code from server code is portability. Most of the product-specific code exists in the server, not in the object implementation.
A good strategy is to package an object implementation with its generated stubs and skeletons as a JavaBean component. This allows the implementation to be manipulated by JavaBean design tools.

Object Adapters

The CORBA specification defines the concept of an object adapter. An object adapter is a framework for implementing CORBA objects. It provides an API that object implementations use for various low level services. According to the CORBA specification, an object adapter is responsible for the following functions:
  • Generation and interpretation of object references
  • Method invocation
  • Security of interactions
  • Object and implementation activation and deactivation
  • Mapping object references to the corresponding object implementations
  • Registration of implementations
The architecture supports the definition of many kinds of object adapters. The specification includes the definition of the basic object adapter(BOA). In the previous section, you saw some server code that uses the services of VisiBroker's implementation of the BOA. The BOA has been implemented in various CORBA products. Unfortunately, since the specification of the BOA was not complete, the various BOA implementations differ in some significant ways. This has compromised server portability.
To address this shortcoming, an entirely new object adapter was added, the portable object adapter (POA). Unfortunately, the POA is not yet supported in many products. In any event, the BOA and the POA are described here.

Activation on Demand by the Basic Object Adapter (BOA)

One of the main tasks of the BOA is to support on-demand object activation. When a client issues a request, the BOA determines if the object is currently running and if so, it delivers the request to the object. If the object is not running, the BOA activates the object and then delivers the request.
The BOA defines four different models for object activation:
Shared serverMultiple active objects share the same server. The server services requests from multiple clients. The server remains active until it is deactivated or exits.
Unshared serverOnly one object is active in the server. The server exits when the client that caused its activation exits.
Server-per-methodEach request results in the creation of a server. The server exits when the method completes.
Persistent serverThe server is started by an entity other than the BOA (you, operating services, etc.). Multiple active objects share the server.

Portable Object Adapter (POA)

According to the specification, "The intent of the POA, as its name suggests, is to provide an object adapter that can be used with multiple ORB implementations with a minimum of rewriting needed to deal with different vendors' implementations." However, most CORBA products do not yet support the POA.
The POA is also intended to allow persistent objects -- at least, from the client's perspective. That is, as far as the client is concerned, these objects are always alive, and maintain data values stored in them, even though physically, the server may have been restarted many times, or the implementation may be provided by many different object implementations.
The POA allows the object implementor a lot more control. Previously, the implementation of the object was responsible only for the code that is executed in response to method requests. Now, additionally, the implementor has more control over the object's identity, state, storage, and lifecycle.
The POA has support for many other features, including the following:
  • Transparent object activation
  • Multiple simultaneous object identities
  • Transient objects
  • Object ID namespaces
  • Policies including multithreading, security, and object management
  • Multiple distinct POAs in a single server with different policies and namespaces
For more detail on the POA, please see the specification.
A word on multithreading. Each POA has a threading policy that determines how that particular POA instance will deal with multiple simultaneous requests. In the single thread model, all requests are processed one at a time. The underlying object implementations can therefore be lazy and thread-unsafe. Of course, this can lead to performance problems. In the alternate ORB-controlled model, the ORB is responsible for creating and allocating threads and sending requests in to the object implementations efficiently. The programmer doesn't need to worry about thread management issues; however, the programmer definitely has to make sure the objects are all thread-safe.

Resources

Web Sites

Documentation and Specs

Books

  • Orfali & Harkey, Client/Server Programming with Java and CORBA (Wiley)
  • Orfali, Harkey & Edwards, Instant CORBA (Wiley)
  • Vogel, Andreas and Keith Duddy, Java Programming with CORBA (Wiley)
  • Perdrick et al, Programming with VisiBroker (Wiley).

Miscellaneous

If you don't have the idltojava compiler, you can find it at Sun's JavaIDL web site.
RMI over IIOP compiler is available at http://java.sun.com/products/rmi-iiop/.
OrbixWeb from Iona

About The Java 2 ORB

The Java IDL ORB that ships with the Java 2 platform allows applications to run either as stand-alone Java applications or as applets within Java-enabled browsers. It uses IIOP as its native protocol.
The Sun Java ORB is fairly generic. This is good, because there are few surprises; however, there are many advanced features of CORBA that are missing. There is no Interface Repository (though Java IDL clients can access an Interface Repository provided by another Java or C++ ORB), Transaction Service, or POA, for example. For a complete list of these unimplemented features, see the CORBA Package JavaDoc Comments -- scroll down to find the section near the bottom of the page describing these shortcomings.
Java IDL is structured with a "pluggable ORB" architecture, which allows you to instantiate ORBs from other vendors from within the Java Virtual Machine. This is accomplished through setting environment variables, or system properties, or at run time through the use of a Properties orString[] object. See the CORBA Package JavaDoc Comments for more details (scroll down past the list of classes to find the appropriate sections).

idltojava Notes

If you don't have the idltojava compiler, you can find it at the Java IDL web site.
By default, idltojava tries to run a C preprocessor on the IDL files before compiling them. Unfortunately, if you do not have a C preprocessor installed on your system, or if idltojava cannot find it, you will see cryptic error message:
Bad command or file name
Couldn't open temporary file
idltojava: fatal error: cannot preprocess input; 
No such file or directory
If you get this message, it means you must invoke idltojava with the -fno-cpp option, as follows:
idltojava -fno-cpp foo.idl

System Properties

The ORB.init() method can read in its configuration parameters from a number of different sources: from the application parameters (the first argument to ORB.init()), from an application-specific Properties object (the second argument to ORB.init()), or from the System Properties (defined on the command line by -D flags).
Quoted verbatim from the Java IDL guide:
Currently, the following configuration properties are defined for all ORB implementations:
org.omg.CORBA.ORBClass
The name of a Java class that implements the org.omg.CORBA.ORB interface. Applets and applications do not need to supply this property unless they must have a particular ORB implementation. The value for the Java IDL ORB is com.sun.CORBA.iiop.ORB.
org.omg.CORBA.ORBSingletonClass
The name of a Java class that implements the org.omg.CORBA.ORB interface. This is the object returned by a call to orb.init() with no arguments. It is used primarily to create typecode instances than can be shared across untrusted code (such as unsigned applets) in a secured environment. The value for the Java IDL ORB is com.sun.CORBA.iiop.ORB.
In addition to the standard properties listed above, Java IDL also supports the following properties:
org.omg.CORBA.ORBInitialHost
The host name of a machine running a server or daemon that provides initial bootstrap services, such as a name service. The default value for this property is localhost for applications. For applets it is the applet host, equivalent to getCodeBase().getHost().
org.omg.CORBA.ORBInitialPort
The port the initial naming service listens to. The default value is 900.

VisiBroker 3.x

Here are some additional details for the VisiBroker 3.x implementation of CORBA. See the product documentation for more details.

VisiBroker Tools

VisiBroker for Java ships with a number of tools. Some are replacements or wrappers for the standard Java compiler and interpreter. Others are specific to the VisiBroker product. The important ones are:
  • vbj is a wrapper for java which sets some properties and adds to your classpath
  • vbjc is a wrapper for javac which sets some properties and adds to your classpath
  • idl2java is an IDL compiler that can produce proprietary or portable stubs and skeletons
  • osagent launches the proprietary Smart Agent binding service

Using VisiBroker with Java 2

To make VisiBroker for Java 3.4 work with the Java 2 platform, a number of changes are necessary, involving both code and configuration.
The Java 2 platform ships with a standard implementation of CORBA classes in the org.omg.CORBA.* package. These classes are somewhat different from the CORBA classes included with VisiBroker. The VisiBroker classes have several nonstandard extensions to CORBA; some of these nonstandard extensions are required for successful operation. To access these functions, you must change your source code to cast the JavaIDL ORB to a VisiBroker ORB. For example:
org.omg.CORBA.ORB orb = 
      org.omg.CORBA.ORB.init(args, null); 
org.omg.CORBA.BOA boa = 
((com.visigenic.vbroker.orb.ORB)orb).BOA_init(
                                            );

This example applies to the server code.
For this cast to work, you must also guarantee that the ORB returned by the ORB.init() call is indeed a VisiBroker ORB, and not the standard JavaIDL ORB from Sun. For that, you need to define two Java system properties before you launch the JVM. These properties are automatically set if you use vbj instead of java to launch your programs. But if you use java, you must set these properties as follows (where each lists the property name first and value second):
  • org.omg.CORBA.ORBClass -
    com.visigenic.vbroker.orb.ORB
  • org.omg.CORBA.ORBSingletonClass -
    com.visigenic.vbroker.orb.ORB

Portable Stubs and Skeletons

By default, VisiBroker for Java creates stub and skeleton code that is interoperable but not portable. This makes sense in the VisiBroker world, since the non-portable code is more efficient and slightly smaller. However, if your code needs to run on several different ORBs, you can use the command
idl2java -portable -no_bind Foo.idl
and the stubs and skeletons will be portable.
There are a few reasons for this. One is if you're writing an applet that will run inside a remote web browser environment, such as Netscape Communicator or the Java Plug-in. The latter uses JavaIDL; the former may be running an older version of VisiBroker.
What's the difference between portable and proprietary versions? A portable stub uses DII (Dynamic Invocation Interface) to marshal the object request; a portable skeleton uses DSI (Dynamic Skeleton Interface). The proprietary versions make direct calls (to the ORB or the implementation), and hence do not have to go through the overhead of creating and parsing the various DII and DSI objects.
Note that your code doesn't need to change--this all happens behind the scenes with idl2java. The only difference in the portable code is that_FooImplBase extends org.omg.CORBA.DynamicImplementation instead of com.inprise.vbroker.CORBA.portable.Skeleton, and that the stub class is named _portable_stub_Foo.java instead of _st_Foo.java. (Note also that if you really want to, you can switch stubs on the fly by using FooHelper.setProxyClass(_portable_stub_Foo.class) -- though that would be kind of weird).

Using the BOA with VisiBroker

The VisiBroker BOA uses a slightly modified version of the standard BOA initialization sequence. For VisiBroker, follow the following boilerplate code.
// create and initialize the ORB
ORB orb = ORB.init(args, null);
The VisiBroker BOA is a customized, proprietary implementation of the CORBA.BOA interface. It has several methods that are not part of the standard interface. In order to use these proprietary methods, you must cast the ORB to a VisiBroker class, as follows.
// Initialize the BOA.
// Must cast to VBJ ORB for 
//Java 2 compatibility
org.omg.CORBA.BOA boa =
((com.inprise.vbroker.CORBA.ORB)orb).BOA_init();

VisiBroker objects are usually persistent. In VisiBroker terms, this means that they are initialized with a name. This is not needed with a portable, non-VisiBroker object implementation.
// create object and register it 
//with the ORB
Stock theStock = new StockImpl(name);
The VisiBroker BOA skips the boa.create() phase and jumps straight to obj_is_ready().
// Export the newly created object.
boa.obj_is_ready(theStock);
The impl_is_ready() method waits indefinitely.
// Wait for incoming requests
boa.impl_is_ready();

Using the VisiBroker Smart Agent

VisiBroker for Java ships with its own location service, called the smart agent. The smart agent is a distributed location service. It collaborates with other smart agents running on the network to locate a suitable implementation of an object. If there is more than one implementation available; the smart agent selects one. This provides a degree of fault tolerance and load balancing. If a machine goes down, the smart agent will automatically find another implementation on another machine to service the request. The client is unaware of this.
If you create a persistent object, by passing in a name when you call its constructor, then the BOA will automatically inform the smart agent. The name you passed in the constructor will become the name it is known by on the smart agent network.
On the client side, the proprietary Helper object defines a method bind() that fetches an object reference for you, bypassing the need to convert a string into an object reference. The bind() method is not part of the standard CORBA-Java mapping.
// "bind" (actually lookup) the 
//object reference
Stock theStock = 
   StockHelper.bind(orb, "GII");