The Artima Developer Community
Sponsored Link

Survival of the Fittest Jini Services, Part III
Implement Transactional Jini Services
by Frank Sommers
First Published in JavaWorld, October 19, 2001

<<  Page 3 of 8  >>

Advertisement

Why Commitment Must Precede Trust

To illustrate the default transaction semantics' goals, let's revisit the Jini bookstore's service interface introduced in Part 2 of this series:

public interface BookStore {
  public Collection findBooks(Book template)

    throws RemoteException;
  public OrderConfirmation buyBook(Book book,

                                   CreditCard card,

                                   Customer customer,

                                   Address shipTo,

                                   int daysToDelivery,

                                   Transaction txn)

    throws NoSuchBookException, CreditCardException,

      DeliveryException, RemoteException, TransactionException;

}

The bookstore uses two other Jini services for credit card processing and shipping. These services have the following interfaces:

public interface CreditCard {
  public ChargeConfirmation debit(Account account,

                                  Charge charge,

                                  Transaction txn)

    throws NoSuchAccountException, CannotChargeException,

      CreditCardException, RemoteException, TransactionException;
  public PaymentConfirmation pay(Account account,

                                 Payment payment,

                                 Transaction txn)

    throws NoSuchAccountException, CreditCardException,

      RemoteException, TransactionException;
public CurrentBalance getBalance(Account account)

    throws NoSuchAccountException, CreditCardException,

      RemoteException;


and

public interface ShippingCompany {
  public PickupGuarantee checkPickup(Address origin,

                                     Address destination,

                                     PackageDesc package,

                                     int daysToShip)

    throws ShippingException, RemoteException;
  public PickupConfirmation schedulePickup(PickupGuarantee guar,

                                           Transaction txn)

    throws NoSuchGuaranteeException, ShippingException,

       RemoteException, TransactionException; }

Regardless of any particular implementation, these services provide interfaces to persistent information. While some method calls in the service interfaces need read-only access to persistent data, other method invocations modify data. For instance, the CreditCard service's getBalance() method only reads the credit card's current balance. The debit() method, on the other hand, modifies persistent data -- it increases the account's balance, and can log a purchase onto persistent store.

You can imagine service-performed operations on persistent data in terms of read and write actions. While transactions are units of state transformation (write actions), their importance extends to read actions as well. You can assume outputs from transaction-performed actions only when the transaction commits. This notion is key to understanding the default transaction semantics for Jini services. To solidify this notion, consider the following steps in arranging a book payment:


Figure 1. Perform a transactional method invocation in Jini

Figure 1 illustrates these steps:

  1. A customer purchases a book by calling BookStore's buyBook() method. BookStore then discovers and obtains a reference to a CreditCard service.
  2. BookStore next calls a CreditCard service's debit() method, passing in the customer's account information.
  3. Because debit() consumes a Transaction object, CreditCard implements the net.jini.core.transaction.server.TransactionParticipant interface. TransactionParticipant is a remote interface; it extends java.rmi.Remote, making its methods available for calls from remote virtual machines.
  4. With a call to debit(), CreditCard joins the transaction instance passed in as a parameter. It first casts the Transaction parameter to a net.jini.core.transaction.server.ServerTransaction; then it calls the join() method on that object, passing a reference to itself (a TransactionParticipant).
  5. As a result of joining the transaction, the transaction manager registers CreditCard as a transaction participant. When the transaction commits, the manager calls the commit() method that TransactionParticipant mandates and an instance of CreditCard implements. If the transaction aborts, then the transaction manager calls abort() instead of commit.

In the simplest terms, BookStore calls CreditCard's debit(), waits for a ChargeConfirmation, and then calls commit() in the Transaction object. Thus, although debit() returns a ChargeConfirmation, that ChargeConfirmation cannot be relied upon until the transaction commits.

Therefore, there are two interaction levels between CreditCard and BookStore: method invocation and a transaction. Method invocation outputs are not reliable -- not final -- until the transaction commits. This is because a transaction is an indivisible set of actions, performed atomically; either all transaction steps succeed, or it must appear to the user that the transaction never took place. For instance, if your credit card charge is accepted, but then the shipping company fails to confirm a book's delivery, you don't want that credit card charge to take effect. In that case, the transaction aborts, causing the credit card charge to appear as though it never took place.

<<  Page 3 of 8  >>


Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2017 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us