Next Previous Up Top Contents Index

6.4 Implementing the bank server

6.4.2 Implementing the servant methods

The next step in implementing the server is to define methods, specialized on the implementation classes, for each of the protocol functions corresponding to an IDL attribute or operation.

Implementing a protocol function boils down to defining a concrete method for that function that specializes on the implementation class of its target object. Recall that the target object of a protocol function is the first parameter to that function.

We can now present the implementations of the protocol functions. Theop:name method corresponding to thename attribute is automatically generated by the IDL compiler to reference a slotop:name in the class that takes theinitarg :name to initialize it. The same rules apply toop:balance.

Theop:credit method on anAccount increments the record's balance field by executing a database update statement:

(corba:define-method op:credit ((self account-implementation) amount)
  (with-slots (op:name bank op:balance) self
    (with-slots (connection) bank
      (let ((old-balance (lookup-row-value op:name 
                         connection :balance)))
        (update-database-row op:name connection 
                             :balance (setf op:balance
                                    (+ old-balance amount)))))))

Theop:debit method on anAccount executes a database update statement that decrements the record'sbalance field, provided the balance exceeds the desired amount:

(corba:define-method op:debit ((self account-implementation) amount)
  (with-slots (op:name bank op:balance) self
    (with-slots (connection) bank
      (let ((old-balance op:balance))
        (if (< old-balance amount)
            (error 'BankingDemo:Account/Refusal 
                   :reason (format nil 
                               "Can't debit ~A because the 
                               balance is ~A." 
                              amount old-balance))
          (update-database-row 
             op:name connection 
             :balance (setf op:balance (- 
                                  old-balance amount))))))))

Theop:limit method is automatically generated, as it is also an attribute.

Because we definedcheckingAccount-servant to inherit from
account-servant, there is no need to re-implement theop:credit method for this implementation class. However, we do want to define a specializedop:debit method oncheckingAccount, to reflect that a checking account can be overdrawn up to its limit:

(corba:define-method op:debit (
                    (self checkingAccount-implementation) amount)
  (with-slots (op:name bank op:balance) self
    (with-slots (connection) bank
      (let ((old-balance (lookup-row-value op:name 
                          connection :balance))
            (limit (lookup-row-value op:name connection :limit)))
        (if (< (+ old-balance limit) amount)
            (error 'BankingDemo:Account/Refusal 
                   :reason (format nil "Can't debit ~A because the
                                    balance is ~A (limit is ~A)." 
                                   amount old-balance limit))
          (update-database-row op:name connection 
                               :balance (setf op:balance (- 
                                  old-balance amount))))))))

TheBankingDemo bankop:name method returns the value of the bank'sop:name slot and is automatically generated.

Theop:openAccount method onBank illustrates the raising of CORBA user exceptions:

(corba:define-method op:openAccount ((self bank-implementation)
                                      name)
  (with-slots (connection poa account-impls) self
    (when (find-database-row name connection)
      (error 'Bankingdemo:Bank/Duplicateaccount))
    (create-database-row name connection)
    (update-database-row name connection :balance 0)
    (let ((new-account (make-instance 'account-implementation
                                      :name name
                                      :bank self
                                      :balance 0)))
      (push new-account account-impls)
      (op:narrow 'BankingDemo:Account
                 (op:servant_to_reference poa new-account)))))

If the(find-database-row name connection) test succeeds, the call to(error 'Bankingdemo:Bank/Duplicateaccount) raises a Common Lisp condition. (We omit the definition offind-database-row, which can be found in the source.) Recall that the condition class
BankingDemo:bank/duplicateAccount corresponds to the IDLduplicateAccount exception. The POA that invoked this method in response to a client's request will catch the condition and send theduplicateAccount exception back to the client. If there is no existing account for the supplied name, theop:openAccount method creates a new record in the database.

Finally, the method makes a newservant of classaccount-implementation, registers it with the bank's POA with a call toop:servant_to_reference, and narrows the resulting object reference to the more specific class
BankingDemo:account, the class of object references toaccount objects.

Theop:openCheckingAccount method is similar, except that it initializes theop:limit field of the new account record with the desired overdraft limit and registers a new servant of classcheckingAccount-implementation, returning an object reference of classBankingDemo:checkingAccount:

(corba:define-method op:openCheckingAccount (
                           (self bank-implementation) name limit)
  (with-slots (connection poa account-impls) self
    (when (find-database-row name connection)
      (error 'Bankingdemo:Bank/Duplicateaccount))
    (create-database-row name connection)
    (update-database-row name connection :balance 0 :limit limit)
    (let ((new-account (make-instance 
                         'checkingaccount-implementation
                                      :name name
                                      :bank self
                                      :balance 0
                                      :limit limit)))
      (push new-account account-impls)
      (op:narrow 'Bankingdemo:Checkingaccount
                 (op:servant_to_reference poa new-account)))))

Theop:retrieveAccount method uses thename parameter to find a database row of the given name. If the query returns nil, indicating that there is no record with that name, the method raises the CORBA user exceptionnonExistentAccount by signalling the corresponding Common Lisp error.

Otherwise, the method uses the value of theop:limit field to distinguish whether the account is an account or a current account, creating a new servant of the appropriate class:

(corba:define-method op:retrieveAccount ((self 
                                       bank-implementation) name)
  (with-slots (connection poa account-impls) self
    (unless (find-database-row name connection)
      (error 'Bankingdemo:Bank/NonExistentAccount))
    (let ((limit (lookup-row-value name connection :limit))
          (balance (lookup-row-value name connection :balance)))
      (if (not limit)
          (let ((account (make-instance 'account-implementation
                        :name name :bank self :balance balance)))
            (push account account-impls)
            (op:narrow 'BankingDemo:Account
                       (op:servant_to_reference
                        poa
                        account)))
        (let ((account (make-instance 
                         'checkingaccount-implementation 
                                      :name name
                                      :bank self
                                      :balance balance
                                      :limit limit)))
          (push account account-impls)
          (op:narrow 'Bankingdemo:Checkingaccount
                     (op:servant_to_reference
                      poa
                      account)))))))

Finally, thecloseAccount removes the record of an account from the database by executingdelete-database-row call:

(corba:define-method op:closeaccount 
    ((self bank-implementation) account)   
  (with-slots (connection poa account-impls) self     
    (let ((servant (op:reference_to_servant poa account)))       
      (op:deactivate_object poa (op:reference_to_id poa account))       
      (removef account-impls servant)       
      (with-slots (op:name) servant         
         (delete-database-row op:name connection))))

Note that we need to de-reference the object reference account that is passed in as the parameter of theop:closeAccount operation, using a call to theop:reference_to_servant operation of the POA.

Here, we make implicit use of our knowledge that, in our application, the server only encounters object references registered with its local POA. This assumption is not true in general.


Developing Component Software with CORBA - 22 Jan 1999

Next Previous Up Top Contents Index

Generated with Harlequin WebMaker