Note: This section assumes some basic familiarity with the CAPI library. See the CAPI Reference Manual for details.
In this section, we define three CAPI interface classes
bank-interface. These classes are used to present graphical interfaces to CORBA objects with the IDL interfaces
We begin by defining the interface class
(capi:define-interface account-interface ()
((account-ref :initarg :account-ref)
(account-name :initarg :account-name :accessor account-name)
(bank-interface :initarg :owner))
:title (:initarg :account-name)
:visible-min-width '(:character 10)
:callbacks '(credit debit)
:items '("Credit" "Debit")
(account-layout capi:column-layout '(balance-field
(:default-initargs :auto-menus nil :max-width t))
This is how we use an instance of class
account-interface. We store the name of the customer owning this account in the title of the display pane (using
account-ref slot stores a CORBA object reference (of class
BankingDemo:account) to the corresponding CORBA
account object on the server. The
bank-interface slot stores a pointer to the
bank interface for this object.
balance-field reports the state of the CORBA object's balance attribute as a readonly text field. We delegate the initialization of this field value to an
initialize-instance after method specialized on
account-interface. The value needs to be updated after each invocation of a CORBA
The button panel
button-panel defines buttons to activate callbacks
credit-callback. These callbacks prompt the user for amounts and then invoke the corresponding CORBA operations
credit on the object reference stored in the
account-ref field. We will implement these callbacks in a moment.
The buttons are laid out in a column layout
account-layout. Mirroring the fact that the IDL interface
checkingAccount inherits from
account, we define the Common Lisp frame class
checking-account-interface as a subclass of
:visible-min-width '(:character 10)
limit-field reports the state of the CORBA object's
limit attribute as a readonly text field. Again, we can delegate the initialization of this field's value to an
initialize instance after method specialized on
checking-account-layout simply lays out the inherited layout
account-layout, containing the account's balance, together with the additional
The definition of
bank-interface class follows the same pattern:
(capi:define-interface bank-interface ()
((bank-ref :initarg :bank-ref))
(("Open Account" :callback 'open-account-callback)
("Open Checking Account" :callback
("Retrieve Account" :callback 'retrieve-account-callback)
("Close Account" :callback 'close-account-callback))
(accounts-area capi:row-layout ()
(:default-initargs :auto-menus nil :best-width 400))
accounts-area layout keeps track of the
account-interfaces created by the
bank interface as the result of invoking operations on the CORBA
bank object. This list is maintained to prevent the user from obtaining more than one interface to the same account. We need to update it whenever an account interface is exited.
The interface menu items
Open Checking Account
These callbacks prompt the user for appropriate arguments and then invoke the corresponding CORBA operations
closeAccount on the object reference stored in the
bank-ref slot. We will see the implementation of these callbacks in a moment.
An easy way to do this is to add an
initialize-instance after method specialized on
account-interface. (In Common Lisp, each call to make an instance of a given class is automatically followed by a call to initialize that instance; you are free to specialize the
initialize-instance generic function on particular classes.)
Here, we encounter our first example of invoking a CORBA operation on a CORBA object. The Common Lisp variable
account-ref, of class
BankingDemo:account, contains a proxy for a CORBA
account object on the server. The application
(op:balance account-ref) invokes a stub method specialized on the proxy's class. The stub method forwards the request across the ORB to the actual object on the server.
The request is executed on the object in the server and the result passed back across the ORB to the stub method, which returns the value to the client as a
corba:long. This value is then used to set the initial value of the
Inheritance ensures that the method on
account-interface is called which registers the interface and sets up its
balance field; a call to the
op:limit stub determines the initial value of its
Defining the callbacks attached to each button is straightforward. Recall that in CAPI, because we stated that the button callback type was
:interface, the argument passed to a callback is the interface whose activation triggered that callback.
The callback is passed the
account interface. It then extracts the CORBA object reference stored in the frame's
account-ref slot and prompts the user for an amount. The function
capi:prompt-for-integer queries the user for an integer and returns nil if the user cancels the dialog. If the amount is valid, the callback invokes the stub method
op:credit on the CORBA object reference with the specified absolute value of the amount (recall that the
credit operation expects an unsigned
long as its argument). Finally, it updates the
balance field of the frame with the current value of the object's
balance attribute, obtained by invoking the stub method
The only difference is that
debit-callback must deal with the additional possibility that the
debit operation, when invoked on the target object, may fail, raising the IDL exception refusal. If the object raises this exception, the
op:debit stub method signals it as a Common Lisp condition of class
The exception can then be caught and handled in any of the standard Common Lisp ways. Here, we simply place the invocation in the body of a
handler-case statement with an appropriate exception clause to handle the condition.
The callback extracts the CORBA object reference stored in the interface's
bank-ref slot. The function
capi:prompt-for-string queries the user for the new customer's name returning a string (or nil if the user cancels the dialog). If the dialog has not been cancelled, the callback invokes the stub method
op:openAccount on the target object reference bank, passing the argument name. If successful, the invocation returns an object reference, of class
BankingDemo:account, to an IDL account object, which is then used to make and start a new
account-interface, via a call to
Recall that the IDL operation
openAccount may fail, raising the IDL user exception
duplicateAccount. As in the definition of
debit-callback, we cater for this eventuality by placing the invocation in the body of a
handler-case statement and install a handler on the corresponding Common Lisp condition of class
BankingDemo:bank/duplicateAccount. This handler simply informs the user of the exception using the CAPI function
display-message to create and display a simple alert dialog box.
The definition of
open-checking-account-callback is similar to the definition of
openAccount-callback but prompts the user for an additional integer to pass as the overdraft limit of the new checking account:
This callback incorporates a test that prevents the user from being presented with more than one interface to the same account. It invokes the stub method
op:retrieveAccount only if the account under that name is not already on display. Because of IDL inheritance, the server implementing the IDL
retrieveAccount operation may return any object reference whose interface inherits from the IDL account interface.
In particular, the server may return an IDL
checkingAccount as a special instance of an IDL account. In Common Lisp terms, this means that the stub method
Op:retrieveAccount may return an object reference of class
BankingDemo:checkingAccount as a special instance of
BankingDemo:account. The call to
make-account-frame dispatches on the actual, or most derived, class of the resulting object reference, making an
checking-account-interface as appropriate.
prompt-with-list presents a dialog asking the user to select a name from the list of available account frames (indexed by their
account-name), returning nil if the user decides to cancel the dialog. Given a valid selection, the callback invokes the stub method
op:closeAccount on the target object reference,
bank-ref, passing the name of the selected account. Finally, the account interface is removed from the bank interface.
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.
Operations providing access to the ORB reside in the
CORBA module. (Like an IDL interface declaration, a (P)IDL 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 an Object Adapter, Interface Repository, Naming Service, and other Object Services reside in the ORB interface defined within 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.
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 Common Lisp 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, we use a very simple technique to obtain the initial object reference. The client assumes that the server has published a reference to its implementation of the
bank object, encoded as a string, in a shared file. After starting up, the client reads the file, decodes the string as an object reference, and then uses this reference as the target of further operations.
let statement first initializes The LispWorks ORB by calling the Common Lisp generic function
op:ORB_init corresponding to the PIDL
ORB_init operation. The first argument to this call is an empty list. Passing an empty sequence instructs the
op:ORB_init function to ignore this argument and use the application's command line arguments (if any) instead. The value of the second argument,
"LispWorks ORB", merely identifies the ORB to use.
op:string_to_object on this ORB, passing the string read from the shared file, reconstitutes the string as an unspecific object reference of class
CORBA:Object. Calling the
op:narrow method on this object reference narrows (that is, coerces) it to a more specific object reference of class
op:narrow method 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-ref, of class
BankingDemo:bank, is used to make and start a new bank interface, displaying the initial GUI to the user. The implementation of the client is now complete.
Developing Component Software with CORBA - 14 Feb 2015