ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Adding Transactions to Servlets with JOTM

by Jeff Mesnil
07/30/2003

Introduction

J2EE provides a great deal of functionality besides servlets. Servlet developers may be reluctant to use such features, neither willing nor having time to replace their simple servlet containers with a big J2EE server that provides more than what they need. However, given the modular nature of J2EE, it is possible to enhance web applications by integrating small components responsible for specific J2EE features into servlet containers. One such feature is transactions.

For a complete description of J2EE transactions, you should have a look at the three ONJava articles about it. For now, just keep in mind that a transaction is a sequence of operations on resources (for example, a database) defined by four properties, often referred to as ACID due to their initials:

Atomicity
The transaction operations either all complete successfully (and the transaction is committed), or none complete (and the transaction is rolled back). This is the all-or-nothing property. A transaction should be treated as a single unit of work. There is absolutely no possibility of a mix of complete and incomplete operations in a transaction.
Consistency
A completed transaction transforms resources from one valid state to another valid state. Some of the concrete examples of consistency are referential integrity of the database and unique primary keys in tables.
Isolation
Changes to shared resources affected by a transaction do not become visible outside of the transaction until the transaction commits. Isolation ensures that transactions do not access data that is being updated concurrently.
Durability
The changes that result from a transaction commitment survive subsequent system or media failures.

JOTM (Java Open Transaction Manager) is a fully functional, open source standalone transaction manager developed by the ObjectWeb consortium. It provides transaction support for Java applications and is compliant with JTA, the Java Transaction API. You can find out more details on the JOTM home page. Integrating JOTM in Tomcat (or any servlet container) makes it possible for JSP and servlet developers to take advantage of transactions in a lightweight way to create more robust web applications.

To highlight how transactions can enhance web applications, consider the classical scenario of an ATM that uses a web browser to interact with the client.

ATM Example

Scenario

The use case is simple: a client wants to withdraw money from an ATM. He gives his client name, john_doe, and the amount of money he wants to withdraw, $50. If he has enough money on his bank account and if there is enough available cash in the ATM, the application gives him the cash and withdraws the amount from his bank account. Otherwise, the operation aborts and nothing happens except an error message. To focus on transactions and to keep things simple, we will not worry about security issues, instead assuming that the user is correctly authenticated.

This very simple example is surprisingly hard to implement in a robust way without transactions. One client operation involves two different resources: the ATM and the client's bank account. This automatically introduces ACIDity issues in the application design. For example, if the operation succeeds on the ATM and fails on the bank account (perhaps due to a communication failure), the client will have the cash but his account won't be updated. Bad news for the bank.

Even worse, if the bank account is correctly updated but an error prevents the ATM from delivering the money, the client won't have the cash, but the money will be withdrawn from his account.

To prevent such cases, in your application, you can 1) contact the two resources and inform them about all of the current operations performed by the client, 2) ask them if they can perform the operations, and, 3) if they both agree, ask them to perform the operations. Even this solution is not robust enough, however, if money is withdrawn from the client's bank account by another operation between the second and third steps. It is possible that the money withdrawal fails, for example, if the client can't have a negative balance.

That's where transactions can make your application more simple and robust: by performing all of the operations on the two resources within one transaction, they will solve the ACIDity issues (especially Atomicity) for you.

Application Design

Data Layer

At the data layer, we have two different databases and one table in each database. To make the example more realistic, we've used two different databases because it is possible to withdraw from an ATM not owned by the client's bank (see below to configure the databases).

  • banktest contains the account table, representing client accounts.
  • atmtest contains the atm table, representing the ATM.

Logic Layer

At the logic layer, we have three classes accessing the resources and performing operations on them:

  • foo.BankAccount represents the bank account of a given client and performs database operations on the account table through JDBC.
  • bar.ATM represents the ATM and performs the JDBC operations on the atm table.
  • bar.CashDelivery uses the two previous classes to perform a client operation.

All of the logic is done in the deliverCash method of CashDelivery.java.

The javax.transaction.UserTransaction interface is used to demarcate the transaction. All of the operations between utx.begin() and utx.commit() (or utx.rollback()) are executed within one transaction. This ensures that your web application won't suffer of the shortcomings discussed previously.

Thanks to transactions, the application logic is much simpler, and consists of these simple steps:

  1. Start a transaction.
  2. Contact the bank of the client and withdraw money from his account.
  3. Tell the ATM to deliver the cash.
  4. Complete the transaction.
    • If things succeeded, commit the transaction.
    • Otherwise, rollback the transaction.
  5. Report to the client the outcome of the transaction. If the transaction succeeds, cash will be delivered and money will be withdrawn from the account. Otherwise, nothing happens.

Example 1. CashDelivery.java

public boolean deliver(String client, int value) {
    InitialContext ctx          = new InitialContext();
    UserTransaction utx = (UserTransaction)
    ctx.lookup("java:comp/UserTransaction");

    ...

    boolean success = false;
    try {
        // begin the transaction
        utx.begin();

        // contact the bank of the client...
        BankAccount account = new BankAccount(client);

        // ... and withdraw the value from his account.
        account.withdraw(value);

        // contact the ATM...
        ATM atm = new ATM();

        // ... and deliver the cash to the client.
        atm.deliverCash(value);

        // everything went ok.
        success = true;

    } catch (Exception e) {
        // something went wrong, we have to
        // report it to the client
        explanation += e.getMessage();

    } finally {
        try {
            if (success) {
                /* everything was ok, we commit the transaction.
                * only now, the money will be effectively withdrawn
                * from the account and the cash delivered to the client.
                */
                utx.commit();

            } else {
                /* something went wrong, we rollback the transaction.
                * none of the operations done within the transaction
                * have been done.
                */
                utx.rollback();
            }
        } catch (Exception e) {
            /* something went wrong during the completion of the transaction.
            * we're still guaranteed that none of the operations done within
            * the transaction have been done/
            */
            // we have to report it to the client
            explanation += "\n" + e.getMessage();

            // finally, the transaction was not a success
            success = false;

        } finally {
            return success;
        }
    }
}

Presentation Layer

At the presentation layer, the application is composed of two JSP files:

  • atm.jsp, the Cash Delivery application, which sends to the bar.CashDelivery class the client's login and the amount of cash to withdraw, and displays the result of the client's operation.
  • admin.jsp, the Management Console used to show and update information related to the two resources. (It is not really a part of the application design, but has been added to simplify resource updates, such as to deposit money on a client's account.)

Application Design Image
Figure 1. Application Design

Pages: 1, 2

Next Pagearrow