Session Beans

Session beans model high-level business processes, session beans utilize data and system resources to implement the application goals using business logic. In this topic I will go into details regarding session beans, continuing from my introduction on session beans in my EJB getting started topic.

Session beans capture the logic such as creating users, add items for an auction, bidding for an item, etc. A session is a connection between a client and a server that lasts for a finite period of time, it can be short-lived or span a long time. Session beans are the only EJB component that are invoked directly by the client, the client could be a web application component (servlet, JSP, JSF, etc), a commandline application or a Swing GUI application.

The following is what session beans can offer

Concurrency and Thread Safety The container will manage concurrency and thread safety automatically by using pooling, session management and passivation.
Remoting and Web Services Session beans support Remote Method Invocation (RMI) and Simple Object Access Protocol (SOAP) remote access which enables distributed computing and interoperability.
Transaction and Security Management Session beans offer fully transaction management and authorization and authentication security
Timer Services and Interceptors

Interceptors are EJB's version of lightweight Aspect-Oriented Programming (AOP), which has the ability to isolate "crosscutting" concerns into their own modules and apply them across the application through configuration, things like logging, auditing are repeated across an application but are not part of the business logic.

Timer services offer a "cron" type of scheduling.

The alternative option is to use Spring, this has advantages and disadvantages and could also be used in conjunction with EJB 3.

Session Bean Anatomy

Each session bean implementation has two distinct parts

To access a bean you must implement a business interface and interface-based programming is the practice of not using implementation classes directly whenever possible. This approach promotes loose coupling since implementation classes can easy be swapped out without alot of code changes.

The interface through which a client accesses the bean is called a business interface, the interface could be local, remote or even a web service, which means the bean can be polymorphic. Session beans are POJO's and since annotations are ignored by the JVM, session beans can be unit-tested using a framework like JUnit without having to deploy them to a container.

Session beans follow a number of rules

If you do not already know there are two types of session beans, stateless and stateful, the reason for this is if you require to save conversational information (like a shopping cart) between requests, thus a stateful bean will remember conversations between the client and the bean, where as stateless bean do not save conversation state.

Bean Lifecycle

A session has a lifecycle, it goes through a predefined set of state transitions, what this means is that the bean is a managed resource and its the Container who is the manager. It controls when the bean instance is created, when dependencies are injected, when the instance is destroyed or when to take optimization measures. By allowing the Container to take control it provides the abstraction that constitute some of the real value of using EJB's, including DI, automated transaction management, AOP, transparent security.

Depending on the bean type stateless or stateful a number of key lifecycle events can occur

Lifecycle callbacks are bean methods (not exposed by the business interface) that the container calls to notify the bean about a lifecycle transition or event. When an event occurs the Container can perform an action like initialization or cleanup resources. The callback methods are called using annotations like below

Annotation Type of EJB What used for
@PostConstruct Stateless, Stateful, MDB Invoke just after the bean instance is created and DI is complete
@PreDestroy Stateless, Stateful, MDB Invoked just before the bean is destroyed
@PrePassivate Stateful Invoke prior to a bean instance being passivated
@PostActivate Stateful Invoked after the bean instance is activated.

Stateless Beans

Stateless beans do not maintain conversation, and you normally use them to complete a business function is a single method call. Stateless beans are pooled which mean they are efficient, the container keeps a certain number of instances in the pool which are defined by you. Once the client has finished with an instance it is returned back to the pool for reuse.

A typical bean example might be the following, try not to worry about the application but focus on what we have discussed above (highlighted) and how use it below, I have also avoided using JPA for the time being as I don't want to make the code to heavy, so I will use JDBC instead, however I will have a number of topics dedicated to JPA.

BidManager Interface @Remote
public interface BidManager {

  void addBid(Bid bid);
  void cancelBid(Bid bid);
  List<Bid> getBids(Item item);
}
stateless bean @Stateless(name="BidManager")
public class BidManagerBean implements BidManager {

  @Resource(name="jdbc/ActionBazzarDS")
  private Datasource datasource;
  private Connection connection;
  ...
  // The no-argument constructor required by the container
  public BidManagerBean() {}


  @PostConstruct
  public void initialize() {
    try {
      connection = datasource.getConnection();
    } catch (SQLException sqle) {
        sqle.printStackTrace();
    }
  }

  public void addBid(Bid bid) {
    try {
      Long bidId = getBidId();
      Statement statement = connection.createStatement();
      statement.execute(
         "INSERT INTO BIDS ("
            + "BID_ID, "
            + "BID_AMOUNT, "
            + "BID_BIDDER_ID, "
            + "BID_ITEM_ID) "
            + "VALUES ("
            + bidId + ", "
            + bid.getAmount() + ", "
            + bid.getBidder().getUserId() + ", "
            + bid.getItem().getItemId() + ")");
    } catch (Exception e) {
         e.printStackTrace();
    }
  }

  @PreDestroy
  public void cleanup() {
    try {
      connection.close();
    } catch (SQLException sqle) {
         sqle.printStackTrace();
    }
  
    private Long getBid() { ... }

    public void cancelBid(Bid Bid) { ... }

    public List<Bid> getBids(Item item) { ... }
  }
}  

We program to an interface which is marked as remote (@Remote) , we declare the bean to be a stateless bean (@Stateless) and we use the resource annotation (@Resource) to perform injection of a JDBC data source. Notice the no-argument constructor which the container will use to create an instance of this EJB object. We have also included two callbacks @PostConstruct and @PreDestory to manage the JDBC connection. There are a couple of other methods that perform certain tasks. Note that you can have multiple @PostConstruct and @PreDestroy callbacks within the bean, they also cannot throw check exceptions.

The @Stateless annotation marks the POJO as a stateless session bean, this makes the container aware of the beans type, the specification of this annotation is

@Stateless annotation @Target(TYPE) @Retension(RUNTIME)
public @interface Stateless {
  String name() default "";
  String mappedName() default "";
  String description() default "";
}

You can give your bean a name that the container will use to reference it by binding it to the global JNDI tree, if no name is given it uses the class name.

Client applications can invoke a session bean in 3 different ways

Local interface
@Local
is designed for clients of stateless session beans collocated in the same container (JVM) instance. Local interfaces do not require any special measures in terms of either defining or implementing them
Remote interface
@Remote

Clients residing outside the container (another JVM) must use some kind of remote interface. The best way to access a remote EJB object is using Remote-Method Invocation (RMI), this API allows you call a method of a stateless bean across the network.

The only requirement of a remote stateless bean is that all parameters and return types of interface methods must be serializable. This is because only serializable objects can be sent across the network using RMI.

You can optionally extend the bean using the java.rmi.Remote, by extending you can then throw java.rmi.RemoteException

Web Services interface
@WebService

The web service endpoint interface (SEI) gives you the ability to expose a stateless bean as a SOAP-based web service.

They can only be used by stateless session beans.

You cannot mark the same interface with more than one access type annotation, however a business interface can extend another interface and you can remove code duplication by creating a business interface that has common methods and business interfaces that extend the common parent interface.

Implementing more than interface

public interface BidManager {
  void addBid(Bid bid);
  List<Bid> getBids(Item item);
}

@Local
public interface BidManagerLocal extends BidManager {
  void cancelBid(Bid bid);
}

@Remote
public interface BidManagerRemote extends BidManagerLocal {
}

@WebService
public interface BidManagerWS extends BidManagerRemote {
}

@Remote(BidManager.class)
@Stateless
public class BidManagerBean {
  ...
}

Stateless session beans have a simple lifecycle and the Container performs the following:

  1. Creates bean instances using the default constructor as needed
  2. Injects resources such as database connections
  3. Puts instances into a managed pool
  4. Pulls an idle bean from the pool when an invocation request is received from the client
  5. Executes the requested business method invoked through the business interface by the client
  6. When the business method finishes executing, pushes the bean back into the "method-ready" pool
  7. As needed retires (destroys) beans from the pool based on pool sizes

The above allows session beans to handle a large number of virtually concurrently clients.

Stateful Beans

Stateful beans are guaranteed to maintain conversational state, they are not programmatically very different from their stateless cousins. The container will make sure that the same client are handled by the same stateful bean bean instance. Stateful beans are not pulled from a pool, instead they are created and live until either requested to be removed or timeout. This has the affect that stateful beans can have significant footprint, however there is a technique that can be used to combat this problem called passivation.

Stateful session beans have additional rules

A stateful bean would be used if multiple data requires collection from the client, the user would enter username/password information, then biographical data (name, address, etc), then billing info after all the data has been collected would could create an account. The information collected would be stored in instance variables.

Interface

@Remote
public interface BidderAccountCreator {
  void addLoginInfo(LoginInfo loginInfo);

  void addBiographicalInfo(BiographicalInfo biographicalInfo) throws WorkflowOrderViolationException;

  void addBillingInfo(BillingInfo billingInfo) throws WorkflowOrderViolationException;

  void cancelAccountCreation();

  void createAccount();
}

stateful session bean example

@Stateful(name = "BidderAccountCreator")
public class BidderAccountCreatorBean implements BidderAccountCreator {

  @Resource(name = "jdbc/ActionBazaarDS", mappedName = "java:/DefaultDS")
  private DataSource dataSource;
  private Connection connection;

  // Used to store client conversational state across business method calls
  private LoginInfo loginInfo;
  private BiographicalInfo biographicalInfo;
  private BillingInfo billingInfo;

  // obtain resource connections just after being created or being activated
  @PostConstruct
  @PostActivate

  public void openConnection() {
    try {
      connection = dataSource.getConnection();
    } catch (SQLException sqle) {
        sqle.printStackTrace();
    }
  }

  // These methods are used to obtain the conversation data and stored in instance variables
  public void addLoginInfo(LoginInfo loginInfo) {
    this.loginInfo = loginInfo;
  }

  public void addBiographicalInfo(BiographicalInfo biographicalInfo)
    throws WorkflowOrderViolationException {
    if (loginInfo == null) {
      throw new WorkflowOrderViolationException("Login info must be set before biographical info");
    }

    this.biographicalInfo = biographicalInfo;
  }

  public void addBillingInfo(BillingInfo billingInfo)
    throws WorkflowOrderViolationException {
    if (biographicalInfo == null) {
      throw new WorkflowOrderViolationException("Biographical info must be set before billing info");
    }

    this.billingInfo = billingInfo;
  }

  @Remove
  public void cancelAccountCreation() {
    loginInfo = null;
    biographicalInfo = null;
    billingInfo = null;
  }

  @Remove
  public void createAccount() {
    try {
      Statement statement = connection.createStatement();
      String sql = "INSERT INTO BIDDERS (" + "username, " + "password, "
      + "first_name, " + "last_name, " + "credit_card_type, "
      + "account_number, " + "expiry_date" + ") VALUES (" + "'"
      + loginInfo.getUsername() + "', " + "'"
      + loginInfo.getPassword() + "', " + "'"
      + biographicalInfo.getFirstName() + "', " + "'"
      + biographicalInfo.getLastName() + "', " + "'"
      + billingInfo.getCreditCardType() + "', " + "'"
      + billingInfo.getAccountNumber() + "', " + "'"
      + billingInfo.getExpiryDate() + "'" + ")";
      statement.execute(sql);
      statement.close();
    } catch (SQLException sqle) {
        sqle.printStackTrace();
    }
  }

  // cleanup before either being destroyed or passivated   
  @PrePassivate
  @PreDestroy

  public void cleanup() {
    try {
      connection.close();
      connection = null;
    } catch (SQLException sqle) {
        sqle.printStackTrace();
    }
  }
}

Stateful session beans have a more complex lifecycle and the Container performs the following:

  1. Always creates new bean instances using the default constructor whenever a new client session is started
  2. Injects resources
  3. Stores the instance in memory
  4. Executes the requested business method invoked through the business interface by the client
  5. Waits for and executes subsequent client requests
  6. if idle for a period of time, container passivates the instance, normally moved out of memory onto disk (serialize the entire bean instance)
  7. If the client invokes a passivated bean, it is actived (brought back from disk to memory)
  8. if a method with the @Remove is invoked the bean is destroyed after the method completes.

Stateful and Stateless Differences

The table below details the differences between stateless and stateful beans

Features
Stateless
Stateful
Conversational state
No
Yes
Pooling
Yes
No
Performance problems
Unlikely
Possible
Lifecycle Events
PostConstruct
PreDestroy
PostConstruct
PreDestroy
PrePassivate
PostActivate
Timer
Yes
No
SessionSynchronization for transactions
No
Yes
Web Services
Yes
No
Extended PersistenceContext
No
Yes

Session Bean Clients

A session works for a client and can be invoked locally (inside the JVM) or remotely (outside the JVM), almost any Java component (POJO's, servlets, JSPs, EJBs) including non-Java components like .Net can access session beans. The client generally uses the below steps to access a session bean

  1. Obtain a reference to the beans directly or indirectly from JNDI
  2. Invocations are made through an interface appropriate for the access type
  3. Makes as many method calls as necessary
  4. In the case of a stateful bean, the last invocation should be a remove method

You can inject references to other beans using the @EJB annotation, you may have to use one of the two other options available for obtaining EJB references using either EJB context lookup or using JNDI lookup.

@EJB annotation

@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
public @interface EJB {
  String name() default "";
  Class beanInterface() default Object.class;
  String beanName() default "";
}

name = the JNDI name that is used to bind the injected EJB
beanInterface = specifies the business interface to use when accessing the bean
beanName = allows us to distinguish among other EJBs if multiple EJBs implement the same business interface

@EJB example @Stateless
public class GoldBidderManagerBean implements GoldBidderManager {

  // use the remote interface
  @EJB
  private BidManager bidManager;

  // using the local interface
  // @EJB(name="BidManagerLocal")
  // private BidManagerLocal bidManager;

  public void addMassBids(List<Bid> bids) {
    for (Bid bid : bids) {
      bidManager.addBid(bid);
    }
  }
}

You can inject a stateless bean into a stateful bean but you cannot inject a stateful bean into a stateless bean.

Performance

Deciding whether you maintain conversational state helps in deciding to choose either stateless or stateful session beans, remember that stateless beans are more efficient and that stateful beans can consume vast amounts of memory if not removed properly. Make sure that if using stateful session beans that you limit the amount of conversation data you store, just store what you need. Also bear in mind if using a cluster that stateful beans are replicated and use network bandwidth.

Certain containers allow you to adjust the passivation policies that can determine how long before to passivate a idle stateful bean, or when a JVM threshold has been reached. Remember to use the @Remove annotation to help the container remove the bean rather than letting it timeout.

Here are some more general tips