The Java EE 6 Tutorial, Volume I

Creating a Singleton Session Bean

The javax.ejb.Singleton annotation is used to specify that the enterprise bean implementation class is a singleton session bean.

@Singleton
public class SingletonBean { ... }

Initializing Singleton Session Beans

The EJB container is responsible for determining when to initialize a singleton session bean instance unless the singleton session bean implementation class is annotated with the javax.ejb.Startup annotation. This is sometimes called eager initialization. In this case, the EJB container must initialize the singleton session bean upon application startup. The singleton session bean is initialized before the EJB container delivers client requests to any enterprise beans in the application. This allows the singleton session bean to perform, for example, application startup tasks.


Example 16–1 An Eagerly Initialized Singleton Session Bean

The following singleton session bean stores the status of an application, and is eagerly initialized:

@Startup
@Singleton
public class StatusBean {
  private String status;

  @PostConstruct
  void init {
    status = "Ready";
  }
  ...
}

Sometimes multiple singleton session beans are used to initialize data for an application, and therefore must be initialized in a specific order. In these cases, use the javax.ejb.DependsOn annotation to declare the startup dependencies of the singleton session bean. The @DependsOn annotation's value attribute is one or more strings that specify the name of the target singleton session bean. If more than one dependent singleton bean is specifies in @DependsOn, the order that they are listed is not necessarily the order that the EJB container will initialize the target singleton session beans.


Example 16–2 Specifying the Ordering Of Singleton Session Bean Initialization

The following singleton session bean, PrimaryBean should be started up first:

@Singleton
public class PrimaryBean { ... }

SecondaryBean depends on PrimaryBean:

@Singleton
@DependsOn("PrimaryBean")
public class SecondaryBean { ... }

This guarantees that the EJB container will initialize PrimaryBean before SecondaryBean.



Example 16–3 Specifying Multiple Dependent Singleton Session Beans

The following singleton session bean, TertiaryBean, depends on PrimaryBean and SecondaryBean:

@Singleton
@DependsOn("PrimaryBean", "SecondaryBean")
public class TertiaryBean { ... }

SecondaryBean explicitly requires PrimaryBean to be initialized before it is initialized (through it's own @DependsOn annotation). In this case, the EJB container will first initialize PrimaryBean, then SecondaryBean, and finally TertiaryBean.

If, however, SecondaryBean did not explicitly depend on PrimaryBean, the EJB container may initialize either PrimaryBean or SecondaryBean first. That is, the EJB container could initialize the singletons in the following order: SecondaryBean, PrimaryBean, TertiaryBean.


Managing Concurrent Access in a Singleton Session Bean

Singleton session beans are designed for concurrent access, or situations where many clients need to access a single instance of a session bean at the same time. A singleton's client only needs a reference to a singleton in order to invoke any business methods exposed by the singleton, and doesn't need to worry about any other clients that may be simultaneously invoking business methods on the same singleton.

When creating a singleton session bean there are two ways of controlling concurrent access to the singleton's business methods: container-managed concurrency and bean-managed concurrency.

The javax.ejb.ConcurrencyManagement annotation is used to specify either container-managed or bean-managed concurrency for the singleton. @ConcurrencyManagement requires a type attribute to be set, one of javax.ejb.ConcurrencyManagementType.CONTAINER or javax.ejb.ConcurrencyManagementType.BEAN. If no @ConcurrencyManagement annotation is present on the singleton implementation class, the EJB container default of container-managed concurrency is used.

Container-Managed Concurrency

If a singleton uses container-managed concurrency, the EJB container controls client access to the business methods of the singleton. The javax.ejb.Lock annotation and a javax.ejb.LockType type are used to specify the access level of the singleton's business methods or @Timeout methods.

Annotate a singleton's business or timeout method using @Lock(READ) if the method can be concurrently accessed, or shared, with many clients. Annotate the business or timeout method with @Lock(WRITE) if the singleton session bean should be locked to other clients while a client is calling that method. Typically, the @Lock(WRITE) annotation is used when clients are modifying the state of the singleton.

Annotating a singleton class with @Lock specifies that all the business methods and any timeout methods of the singleton will use the specified lock type unless they explicitly set the lock type with a method-level @Lock annotation. If no @Lock annotation is present on the singleton class, the default lock type of @Lock(WRITE) is applied to all business and timeout methods.


Example 16–4 Specifying Container-Managed Concurrency in a Singleton Session Bean

The following example shows how to use the @ConcurrencyManagement, @Lock(READ), and @Lock(WRITE) annotations for a singleton that uses container-managed concurrency.

Although by default singletons use container-managed concurrency, the @ConcurrencyManagement(CONTAINER) annotation may be added at the class level of the singleton to explicitly set the concurrency management type.

@ConcurrencyManagement(CONTAINER)
@Singleton
public class ExampleSingletonBean {
  private String state;

  @Lock(READ)
  public String getState() {
    return state;
  }

  @Lock(WRITE)
  public void setState(String newState) {
    state = newState;
  }
}

The getState method can be accessed by many clients at the same time, because it is annotated with @Lock(READ). When the setState method is called, however, all the methods in ExampleSingletonBean will be locked to other clients because setState is annotated with @Lock(WRITE). This prevents two clients from attempting to simultaneously change the state variable of ExampleSingletonBean.



Example 16–5 Using Class- and Method-Level @Lock Annotations in a Singleton Session Bean

The getData and getStatus methods in the following singleton are of type READ, and the setStatus method is of type WRITE:

@Singleton
@Lock(READ)
public class SharedSingletonBean {
  private String data;
  private String status;

  public String getData() {
    return data;
  }

  public String getStatus() {
    return status;
  }

  @Lock(WRITE)
  public void setStatus(String newStatus) {
    status = newStatus;
  }
}

If a method is of locking type WRITE, client access to all the singleton's methods are blocked until the current client finishes its method call, or an access timeout occurs. When an access timeout occurs, the EJB container throws a javax.ejb.ConcurrentAccessTimeoutException. The javax.ejb.AccessTimeout annotation is used to specify the number of milliseconds before an access timeout occurs. If @AccessTimeout is added at the class level of a singleton, it specifies the access timeout value for all methods in the singleton unless a method explicitly overrides the default with its own @AccessTimeout annotation.

The @AccessTimeout annotation can be applied to both @Lock(READ) and @Lock(WRITE) methods.

@AccessTimeout has one required element, value, and one optional element, timeUnit. By default, the value is specified in milliseconds. To change the value unit, set timeUnit to one of the java.util.concurrent.TimeUnit constants: MICROSECONDS, MILLISECONDS, MICROSECONDS, or SECONDS.


Example 16–6 Setting the Access Timeout in a Singleton

The following singleton has a default access timeout value of 120,000 milliseconds, or 2 minutes. The doTediousOperation method overrides the default access timeout and sets the value to 360,000 milliseconds, or 6 minutes.

@Singleton
@AccessTimeout(value=120000)
public class StatusSingletonBean {
  private String status;

  @Lock(WRITE)
  public void setStatus(String new Status) {
    status = newStatus;
  }

  @Lock(WRITE)
  @AccessTimeout(value=360000)
  public void doTediousOperation {
    ...
  }
}


Example 16–7 Setting the Access Timeout in a Singleton in Seconds

The following singleton has a default access timeout value of 60 seconds, specified using the TimeUnit.SECONDS constant.

@Singleton
@AccessTimeout(value=60, timeUnit=SECONDS)
public class StatusSingletonBean { ... }

Bean-Managed Concurrency

Singletons that use bean-managed concurrency allow full concurrent access to all the business and timeout methods in the singleton. The developer of the singleton is responsible for ensuring that the state of the singleton is synchronized across all clients. Developers who create singletons with bean-managed concurrency are allowed to use the Java programming language synchronization primitives like synchronization and volatile to prevent errors during concurrent access.


Example 16–8 Specifying Bean-Managed Concurrency in a Singleton Session Bean

Add a @ConcurrencyManagement annotation at the class level of the singleton to specify bean-managed concurrency.

@ConcurrencyManagement(BEAN)
@Singleton
public class AnotherSingletonBean { ... }

Handling Errors in a Singleton Session Bean

If a singleton session bean encounters an error when it is initialized by the EJB container, that singleton instance will be destroyed.

Unlike other enterprise beans, once a singleton session bean instance is initialized it is not destroyed if the singleton's business or lifecycle methods cause system exceptions. This ensures that the same singleton instance is used throughout the application lifecycle.