The Java EE 5 Tutorial

Chapter 22 Session Bean Examples

Session beans provide a simple but powerful way to encapsulate business logic within an application. They can be accessed from remote Java clients, web service clients, and from components running in the same server.

In Chapter 21, Getting Started with Enterprise Beans, you built a stateless session bean named ConverterBean. This chapter examines the source code of three more session beans:

The cart Example

The cart session bean represents a shopping cart in an online bookstore. The bean’s client can add a book to the cart, remove a book, or retrieve the cart’s contents. To assemble cart, you need the following code:

All session beans require a session bean class. All enterprise beans that permit remote access must have a remote business interface. To meet the needs of a specific application, an enterprise bean may also need some helper classes. The CartBean session bean uses two helper classes (BookException and IdVerifier) which are discussed in the section Helper Classes.

The source code for this example is in the tut-install/javaeetutorial5/examples/ejb/cart/ directory.

The Business Interface

The Cart business interface is a plain Java interface that defines all the business methods implemented in the bean class. If the bean class implements a single interface, that interface is assumed to the business interface. The business interface is a local interface unless it is annotated with the javax.ejb.Remote annotation; the javax.ejb.Local annotation is optional in this case.

The bean class may implement more than one interface. If the bean class implements more than one interface, either the business interfaces must be explicitly annotated either @Local or @Remote, or the business interfaces must be specified by decorating the bean class with @Local or @Remote. However, the following interfaces are excluded when determining if the bean class implements more than one interface:

The source code for the Cart business interface follows:

package com.sun.tutorial.javaee.ejb;

import java.util.List;
import javax.ejb.Remote;

@Remote
public interface Cart {
    public void initialize(String person) throws BookException;
    public void initialize(String person, String id)
         throws BookException;
    public void addBook(String title);
    public void removeBook(String title) throws BookException;
    public List<String> getContents();
    public void remove();
}

Session Bean Class

The session bean class for this example is called CartBean. Like any stateful session bean, the CartBean class must meet these requirements:

Stateful session beans also may:

The source code for the CartBean class follows.

package com.sun.tutorial.javaee.ejb;

import java.util.ArrayList;
import java.util.List;
import javax.ejb.Remove;
import javax.ejb.Stateful;

@Stateful
public class CartBean implements Cart {
    String customerName;
    String customerId;
    List<String> contents;

    public void initialize(String person) throws BookException {
        if (person == null) {
            throw new BookException("Null person not allowed.");
        } else {
            customerName = person;
        }

        customerId = "0";
        contents = new ArrayList<String>();
    }

    public void initialize(String person, String id)
                 throws BookException {
        if (person == null) {
            throw new BookException("Null person not allowed.");
        } else {

            customerName = person;
        }

        IdVerifier idChecker = new IdVerifier();

        if (idChecker.validate(id)) {
            customerId = id;
        } else {
            throw new BookException("Invalid id: " + id);
        }

        contents = new ArrayList<String>();
    }

    public void addBook(String title) {
        contents.add(title);
    }

    public void removeBook(String title) throws BookException {
        boolean result = contents.remove(title);
        if (result == false) {
            throw new BookException(title + " not in cart.");
        }
    }

    public List<String> getContents() {
        return contents;
    }

    @Remove
    public void remove() {
        contents = null;
    }
}

Life-Cycle Callback Methods

Methods in the bean class may be declared as a life-cycle callback method by annotating the method with the following annotations:

Life-cycle callback methods must return void and have no parameters.

@PostConstruct methods are invoked by the container on newly constructed bean instances after all dependency injection has completed and before the first business method is invoked on the enterprise bean.

@PreDestroy methods are invoked after any method annotated @Remove has completed, and before the container removes the enterprise bean instance.

@PostActivate methods are invoked by the container after the container moves the bean from secondary storage to active status.

@PrePassivate methods are invoked by the container before the container passivates the enterprise bean, meaning the container temporarily removes the bean from the environment and saves it to secondary storage.

Business Methods

The primary purpose of a session bean is to run business tasks for the client. The client invokes business methods on the object reference it gets from dependency injection or JNDI lookup. From the client’s perspective, the business methods appear to run locally, but they actually run remotely in the session bean. The following code snippet shows how the CartClient program invokes the business methods:

cart.create("Duke DeEarl", "123");
...
cart.addBook("Bel Canto");
 ...
List<String> bookList = cart.getContents();
...
cart.removeBook("Gravity’s Rainbow");

The CartBean class implements the business methods in the following code:

public void addBook(String title) {
   contents.addElement(title);
}

public void removeBook(String title) throws BookException {
   boolean result = contents.remove(title);
   if (result == false) {
      throw new BookException(title + "not in cart.");
   }
}

public List<String> getContents() {
   return contents;
}

The signature of a business method must conform to these rules:

The throws clause can include exceptions that you define for your application. The removeBook method, for example, throws the BookException if the book is not in the cart.

To indicate a system-level problem, such as the inability to connect to a database, a business method should throw a javax.ejb.EJBException. The container will not wrap application exceptions such as BookException. Because EJBException is a subclass of RuntimeException, you do not need to include it in the throws clause of the business method.

The Remove Method

Business methods annotated with javax.ejb.Remove in the stateful session bean class can be invoked by enterprise bean clients to remove the bean instance. The container will remove the enterprise bean after a @Remove method completes, either normally or abnormally.

In CartBean, the remove method is a @Remove method:

@Remove
public void remove() {
	contents = null;
}

Helper Classes

The CartBean session bean has two helper classes: BookException and IdVerifier. The BookException is thrown by the removeBook method, and the IdVerifier validates the customerId in one of the create methods. Helper classes may reside in the EJB JAR file that contains the enterprise bean class, or in an EAR that contains the EJB JAR.

Building, Packaging, Deploying, and Running the cart Example

You can build, package, deploy, and run the cart application using either NetBeans IDE or the Ant tool.

Building, Packaging, and Deploying the cart Example Using NetBeans IDE

    Follow these instructions to build, package, and deploy the cart example to your Application Server instance using the NetBeans IDE IDE.

  1. In NetBeans IDE, select File->Open Project.

  2. In the Open Project dialog, navigate to tut-install/javaeetutorial5/examples/ejb/.

  3. Select the cart folder.

  4. Select the Open as Main Project and Open Required Projects check boxes.

  5. Click Open Project.

  6. In the Projects tab, right-click the cart project and select Undeploy and Deploy.

This builds and packages the application into cart.ear, located in tut-install/javaeetutorial5/examples/ejb/cart/dist/, and deploys this EAR file to your Application Server instance.

Running the cart Application Client Using NetBeans IDE

To run cart’s application client, select Run->Run Main Project. You will see the output of the application client in the Output pane:

...
Retrieving book title from cart: Infinite Jest
Retrieving book title from cart: Bel Canto
Retrieving book title from cart: Kafka on the Shore
Removing "Gravity’s Rainbow" from cart.
Caught a BookException: "Gravity’s Rainbow" not in cart.
Java Result: 1
run-cart-app-client:
run-nb:
BUILD SUCCESSFUL (total time: 14 seconds)

Building, Packaging, and Deploying the cart Example Using Ant

    Now you are ready to compile the remote interface (Cart.java), the home interface (CartHome.java), the enterprise bean class (CartBean.java), the client class (CartClient.java), and the helper classes (BookException.java and IdVerifier.java).

  1. In a terminal window, go to this directory:


    tut-install/javaeetutorial5/examples/ejb/cart/
  2. Type the following command:


    ant
    

    This command calls the default target, which builds and packages the application into an EAR file, cart.ear, located in the dist directory.

  3. Type the following command:


    ant deploy
    

    cart.ear will be deployed to the Application Server.

Running the cart Application Client Using Ant

    When you run the client, the application client container injects any component references declared in the application client class, in this case the reference to the Cart enterprise bean. To run the application client, perform the following steps.

  1. In a terminal window, go to this directory:


    tut-install/javaeetutorial5/examples/ejb/cart/
  2. Type the following command:


    ant run
    

    This task will retrieve the application client JAR, cartClient.jar and run the application client. cartClient.jar contains the application client class, the helper class BookException, and the Cart business interface.

    This is the equivalent of running:


    appclient -client cartClient.jar
    
  3. In the terminal window, the client displays these lines:


    [echo] running application client container.
    [exec] Retrieving book title from cart: Infinite Jest
    [exec] Retrieving book title from cart: Bel Canto
    [exec] Retrieving book title from cart: Kafka on the Shore
    [exec] Removing "Gravity’s Rainbow" from cart.
    [exec] Caught a BookException: "Gravity’s Rainbow" not in cart.
    [exec] Result: 1

The all Task

As a convenience, the all task will build, package, deploy, and run the application. To do this, enter the following command:


ant all

Undeploying the cart Example

    To undeploy cart.ear using NetBeans IDE:

  1. Click the Services tab.

  2. Expand the Servers node and locate the Application Server instance to which you deployed cart.

  3. Expand your Application Server instance node, then Applications->Enterprise Applications.

  4. Right-click cart and select Undeploy.

To undeploy cart.ear using Ant, enter the following command:


ant undeploy

A Web Service Example: helloservice

This example demonstrates a simple web service that generates a response based on information received from the client. HelloServiceBean is a stateless session bean that implements a single method, sayHello. This method matches the sayHello method invoked by the client described in A Simple JAX-WS Client.

The Web Service Endpoint Implementation Class

HelloServiceBean is the endpoint implementation class. The endpoint implementation class is typically the primary programming artifact for enterprise bean web service endpoints. The web service endpoint implementation class has the following requirements:

Stateless Session Bean Implementation Class

The HelloServiceBean class implements the sayHello method, which is annotated @WebMethod. The source code for the HelloServiceBean class follows:

package com.sun.tutorial.javaee.ejb;

import javax.ejb.Stateless;
import javax.jws.WebMethod;
import javax.jws.WebService;

@Stateless
@WebService
public class HelloServiceBean {
    private String message = "Hello, ";

    public void HelloServiceBean() {}

    @WebMethod
    public String sayHello(String name) {
        return message + name + ".";
    }
}

Building, Packaging, Deploying, and Testing the helloservice Example

You can build, package, and deploy the helloservice example using either NetBeans IDE or Ant. You can then use the Admin Console to test the web service endpoint methods.

Building, Packaging, and Deploying the helloservice Example Using NetBeans IDE

    Follow these instructions to build, package, and deploy the helloservice example to your Application Server instance using the NetBeans IDE IDE.

  1. In NetBeans IDE, select File->Open Project.

  2. In the Open Project dialog, navigate to tut-install/javaeetutorial5/examples/ejb/.

  3. Select the helloservice folder.

  4. Select the Open as Main Project and Open Required Projects check boxes.

  5. Click Open Project.

  6. In the Projects tab, right-click the helloservice project and select Undeploy and Deploy.

This builds and packages to application into helloservice.ear, located in tut-install/javaeetutorial5/examples/ejb/helloservice/dist, and deploys this ear file to your Application Server instance.

Building, Packaging, and Deploying the helloservice Example Using Ant

    Follow these instructions to build, package, and deploy the helloservice example to your Application Server instance using Ant.

  1. In a terminal window, go to the tut-install/javaeetutorial5/examples/ejb/helloservice/ directory.

  2. To build helloservice, type the following command:


    ant
    

    This runs the default task, which compiles the source files and packages the application into a JAR file located at tut-install/examples/ejb/helloservice/dist/helloservice.jar.

  3. To deploy helloservice, type the following command:


    ant deploy
    

    Upon deployment, the Application Server generates additional artifacts required for web service invocation, including the WSDL file.

Testing the Service without a Client

  1. The Application Server Admin Console allows you to test the methods of a web service endpoint. To test the sayHello method of HelloServiceBean, do the following:Open the Admin Console by opening the following URL in a web browser:


    http://localhost:4848/
  2. Enter the admin username and password to log in to the Admin Console.

  3. Click Web Services in the left pane of the Admin Console.

  4. Click helloservice.

  5. Click Test.

  6. Under Methods, enter a name as the parameter to the sayHello method.

  7. Click the sayHello button.

    This will take you to the sayHello Method invocation page.

  8. Under Method returned, you’ll see the response from the endpoint.

Using the Timer Service

Applications that model business work flows often rely on timed notifications. The timer service of the enterprise bean container enables you to schedule timed notifications for all types of enterprise beans except for stateful session beans. You can schedule a timed notification to occur at a specific time, after a duration of time, or at timed intervals. For example, you could set timers to go off at 10:30 AM on May 23, in 30 days, or every 12 hours.

When a timer expires (goes off), the container calls the method annotated @Timeout in the bean’s implementation class. The @Timeout method contains the business logic that handles the timed event.

The Timeout Method

Methods annotated @Timeout in the enterprise bean class must return void and take a javax.ejb.Timer object as the only parameter. They may not throw application exceptions.

@Timeout
public void timeout(Timer timer) {
    System.out.println("TimerBean: timeout occurred");
}

Creating Timers

To create a timer, the bean invokes one of the createTimer methods of the TimerService interface. (For details on the method signatures, see the TimerService API documentation at http://java.sun.com/javaee/5/docs/api/javax/ejb/TimerService.html.) When the bean invokes createTimer, the timer service begins to count down the timer duration.

The bean described in The timersession Example creates a timer as follows:

Timer timer = timerService.createTimer(intervalDuration,
        "Created new timer");

In the timersession example, createTimer is invoked in a business method, which is called by a client.

Timers are persistent. If the server is shut down (or even crashes), timers are saved and will become active again when the server is restarted. If a timer expires while the server is down, the container will call the @Timeout method when the server is restarted.

The Date and long parameters of the createTimer methods represent time with the resolution of milliseconds. However, because the timer service is not intended for real-time applications, a callback to the @Timeout method might not occur with millisecond precision. The timer service is for business applications, which typically measure time in hours, days, or longer durations.

Canceling and Saving Timers

Timers can be canceled by the following events:

If a method is invoked on a canceled timer, the container throws the javax.ejb.NoSuchObjectLocalException.

To save a Timer object for future reference, invoke its getHandle method and store the TimerHandle object in a database. (A TimerHandle object is serializable.) To re-instantiate the Timer object, retrieve the handle from the database and invoke getTimer on the handle. A TimerHandle object cannot be passed as an argument of a method defined in a remote or web service interface. In other words, remote clients and web service clients cannot access a bean’s TimerHandle object. Local clients, however, do not have this restriction.

Getting Timer Information

In addition to defining the cancel and getHandle methods, the Timer interface defines methods for obtaining information about timers:

public long getTimeRemaining();
public java.util.Date getNextTimeout();
public java.io.Serializable getInfo();

The getInfo method returns the object that was the last parameter of the createTimer invocation. For example, in the createTimer code snippet of the preceding section, this information parameter is a String object with the value created timer.

To retrieve all of a bean’s active timers, call the getTimers method of the TimerService interface. The getTimers method returns a collection of Timer objects.

Transactions and Timers

An enterprise bean usually creates a timer within a transaction. If this transaction is rolled back, the timer creation is also rolled back. Similarly, if a bean cancels a timer within a transaction that gets rolled back, the timer cancellation is rolled back. In this case, the timer’s duration is reset as if the cancellation had never occurred.

In beans that use container-managed transactions, the @Timeout method usually has the Required or RequiresNew transaction attribute to preserve transaction integrity. With these attributes, the EJB container begins the new transaction before calling the @Timeout method. If the transaction is rolled back, the container will call the @Timeout method at least one more time.

The timersession Example

The source code for this example is in the tut-install/javaeetutorial5/examples/ejb/timersession/timersession-ejb/src/java/ directory.

TimerSessionBean is a stateless session bean that shows how to set a timer. In the source code listing of TimerSessionBean that follows, note the createTimer and @Timeout methods. Because it’s a business method, createTimer is defined in the bean’s remote business interface (TimerSession) and can be invoked by the client. In this example, the client invokes createTimer with an interval duration of 30,000 milliseconds. The createTimer method creates a new timer by invoking the createTimer method of TimerService. A TimerService instance is injected by the container when the bean is created. Now that the timer is set, the EJB container will invoke the timeout method of TimerSessionBean when the timer expires, in about 30 seconds. Here’s the source code for the TimerSessionBean class:

package com.sun.tutorial.javaee.ejb;

import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerService;
@Stateless
public class TimerSessionBean implements TimerSession {
    @Resource
    TimerService timerService;

private static final Logger logger = Logger
        .getLogger("com.sun.tutorial.javaee.ejb.
                timersession.TimerSessionBean");

    public void createTimer(long intervalDuration) {
        Timer timer = timerService.createTimer(intervalDuration,
                "Created new timer");
    }

    @Timeout
    public void timeout(Timer timer) {
        logger.info("Timeout occurred");
    }
}

Note –

Application Server has a default minimum timeout value of 7000 milliseconds, or 7 seconds. If you need to set the timeout value lower than 7000 milliseconds, change the value of the minimum-delivery-interval-in-millis element in domain-dir/config/domain.xml. Due to virtual machine constraints, the lowest practical value for minimum-delivery-interval-in-millis is around 10 milliseconds.


Building, Packaging, Deploying, and Running the timersession Example

You can build, package, deploy, and run the timersession example using either NetBeans IDE or Ant.

Building, Packaging, Deploying, and Running the timersession Example Using NetBeans IDE

    Follow these instructions to build, package, and deploy the timersession example to your Application Server instance using the NetBeans IDE IDE.

  1. In NetBeans IDE, select File->Open Project.

  2. In the Open Project dialog, navigate to tut-install/javaeetutorial5/examples/ejb/.

  3. Select the timersession folder.

  4. Select the Open as Main Project and Open Required Projects check boxes.

  5. Click Open Project.

  6. Select Run->Run Main Project.

This builds and packages the application into timersession.ear, located in tut-install/javaeetutorial5/examples/ejb/timersession/dist/, deploys this EAR file to your Application Server instance, and then runs the application client.

You will see the output from the application client in the Output tab:


...
Creating a timer with an interval duration of 3000 ms.
run-timersession-app-client:
run-nb:
BUILD SUCCESSFUL (total time: 16 seconds)

    The output from the timer is sent to the server.log file located in the domain-dir/server/logs/ directory. To view this file:

  1. Click the Services tab.

  2. Right-click your Application Server instance and select View Server Log.

Look for the following line at the bottom of server.log:


Timeout occurred

Building, Packaging, and Deploying the timersession Example Using Ant

    Follow these instructions to build, package, and deploy the timersession example to your Application Server instance using Ant.

  1. In a terminal window, go to the tut-install/javaeetutorial5/examples/ejb/timersession/ directory.

  2. To build TimerSessionBean, type the following command:


    ant build
    

    This runs the default task, which compiles the source files and packages the application into an EAR file located at tut-install/examples/ejb/timersession/dist/timersession.ear.

  3. To deploy the application, type the following command:


    ant deploy
    

Running the timersession Application Client Using Ant

    To run the application client, perform the following steps.

  1. In a terminal window, go to the tut-install/javaeetutorial5/examples/ejb/timersession/ directory.

  2. Type the following command:


    ant run
    

    This task first retrieves the client JAR, timersessionClient.jar to the dist directory, and then runs the client. This is the equivalent of running:


    appclient -client TimerSessionAppClient.jar
    
  3. In the terminal window, the client displays these lines:


    Creating a timer with an interval duration of 30000 ms.

The output from the timer is sent to the server.log file located in the domain-dir/server/logs/ directory.

    View the output in the Admin Console:

  1. Open the Admin Console by opening the following URL in a web browser:


    http://localhost:4848/
  2. Enter the admin username and password to log in to the Admin Console.

  3. Click Application Server in the navigation pane.

  4. Click View Log Files.

  5. At the top of the page, you’ll see this line in the Message column:


    Timeout occurred

Alternatively, you can look at the log file directly. After about 30 seconds, open server.log in a text editor and you will see the following lines:


TimerSessionBean: Timeout occurred

Handling Exceptions

The exceptions thrown by enterprise beans fall into two categories: system and application.

A system exception indicates a problem with the services that support an application. Examples of these problems include the following: a connection to an external resource cannot be obtained or an injected resource cannot be found. If your enterprise bean encounters a system-level problem, it should throw a javax.ejb.EJBException. Because the EJBException is a subclass of the RuntimeException, you do not have to specify it in the throws clause of the method declaration. If a system exception is thrown, the EJB container might destroy the bean instance. Therefore, a system exception cannot be handled by the bean’s client program; it requires intervention by a system administrator.

An application exception signals an error in the business logic of an enterprise bean. Application exceptions are typically exceptions that you’ve coded yourself, such as the BookException thrown by the business methods of the CartBean example. When an enterprise bean throws an application exception, the container does not wrap it in another exception. The client should be able to handle any application exception it receives.

If a system exception occurs within a transaction, the EJB container rolls back the transaction. However, if an application exception is thrown within a transaction, the container does not roll back the transaction.