The following are some of the ORB pseudo-objects:
CORBA::ORB | Provides many methods supporting distributed objects. |
CORBA::Object | Provides general methods that can be called for any object. Every objects inherits methods from CORBA::Object. |
CORBA::BOA | A pseudo-object that provides an interface to the Basic Object Adapter. |
CORBA::Request | Used to create dynamic invocation requests. |
CORBA::NVList | Used to create parameter lists for dynamic invocation requests. |
The following table describes the CORBA services:
Service | Purpose |
Life Cycle | Management of objects. |
Persistence | Storage for objects. |
Naming | White Pages for objects. |
Event | Object notification on occurrence of specific events. |
Concurrency | Locking on behalf of transactions or threads. |
Transaction | Support for transactions between objects. |
Relationship | Management of object relationships that can be created dynamically. |
Externalization | Streaming data in and out of components. |
Query | A subset of SQL providing query operations for objects. |
Licensing | Billing for the use of objects. |
Properties | Management of object properties that can be dynamically associated with objects. |
Time | Time synchronization and timeout services. |
Security | Authentication, access control lists, confidentiality for distributed objects. |
Trader | Yellow Pages for objects. |
Collection | Creation and manipulation of common collections for CORBA interfaces. |
Startup | Allows requests to automatically start up when an ORB is invoked. |
CORBA allows that other protocols may be used in addition to IIOP. Accordingly, they are called Environment-Specific Inter-ORB Protocols (ESIOPs). DCE ESIOP is one of such optional protocols that provide a bridge to the DCE components.
The following table provides their description:
Shared server | Objects run inside one server process as threads. |
Unshared server | Each object resides on a separate process server. |
Server-per-method | Each method is run within a separate server process, so several servers might be running for the same object. |
Persistent server | The server is started outside of the CORBA facilities, but otherwise it behaves like a shared server. |
The implementation of the server is not regulated by CORBA, but its relationship to BOA is standardized through the BOA API. Each BOA implementation should provide the functionality for object management, which includes management of an Implementation Repository. BOA has to provide the mechanism for invoking methods and passing them parameters using skeletons. In a case of an error in execution of a request, BOA.set_exception can be used to inform the caller through an exception.
Like its C++ counterpart, the IDL compiler uses a precompiler to preprocess the source. C++-like precompiler directives can be included in the specifications. For example, the #include directive can be used for including other specification files. The description of the interface may also include special directives called pragmas; e.g., for generating unique global interface identifiers. Such identifiers can be used to reference services that are available on any accessible ORB supporting IIOP.
IDL is not a programming language. In fact, it is independent of any programming language and completely declarative; i.e. the implementation details are left for the application programmers. IDL provides bindings to several programming languages, so the interfaces can be easily incorporated in applications written in these languages. IDL not only provides access to the components, but also to all services that are available through an ORB.
The IDL syntax provides for declaration of the following elements:
module <module_name>
{
<type declarations>;
<constant declarations>;
<exception declarations>;
interface <interface_name> [: <inheritance> ]
{
<type declarations>;
<constant declarations>;
<exception declarations>;
<operation_type> operation_name [ <parameters>
]
[ raises exception ] [ context ];
.
.
.
}
.
.
.
}
In the following example, we declare a module for several common household devices. Each of the devices has an owner, so they all inherit from the ownership interface. There are several abstract types of devices for entertainment, communication and Web browsing. The last two interfaces are for a TV and a telephone. In the declarations of both of them, we use multiple inheritance. A TV can be used for both entertainment and Web browsing. A telephone can also be used for Web browsing, but its primary function so far has been communication with other people. Both of them have an owner and they both can be sold. This behavior is inherited indirectly from the ownership interface.
module Devices
{
interface ownership
{
attribute string owner;
void sell(in string new_owner);
};
interface EntertainmentDevice : ownership
{
attribute string video_standard;
exception NotAllowed {string explanation};
void play(in string game_name)
raises (NotAllowed);
};
interface CommDevice : ownership
{
attribute string medium;
Exception NotResponding {string explanation};
void connect(in string receiver)
raises (NotResponding);
};
interface WebDevice : ownership
{
exception NotFound {string explanation};
void browse(in string url)
raises (NotFound);
};
interface CompDevice : ownership
{
exception TooBusy {string explanation};
void Run(in string program)
raises (TooBusy);
};
interface Computer
: EntertainmentDevice, WebDevice, CompDevice,
CommDevice
{
exception CompError {strig explanation};
void compute(in string program)
raises (CompError);
};
interface TV : EntertainmentDevice, WebDevice
{
exception NotOnAir {string explanation};
void watch(in int channel)
raises (NotOnAir);
};
interface Telephone : CommDevice, WebDevice
{
exception NotAtHome {string explanation};
void talk(in string person_name)
raises (NotAtHome);
};
}
An IDL compiler is used to generate a number of elements that will
be included in the client and server applications. We will look at them
when we study an example of a Java application that uses CORBA.
The structure of the repository reflects the IDL definitions through a hierarchy of objects, which are instances of classes that correspond to the elements of the IDL. The root of the repository is the Repository object. The ORB.get_interface method returns a reference to the repository.
CORBA ORB provides a number of methods to access and manage the repository. Most of them are provided through several abstract classes: IRObject, Container and Contained. Every class in the repository is a subclass of one or more of these subclasses, so the methods can be invoked polymorphically on any object. Some methods are specific only to the Repository and InterfaceDef objects.
The following table presents a number of methods for accessing Interface
Repositories. They can be used to generate invocation requests dynamically
or to verify correctness of remote calls. If the reference to an object
is known, then the ORB::Object.get_interface
method can be used to obtain a reference to the object’s interface (an
instance of InterfaceDef).
Repository.lookup_id | To find an object in the repository by its Repository ID. |
Container.lookup_name | To obtain an object reference by name |
Contained.describe | To obtain a structure with an IDL description of the object. |
Container.contents | To obtain a list of contained objects. It is useful to navigate through a hierarchy of objects. |
InterfaceDef.
describe_interface |
To retrieve a description of the interface to an object. |
InterfaceDef.is_a | To check whether the object is a subclass of the specified class (inherits interface). |
DCE:600bd300-1001-12de-bbe8:2
#pragma prefix DevicesCorp #pragma version Computer 1.2An arbitrary identifier can be assigned to an interface through the ID pragma. For example,
#pragma ID MyInterface "This_Better_Be_A_Valid_IDL_or_DCE_Id"
The static mechanism of method invocation is very similar to an RPC call. The client needs to know the reference to the object providing the method. CORBA requires that all ORB implementations provide the same object mapping for any language. Therefore, both client and server handle objects in the language specific way. Any required transformation of the reference is handled transparently by the involved ORBs. The vendors have to use a special Interoperable Object Reference (IOR) in inter-ORB communication.
A reference to an object can be obtained from directories or invocations to other objects. Usually a binding utility can be used by clients. An object reference can be translated into a string using the ORB.object_to_string method of the ORB pseudo-object. This string can be stored or communicated to others, who can transform it back to a reference by calling ORB.string_to_object.
The identifier, the method and the input and output parameters are handled by a local stub. The stub uses the ORB core to communicate with the BOA on the node running the remote server. The BOA ensures that the called object is active, and then passes the control to the skeleton. The remote skeleton calls appropriate method on the server object, which performs the requested operation and returns the results to the skeleton. Analogously to the stub, the skeleton uses the core of ORB to send back the results to the caller through the client stub. Owing to the IOR and IIOP, the client does not care what ORB the server object is working with.
We will analyze the details while presenting a Java example in later sections.
The client has to discover who is it that needs to be called. That may come from the CORBA Naming or Trading Services. Alternatively, another application can send a string representation of the object, which can be transformed back into a reference by ORB.string_to_object. The Naming Service is useful when more details about the required server are available, while the Trading Service relies on the clients needs to find appropriate server. When a reference to the object is available, then the Interface Repository can be asked for the reference to the objects interface by calling get_interface on this object. As we know, this method is inherited from the Object pseudo-object of which every object is a subclass. The returned value is a handle of the interface object (an instance of InterfaceDef). It can be used to obtain the description of the interface. For example, InterfaceDef.lookp_name can be used to get a hanlde of a particular method and InterfaceDef.describe tp obtain an IDL description of the method’s interface. With this information, we can create a Named Value List (NVList) object that is used to pass parameters. This can be done by calling ORB.create_list and a series of calls to NVList.add_item, or ORB.create_operation_list can be used instead. A request can be constructed dynamically by calling the create_request on the object reference. This method is inherited from the Object pseudo-object. It returns a handle of a new instance of class Request. A shorter construction process for requests without parameters can be applied by calling the _request method on the object, which is inherited as well. With a request constructed, the server can be called in one of three ways. The first uses the Request.invoke method to invoke the remote method and obtain results in one shot. The caller is suspended waiting for the results. The Request.send_deferred, Request.poll_response and Request.get_response methods can be used to obtain a deferred response by the means of polling the server. If the client just wants to activate certain service, but is not expecting any results, then the last invocation method, Request.send_oneway, can be used.
As we have already seen, the interactions with the server object are
controlled by the Object Adapter. It will ensure that the dynamically built
request is fulfilled by the appropriate object. If the object is not active,
then it will be activated as a process or a thread appropriately to the
server’s mode.
Warning: The textbook uses VisiBroker CORBA. There are slight variations in uses of CORBAs from different users. For example, here we do not call BOA.create() (it is done in the skeleton when the constructor for a server object calls super(nameOfObject), which is a requirement).
module Counter
{
interface Count
{ attribute long sum;
long increment();
};
};
idl2java compiler creates a number of Java classes grouped in the Counter package. Two files are for the client side, and three groups are for the server.
The following code has been created by extending Counter._example_Count.
// CountImpl.java: The Count Implementation
class CountImpl extends Counter._sk_Count implements Counter.Count
{
private int sum;
// Constructor
CountImpl(String name)
{ super(name);
System.out.println("Count Object Created");
sum = 0;
}
// get sum
public int sum() throws CORBA.SystemException
{ return sum;
}
// set sum
public void sum(int val) throws CORBA.SystemException
{ sum = val;
}
// increment method
public int increment() throws CORBA.SystemException
{ sum++;
return sum;
}
}
// CountServer.java: The Count Server main program
class CountServer
{ static public void main(String[] args)
{ try
{ // Initialize the ORB.
CORBA.ORB orb = CORBA.ORB.init();
// Initialize the BOA.
CORBA.BOA boa = orb.BOA_init();
// Create the Count object.
CountImpl count = new CountImpl("My
Count");
// Export to the ORB newly created
object.
boa.obj_is_ready(count);
// Ready to service requests.
boa.impl_is_ready();
}
catch(CORBA.SystemException e)
{ System.err.println(e);
}
}
}
// CountClient.java Static Client, VisiBroker for Java
class CountClient
{ public static void main(String args[])
{ try
{ // Initialize the ORB
System.out.println("Initializing
the ORB");
CORBA.ORB orb = CORBA.ORB.init();
// Bind to the Count Object
System.out.println("Binding to Count
Object");
Counter.Count counter = Counter.Count_var.bind("My
Count");
// Set sum to initial value of 0
System.out.println("Setting sum
to 0");
counter.sum((int)0);
// Calculate Start time
long startTime = System.currentTimeMillis();
// Increment 1000 times
System.out.println("Incrementing");
for (int i = 0 ; i < 1000 ; i++
)
{ counter.increment();
}
// Calculate stop time; print out
statistics
long stopTime = System.currentTimeMillis();
System.out.println("Avg Ping = "
+ ((stopTime - startTime)/1000f) + " msecs");
System.out.println("Sum = " + counter.sum());
} catch(CORBA.SystemException e)
{ System.err.println("System Exception");
System.err.println(e);
}
}
}
> start osagent
> start java CountServer
> java CountClient
// CountClientApplet.java Applet Client, VisiBroker for Java
// NOTE: This code works with pre-JDK 1.1 browsers
import java.awt.*;
public class CountClientApplet extends java.applet.Applet
{
private TextField countField, pingTimeField;
private Button runCount;
private Counter.Count counter;
public void init()
{
// Create a 2 by 2 grid of widgets.
setLayout(new GridLayout(2, 2, 10, 10));
// Add the four widgets, initialize where necessary
add(new Label("Count"));
add(countField = new TextField());
countField.setText("1000");
add(runCount = new Button("Run"));
add(pingTimeField = new TextField());
pingTimeField.setEditable(false);
try
{
// Initialize the ORB.
showStatus("Initializing the ORB");
CORBA.ORB orb = CORBA.ORB.init(this);
// Bind to the Count Object
showStatus("Binding to Count Object");
counter = Counter.Count_var.bind("My
Count");
}
catch(CORBA.SystemException e)
{
showStatus("Applet Exception" +
e);
}
}
public boolean action(Event ev, Object arg)
{
if(ev.target == runCount)
{
try
{
// Set Sum to initial
value of 0
showStatus("Setting
Sum to 0");
counter.sum((int)0);
// get data from and
set value of applet fields
showStatus("Incrementing");
int stopCount = Integer.parseInt(countField.getText());
pingTimeField.setText("
");
// Calculate Start time
long startTime = System.currentTimeMillis();
// Increment stopCount
times
for (int i = 0 ; i <
stopCount ; i++ )
{
counter.increment();
}
// Calculate stop time;
show statistics
long stopTime = System.currentTimeMillis();
pingTimeField.setText("Avg
Ping = "
+ Float.toString((float)(stopTime- startTime)/stopCount)
+ " msecs");
showStatus("Sum = "
+ counter.sum());
}
catch(CORBA.SystemException e)
{
showStatus("System Exception"
+ e);
}
return true;
}
return false;
}
}
The client and the server should be installed on the Web server. Netscape Navigator supports IIOP, so after the applet is downloaded, it can communicate with the remote object.
Some extras might be required to address the security restrictions of applets.
// CountClientDii.java Dynamic Client, VisiBroker for Java
class CountClientDii
{ public static void main(String args[])
{
boolean loop_all = false;
long startTime, stopTime;
CORBA.Request request;
try
{ // Initialize the ORB.
System.out.println("Initializing
the ORB");
CORBA.ORB orb = CORBA.ORB.init();
// Bind to the Count Object
System.out.println("Binding to Count
Object");
Counter.Count counter = Counter.Count_var.bind("My
Count");
// Set Sum to initial value of 0
System.out.println("Setting Sum
to 0");
counter.sum((int)0);
if ((args.length != 0) &&
(args[0].compareTo("loop_all")
== 0))
loop_all = true;
if (loop_all)
{
System.out.println("Starting
IR lookup + invoke test");
// Calculate Start time
startTime = System.currentTimeMillis();
for (int i = 0 ; i <
1000 ; i++ )
{
request
= buildRequest(counter);
request.invoke();
}
}else
{
System.out.println("Starting
invoke only test");
request = buildRequest(counter);
// Calculate Start time
startTime = System.currentTimeMillis();
// Increment 1000 times
for (int i = 0 ; i <
1000 ; i++ )
{ request.invoke();
}
}
// Calculate stop time; print out
statistics
stopTime = System.currentTimeMillis();
System.out.println("Avg Ping = "
+ ((stopTime - startTime)/1000f)
+ " msecs");
System.out.println("Sum = " + counter.sum());
}
catch(CORBA.SystemException e)
{ System.err.println("System Exception");
System.err.println(e);
}
}
public static CORBA.Request buildRequest(Counter.Count counter)
throws CORBA.SystemException
{
//get interface for Count object
CORBA.InterfaceDef CountInterface = counter._get_interface();
// describe interface for Count object
CORBA._InterfaceDef.FullInterfaceDescription
intDesc =
CountInterface.describe_interface();
if (intDesc.operations[0].name.compareTo("increment")
== 0)
{ //create request object for dynamic increment
CORBA.Request request = counter._request("increment");
// initialize result value
request.result().value().from_long(0);
return request;
} else
System.out.println("Unknown method");
return null;
}
}