Having written the client GUI we are now ready to set up the client's CORBA environment. A client can only communicate with a CORBA object if it possesses a reference to that object. This raises the question of how the client obtains its initial object reference. The fact that some IDL operation may return an object reference is of no help here: without a reference to specify as its target, there is no way to invoke this operation.
In more detail, before a client can enter the CORBA environment, it must first:
CORBA provides a standard set of operations, specified in pseudo IDL (PIDL), to initialize applications and obtain the appropriate object references.
Operations providing access to the ORB reside in the CORBA module. (Like an IDL interface declaration, an IDL (or PIDL) module declaration defines a new namespace for the body of declarations it encloses. What it does not do is define a new type of CORBA object.) Operations providing access to CORBA features such as Object Adapters, the Interface Repository, the Naming Service, and other Object Services reside in the ORB interface defined within the CORBA module.
To provide some flavor of PIDL, here is a fragment of the PIDL specification of CORBA that we rely on in our implementation of the bank client.
module CORBA {
interface Object {
boolean is_a (in string logical_type_id);
...
};
interface ORB {
string object_to_string (in Object obj);
Object string_to_object (in string str);
...
};
...
typedef string ORBid;
typedef sequence <string> arg_list;
ORB ORB_init (inout arg_list argv, in ORBid orb_identifier);
};
The Object interface is implicitly inherited by all IDL interfaces, much as every Dylan class inherits from the class <object>. The is_a operation provides a test for inheritance -- the logical_type_id is a string representation of an interface identifier. The operation returns true if the object is an instance of that interface, including if that interface is an ancestor of the most derived interface of that object.
The ORB operations object_to_string and string_to_object provide an invertible mapping from object references to their representations as strings.
Notice that the CORBA operation ORB_init is defined outside the scope of any interface, providing a means of bootstrapping into the CORBA world. Calling ORB_init initializes the ORB, returning an ORB pseudo object that can be used as the target for further ORB operations.
Like most other language bindings, the Dylan binding adopts the pseudo objects approach, in which these CORBA and ORB operations are accessed by applying the binding's normal IDL mapping rules to the PIDL specification.
In this tutorial, as in the Hello World example of Chapter 2, the client can obtain the initial object reference from a shared file, in which the server has published a reference to its implementation of the bank object, encoded as a string. After starting up, the client reads the file, decodes the string into an object reference (using the ORB utility operation file_to_object, which in turn uses string_to_object), and then uses this reference as the target of further operations.
Alternatively, this demonstration can also use a Name Service to communicate the initial bank reference between the client and server. A Name Service acts as an intermediary, allowing the server to register a reference against name, and then allowing the client to query for the associated reference. To use the Name Service pass
-location-service:naming-service
on the command line of the client.
To change the command line arguments given to the program, choose the Project > Settings... dialog and switch to the Debug tab page. By default the command line arguments for the Bank demo are
-ORBname-service-file c:\temp\ns.ior -location-service:shared-file
which tells the ORB where the Name Service is, but that it should use a shared file to pass the initial Bank reference.
Here is some the Dylan code that implements the initialization of the client:
define method initialize-client ()
let orb = CORBA/ORB-init(make(CORBA/<arg-list>),
"Functional Developer ORB");
let ls = get-location-service();
block ()
let bank = lookup-bank(orb, ls);
let bank-frame = make(<bank-frame>, bank: bank);
start-frame(bank-frame);
exception (lookup-bank-failure(orb, ls))
notify-user("Cannot locate the Bank. Click OK to Exit.");
end block;
end method;
This method first initializes the Functional Developer ORB by calling the Dylan generic function CORBA/ORB-init corresponding to the PIDL ORB_init operation. (Note that the IDL module name CORBA forms a prefix of the Dylan operation name, and that IDL underscore "_" maps to a Dylan dash "-".) The first argument to this call evaluates to an empty CORBA/<arglist>. Passing an empty sequence instructs the CORBA/ORB-init function to ignore this argument and use the application's command line arguments (if any) instead. The value of the second argument, "Functional Developer Orb", merely identifies the ORB to use. The call returns an object of class CORBA/<ORB>.
The function get-location-service reads the command line to see whether to look for a shared file or use a Name Service. It then passes this information to the function lookup-bank, which knows how to get a bank reference using either method. For example, for the shared file case lookup-bank does the following:
define method lookup-bank (orb :: corba/<orb>,
location-service == #"shared-file")
=> (bank :: bankingdemo/<bank>)
as(BankingDemo/<bank>, corba/orb/file-to-object(orb,
$bank-ior-file));
end method;
The constant $bank-ior-file is the name of the shared file used to pass the reference of the bank object from the server to the client.
Invoking CORBA/ORB/file-to-object on this ORB, passing the shared file name, reconstitutes the IOR string contained in the file as an unspecific object reference of class CORBA/<Object>. Calling the as method on this object reference narrows (that is, coerces) it to a more specific object reference of class BankingDemo/<bank>. (The as method, which is generated by the IDL compiler and defined in the Bank-Stubs library, employs an implicit call to the object's is_a operation to check that the desired coercion is safe.)
Finally, the resulting object reference bank, of class BankingDemo/<bank>, is used to make and start a new bank frame, displaying the initial GUI to the user.
The full implementation of the client initialization can be found in the file init-client.dylan.
The implementation of the client is now complete.