Next Prev Up Top Contents Index

5.4.2 Defining the callbacks

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 credit-callback is activated by the Credit button of some account interface:

(defun credit (self)
  (with-slots (balance-field account-ref) self
    (let ((amount (capi:prompt-for-integer "Amount?" :min 0)))
       (when amount
        (op:credit account-ref amount)
        (setf (capi:display-pane-text balance-field)
              (format nil "~A" (op:balance account-ref)))))))

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 op:balance .

The definition of debit-callback is very similar to the definition of
credit-callback:

(defun debit (self)
  (with-slots (balance-field account-ref) self
    (let ((amount (capi:prompt-for-integer "Amount?" :min 0)))
      (when amount
        (handler-case
            (progn
              (op:debit account-ref amount)
              (setf (capi:display-pane-text balance-field)
                    (format nil "~A" (op:balance account-ref))))
          (BankingDemo:account/refusal
           (xx)
           (capi:display-message "Debit returned refusal with
                                 string: <~A>"
                                 (op:reason xx))))))))

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
BankingDemo:account/refusal .

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 open-account-callback is activated by the openAccount-button of some bank frame:

(defun open-account-callback (self)
  (with-slots (bank-ref) self
    (let ((name (capi:prompt-for-string "Name?")))
      (when name
        (handler-case
            (let ((account-ref
                   (op:openaccount bank-ref name)))
              (make-account-frame account-ref
               :bank-interface self :title name))
          (Bankingdemo:Bank/DuplicateAccount
           ()
           (capi:display-message "Cannot create account for
                                ~A" name)))))))

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 make-account-frame .

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:

(defun open-checking-account-callback (self)
  (with-slots (bank-ref) self
    (let ((name (capi:prompt-for-string "Name?")))
      (when name
        (let ((limit (capi:prompt-for-integer "Limit?")))
          (when limit
            (handler-case
                (let ((account-ref
                       (op:opencheckingaccount bank-ref
                                              name limit)))
                  (make-account-frame account-ref
                   :bank-interface self :title name))
              (Bankingdemo:Bank/DuplicateAccount
               ()
               (capi:display-message "Cannot create another
                                    account for ~A" name)))))))))

While openAccount and openCheckingAccount create accounts for new customers, the retrieveAccount operation is simply meant to look up the account of an existing customer:

(defun retrieve-account-callback (self)
  (with-slots (bank-ref) self
    (let ((name (capi:prompt-for-string "Name?")))
      (when name
        (if (find-named-frame self name)
            (capi:display-message "Already viewing it...")
          (handler-case
              (let ((account-ref
                     (op:retrieveaccount bank-ref name)))
                (when (op:Is_a account-ref (op:id
                   Bankingdemo:_Tc_Checkingaccount))
                  (setf account-ref
                        (op:narrow 'Bankingdemo:Checkingaccount
                                    account-ref)))
                (make-account-frame account-ref
                  :bank-interface self :title name))
            (Bankingdemo:Bank/NonExistentAccount
             ()
             (capi:display-message "No account exists for
                                   name ~A" name))))))))

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 account-interface or checking-account-interface as appropriate.

The definition of the close-account-callback is straightforward:

(defun close-account-callback (self)
  (with-slots (bank-ref) self
    (let ((name (capi:prompt-with-list (all-frame-names self)
                                       "Choose account")))
      (when name
        (op:closeaccount bank-ref
                         (with-slots (account-ref)
                             (find-named-frame self name)
                           account-ref))
        (remove-account-frame self name)))))

The function 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.


Developing Component Software with CORBA - 14 Dec 2001

Next Prev Up Top Contents Index