The Artima Developer Community
Sponsored Link

Survival of the Fittest Jini Services, Part II
Use Transactions to Coordinate the Reliable Interaction of Jini Services
by Frank Sommers
First Published in JavaWorld, July 2001

<<  Page 4 of 8  >>

Advertisement

Towards transactional services

The first step for a service to become a transaction participant is to define transactional methods in its public service interface. Since Jini has to accommodate both transactional and nontransactional services, there is no equivalent of the transactional remote procedure call (TRPC) mechanism popular in traditional transaction processing systems. In those systems, the runtime infrastructure annotates each method call with a unique transaction identifier (TRID). By having an identical TRID, a set of operations are easily identified as belonging to (performed under) the same transaction.

Jini transactions group operations together via an object representing a transaction instance. For the default semantics, the net.jini.core.transaction.Transaction interface defines this object, which is implemented by net.jini.core.transaction.server.ServerTransaction. You need to pass in a Transaction instance along with the other parameters to make a method call transactional:

public interface BookStore {

  public Collection findBooks(Book template)
    throws RemoteException;

  public OrderConfirmation buyBook(Book book,
                                   Account creditCard,
                                   Customer customer,
                                   Address shipTo,
                                   int daysToDelivery,
                                   Transaction txn)
    throws NoSuchBookException, CreditCardException,
      DeliveryException, BookStoreException,
      RemoteException, TransactionException;

}

For the credit card service:

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 for the shipping service:

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; }

You will notice that each method now declares TransactionException to indicate possible failure in the transaction's processing. We still need the other application-specific exceptions for failures that occur independent of the transaction.

In addition to extending the method signatures, each service's implementation must also declare itself to be a transaction participant by implementing the net.jini.core.transaction.server.TransactionParticipant interface. By implementing this interface, the object guarantees that it can join the transaction and participate in the 2PC protocol.

Note that the service's implementation becomes a transaction participant; the service's proxy does not. The proxy runs inside the address space of whatever client retrieves it from lookup services. Therefore, the proxy's state becomes intrinsically a part of that client's state -- all computations performed on the proxy itself are local to the client.

A TransactionParticipant service must join a transaction when it receives a transactional method call. The ServerTransaction class provides a join() method that consumes a TransactionParticipant and a crash count. Your service can join the same transaction with the same crash count any number of times. All the method calls that join the transaction perform their actions under that transaction.

During a transaction, your service might crash for some reason. If such a crash causes your service to lose the changes made during the transaction, you must increase the crash count number when the service reactivates and reinitializes. Since the default semantics must guarantee ACID properties, joining a transaction with a crash count number different from the service's original crash count results in a CrashCountException. At that point, you can decide what to do: you might choose to abort the whole transaction.

<<  Page 4 of 8  >>


Sponsored Links



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