Developing singleton session beans

Create a bean implementation class for a singleton session bean, introduced by the Enterprise JavaBeans (EJB) 3.1 specification. The EJB container initializes only one instance of a singleton session bean, and that instance is shared by all clients. Because a single instance is shared by all clients, singleton session beans have special life cycle and concurrency semantics.

Before you begin

Make sure that you understand the inheritance rules for each annotation you implement. For example, the @ConcurrencyManagement annotation is coded on the singleton session bean class only. You cannot use the @ConcurrencyManagement annotation in the class that it extends, or any class higher in the class inheritance tree.

About this task

Singleton session beans can have business local, business remote, and web service client views; they cannot have EJB 2.1 local or remote client views. This singleton session bean support replaces the proprietary startup bean functionality, which has been deprecated.
The following example shows a basic singleton session bean:
public interface Configuration {
	Object get(String name);
	void set(String name, Object value);
}

@Singleton
public class ConfigurationBean implements Configuration {
	private Map<String, Object> settings = new HashMap<String, Object>();

	public Object get(String name) {
		return settings.get(name);
	}

	public void set(String name, Object value) {
		settings.put(name,value);
	}
}

As with other enterprise bean types, you can also declare metadata for singleton session beans in the deployment descriptor rather than using annotations; for example:

<?xml version="1.0"?>
<ejb-jar
  xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
  version="3.1"
>
  <enterprise-beans>
    <ejb-name>ConfigurationBean</ejb-name>
    <business-local>com.ibm.example.Configuration</business-local>
    <ejb-class>com.ibm.example.ConfigurationBean</ejb-class>
    <session-type>Singleton</session-type>
  </enterprise-beans>
</ejb-jar>

Procedure

  • Code the initialization and destruction methods, understanding how they relate to your options for setting the transaction context.
    During initialization, the instance is created, dependency injection occurs, and PostConstruct life cycle interceptor callbacks are started. The PreDestroy life cycle interceptor callbacks are started for a singleton session bean when its containing application is stopped.

    It can be useful to complete transactional activities during the PostConstruct and PreDestroy life cycle interceptor callbacks. For this reason, singleton session bean life cycle interceptor callbacks have a well-defined transaction context. The following transaction context values are similar to @Timeout methods: only REQUIRED (default), REQUIRES_NEW, and NOT_SUPPORTED can be used, and REQUIRED is translated to REQUIRES_NEW.

    Transaction attributes are only recognized when they are specified on life cycle interceptor methods on the bean class. The same transaction context is used for all life cycle interceptors. The following example illustrates a singleton session bean with transaction attributes specified for the PostConstruct and PreDestroy life cycle interceptor callbacks.

    @Singleton
    public class ConfigurationBean implements Configuration {
    	@PostConstruct
    	@TransactionAttribute(REQUIRED) // the default; specified for illustration
    	public void initialize() {
    		// ...
    	}
    
    	@PreDestroy
    	@TransactionAttribute(NOT_SUPPORTED)
    	public void destroy() {
    		// ...
    	}
    
    	// ...
    }

    Instead of using an annotation, you can specify the same metadata using the XML deployment descriptor. If you specify transaction attributes in the XML deployment descriptor, then any metadata obtained from the @TransactionAttribute annotation is ignored. The following example uses the XML deployment descriptor to specify the same metadata as the previous example.

    
      <assembly-descriptor>
        <container-transaction>
          <method>
            <ejb-name>ConfigurationBean</ejb-name>
            <method-name>initialize</method-name>
          </method>
          <trans-attribute>Required</trans-attribute>
        </container-transaction>
      
        <container-transaction>
          <method>
            <ejb-name>ConfigurationBean</ejb-name>
            <method-name>destroy</method-name>
          </method>
          <trans-attribute>NotSupported</trans-attribute>
        </container-transaction>
      </assembly-descriptor>
    
  • Unless otherwise specified, the singleton session bean instance is typically initialized when the bean is first used through one of its client views, which is the same as any other session bean.
    Use the @Startup annotation or the corresponding XML deployment descriptor to mark a bean as a startup bean. Marking a singleton bean as a startup bean means that the EJB container must run the PostConstruct method before it supports any external client requests made to the application to run. A PostConstruct method in a singleton bean can create an EJB timer, add a message to a JMS queue or topic, call an asynchronous EJB method, or initiate other asynchronous mechanisms that call an EJB. However, to avoid a deadlock, the PostConstruct method must not wait for an EJB timer to run, a message-driven bean method to be called, or an asynchronous EJB method to finish.

    Application developers can place business logic in the PostConstruct methods of these startup singleton instances to complete tasks that must be performed before any client work being started by the container, such as preloading caches or initiating asynchronous work within the application.

    The following example illustrates a singleton session bean with startup initialization:

    @Singleton
    @Startup
    public class ConfigurationBean implements Configuration {
    	@PostConstruct
    	public void initialize() {
    		// 1. Create the database table if it does not exist.
    		// 2. Initialize settings from the database table.
    		// 3. Load a cache.
    		// 4. Initiate asynchronous work (for example, work to a messaging queue or to
    		//    calls to asynchronous session bean methods.
    	}
    
    	// ...
    }

    Instead of using an annotation, you can specify the same metadata using the XML deployment descriptor. Specify true to mark this singleton bean as a startup singleton. Conversely, specify false, and the @Startup annotation is overridden, if it exists on the class file.

        <session>
          <ejb-name>ConfigurationBean</ejb-name>
          <init-on-startup>true</init-on-startup>
        </session>
  • Determine whether the initialization method of the singleton session bean has an implicit dependency on another singleton session bean.

    If an implicit dependency exists, use dependency metadata to make the dependency explicit. The container ensures that dependency singleton beans are initialized before their dependent beans are initialized and that they are destroyed after their dependent beans are destroyed. The following example illustrates a singleton session bean with dependency metadata:

    @Singleton
    public class DatabaseBean {
    	@PostConstruct
    	public void initialize() {
    		// Create database tables.
    	}
    }
    
    @Singleton
    @DependsOn({"DatabaseBean"})
    public class ConfigurationBean implements Configuration {
    	@PostConstruct
    	public void initialize() {
    		// Initialize settings from a database table.
    	}
    
    	// ...
    }

    Additionally, you can make cross-module dependencies by using the ejb-link module.jar#bean syntax. Circular dependencies are not supported, and cause an application to fail.

    Instead of using an annotation, you can specify the same metadata using the XML deployment descriptor. If you specify dependency metadata in the XML deployment descriptor, then any metadata from the @DependsOn annotation is ignored.

        <session>
          <ejb-name>ConfigurationBean</ejb-name>
          <depends-on>
            <ejb-name>DatabaseBean</ejb-name>
          </depends-on>
        </session>
  • Decide whether to use container-managed concurrency or bean-managed concurrency.
    The @Lock and @AccessTimeout annotations are not applicable when bean-managed concurrency is used.

    You can implement the @ConcurrencyManagement annotation on the singleton session bean class only. It cannot be used on the class it extends or any class higher in the class inheritance tree.

    The following code example illustrates a singleton with bean-managed concurrency:
    @Singleton
    @ConcurrencyManagement(BEAN)
    public class ConfigurationBean implements Configuration {
    	private Map<String, Object> settings = new HashMap<String, Object>();
    
    	synchronized public Object get(String name) {
    		return settings.get(name);
    	}
    
    	synchronized public void set(String name, Object value) {
    		settings.put(name, value);
    	}
    }
    

    Instead of using an annotation, you can specify the same metadata using the XML deployment descriptor. If the metadata is specified in both the XML deployment descriptor and using the @ConcurrencyManagement annotation, then the value must match or the application fails. The following example uses the XML deployment descriptor to specify the same metadata as the previous example.

        <session>
          <ejb-name>ConfigurationBean</ejb-name>
          <concurrency-management-type>Bean</concurrency-management-type>
        </session>

    The container does not do locking for each method called. Instead, the actual bean is responsible for the locking that is required. In the example, the bean provider has chosen to implement the methods using the synchronized keyword. This is supported for singleton session beans with bean-managed concurrency, but it is not supported for other EJB component types. It is not required for the bean provider to use the synchronized keyword to provide concurrency. For example, the bean provider can use the java.util.concurrent.locks.ReentrantReadWriteLock class that is found in JDK 5 and later.

    The locking semantics required by the EJB 3.1 specification for container-managed concurrency matches the behavior of the java.util.concurrent.locks.ReentrantReadWriteLock class.

  • If you are using container-managed concurrency, use the @Lock notation to manage concurrency of methods.
    The following code example illustrates singleton with container-managed concurrency.
    @Singleton
    public class ConfigurationBean implements Configuration {
    	private Map<String, Object> settings = new HashMap<String, Object>();
    
    	@Lock(READ)
    	public Object get(String name) {
    		return settings.get(name);
    	}
    
    	public void set(String name, Object value) {
    		settings.put(name, value);
    	}
    }
    

    Instead of using an annotation, you can specify the same metadata using the XML deployment descriptor. If the metadata is specified in both the XML deployment descriptor and the @Lock annotation, then the metadata obtained from the @Lock annotation is ignored. The following example uses the XML deployment descriptor to specify the same metadata as the previous example.

        <session>
          <ejb-name>ConfigurationBean</ejb-name>
          <concurrent-method>  
            <method>
              <method-name>get</method-name>
            </method>  		    
            <lock>Read</lock> 
          </concurrent-method> 
        </session>

    The example also illustrates annotating a method with @Lock(READ) to indicate that the container must get a read lock when that method is started. When a method is annotated with @Lock, it overrides the @Lock annotation that is specified at the class level. When there is no @Lock annotation at class level, the default is a write lock. In the example, the @Lock(READ) on the method is overriding the default write lock at the class level. When a method is not annotated at the method level and there is no annotation at the class level, the default of write lock is used by the container.

    Because most methods require a read lock, use @Lock(READ) at the class level to indicate that all business methods in this class require the container to obtain a read lock. For methods that require a write lock, annotate those methods with @Lock(WRITE) to show that it overrides the read lock that was specified at the class level.

    The following example illustrates this technique:

    @Singleton
    @Lock(READ)
    public class ConfigurationBean implements Configuration {
    	private Map<String, Object> settings = new HashMap<String, Object>();
    
    	public Object get(String name) {
    		return settings.get(name);
    	}
    
    	@Lock(WRITE)
    	public void set(String name, Object value) {
    		settings.put(name, value);
    	}
    }

    The @Lock annotation applies only to methods that are declared in the same class as the @Lock annotation. For a given class, the metadata for @Lock is never inherited from a class higher in the class inheritance tree. Instead of using an annotation at the class level, the same metadata can be specified in the XML deployment descriptor by using the special method-name *, which matches all methods.

  • If you are using container-managed concurrency, use the @AccessTimeout notation to limit how long a method waits for a lock to be granted.
    The following code example illustrates concurrent access timeouts.
    @Singleton
    public class ConfigurationBean implements Configuration {
    	@Lock(READ)
    	@AccessTimeout(1000)
    	public Object get(String name) {
    		// query the database
    	}
    
    	public void set(String name, Object value) {
    		// update the database
    	}
    }

    If no annotation is provided, the method defaults to wait until a lock is granted. There is no time limit for how long a client waits for the lock to be granted. Because nothing is coded at the class level, there is no wait time limit for a lock to be granted for all methods of the class. If the @AccessTimeout annotation is used and the container cannot grant the lock within the specified time limit, a javax.ejb.ConcurrentAccessTimeoutException is thrown to the client. The @AccessTimeout annotation applies only to methods that are declared in the same class as the @AccessTimeout annotation is in. For a given class, the metadata for the @AccessTimeout annotation is never inherited from a class higher in the class inheritance tree.

    Like the @Lock annotation, you can also specify the @AccessTimeout annotation using the XML deployment descriptor, and if you use the XML deployment descriptor, the metadata from the @AccessTimeout annotation is ignored. The following example uses the XML deployment descriptor to specify the same metadata as the previous example.

        <session>
          <ejb-name>ConfigurationBean</ejb-name>
          <concurrent-method>  
            <method>
              <method-name>get</method-name>
            </method>  		    
            <lock>Read</lock> 
            <access-timeout>
              <timeout>1000</timeout>
              <unit>Milliseconds</unit>
            </access-timeout> 
          </concurrent-method> 
        </session>
  • It is important to know that the XML coding of the concurrent-methodType follows the three styles outlined in the EJB specification for composing the XML for container-transaction method elements.

    Remember that both the lock and access-timeout elements are optional. The three styles are described as follows:

    Style 1 uses the special method name * to apply the lock type, access-timeout value, or both to all business methods for the specified bean.

    
    <!-- Example: Style 1 -->
    
          <concurrent-method>  
            <method>
              <method-name>*</method-name>
            </method>
            <lock>Read</lock>		    
            <access-timeout>
              <timeout>2000</timeout>
              <unit>Milliseconds</unit>
            </access-timeout> 
          </concurrent-method> 
                        

    Style 2 is used to refer to a business method with a specific name and assign it the specified lock type, access-timeout value, or both. If the method name is overloaded, meaning multiple methods have the same name but different method signatures, all methods with this name have the specified lock type, access-timeout value, or both. Style 2 takes precedence over style 1.

    
    <!-- Example: Style 2 -->
    
          <concurrent-method>  
            <method>
              <method-name>businessMethod</method-name>
            </method>
            <lock>Read</lock>		    
            <access-timeout>
              <timeout>2000</timeout>
              <unit>Milliseconds</unit>
            </access-timeout> 
          </concurrent-method> 
                        

    Style 3 is used to refer to a distinct method that matches the given method name and has a method signature which matches the method parameters listed. Style 3 takes precedence over both style 1 and style 2.

    
    <!-- Example: Style 3 -->
    
          <concurrent-method>  
            <method>
              <method-name>businessMethod</method-name>
              <method-params>
                <method-param>long</method-param>
                <method-param>int</method-param>
              </method-params>
            </method>
            <lock>Read</lock>		    
            <access-timeout>
              <timeout>2000</timeout>
              <unit>Milliseconds</unit>
            </access-timeout> 
          </concurrent-method> 
                        

    If style 1 XML is used to define a lock type, then all @Lock annotations on the bean are ignored. The same is true for access timeout. If style 1 XML is used to define an access timeout value then all @AccessTimeout annotations on the bean are ignored.

  • It is important to understand that the @Lock and @AccessTimeout annotations are treated independently of one another as is the corresponding XML deployment descriptor code for each.

    The implementation of this concept has multiple benefits. This separation of lock type and access timeout helps to prevent you from negatively affecting black box application code by not requiring you to know the lock type. You can safely leave the lock type value and adjust the access timeout value only to fit your environment needs, and avoid possible deadlock situations or other concurrency issues.

    Consider a scenario where you have a vendor supplied EJB application running and due to system slow down it keeps timing out. You do not want to change the locking logic for fear of causing a deadlock situation but you do want to modify the timeout limit. You can edit the deployment descriptor and specify the access timeout value you need for the methods you want to modify.

    The following example shows how you can specify just a method level @Lock(READ) annotation in the bean implementation and use style 2 for composing XML to specify the access-timeout element to be 2,000 milliseconds, not providing the optional lock element. The result is a method with a read lock that has an access timeout of 2,000 milliseconds.

    @Singleton
    public class ConfigurationBean implements Configuration {
    
    	@Lock(READ)
    	public Object businessMethod(long value) {
    		// ...
    	}
    
           // ...
    }
    
    
    
    
        <session>
          <ejb-name>ConfigurationBean</ejb-name>
          <concurrent-method>  
            <method>
              <method-name>businessMethod</method-name>
            </method>  		    
            <access-timeout>
              <timeout>2000</timeout>
              <unit>Milliseconds</unit>
            </access-timeout> 
          </concurrent-method> 
        </session>
    
    Similarly, you can use a class level lock annotation and then specify the wanted access timeout value in XML using either style 2, style 3, or both as shown in the following example.
    @Singleton
    @Lock(READ)
    public class ConfigurationBean implements Configuration {
    
    	public Object businessMethod(long value) {
    		// ...
    	}
    
    	public Object businessMethod(long value, int i, Object value) {
    		// ...
    	}
    
    	public Object businessMethod(long value, int i) {
    		// ...
    	}
    
    }
    
    
    
    
        <session>
          <ejb-name>ConfigurationBean</ejb-name>
          <concurrent-method>  
            <method>
              <method-name>businessMethod</method-name>
            </method>  		    
            <access-timeout>
              <timeout>2000</timeout>
              <unit>Milliseconds</unit>
            </access-timeout> 
          </concurrent-method> 
          <concurrent-method>  
            <method>
              <method-name>businessMethod</method-name>
              <method-params>
                <method-param>long</method-param>
                <method-param>int</method-param>
              </method-params>
            </method>  		    
            <access-timeout>
              <timeout>8000</timeout>
              <unit>Milliseconds</unit>
            </access-timeout> 
          </concurrent-method> 
        </session>
    
    

    The previous code example results in all methods named "businessMethod" having a lock type of read and an access timeout of 2,000 milliseconds. The exception is the one instance of the method "businessMethod" that has a method signature with the first parameter being of type long and the second being of type int, This instance of method "businessMethod" has a lock type of read, but has an access timeout of 8,000 milliseconds.

    The same principle applies when style 1 XML is used to define only a lock type, but not an access timeout value. You can add an access timeout value to a specific method or methods using style 2, style 3, or both to get a specific lock type and access timeout value result. The following example illustrates this point:
    
        <session>
          <ejb-name>ConfigurationBean</ejb-name>
          <concurrent-method>  
            <method>
              <method-name>*</method-name>
            </method>  		    
            <lock>Read</lock> 
          </concurrent-method> 
          <concurrent-method>  
            <method>
              <method-name>businessMethod</method-name>
            </method>  		    
            <access-timeout>
              <timeout>2000</timeout>
              <unit>Milliseconds</unit>
            </access-timeout> 
          </concurrent-method> 
        </session>
    
    

    The previous code example results in all business methods having a lock type of read and the method named, businessMethod, having a lock type of read and an access timeout of 2,000 milliseconds.

    You can also have a class level @Lock annotation to set the lock type for all methods and use style 1 for composing XML to set just the access timeout value for all methods. See the following example:
    @Singleton
    @Lock(READ)
    public class ConfigurationBean implements Configuration {
    
    	public Object businessMethod(long value) {
    		// ...
    	}
    
            // ...
    
    }
    
    
    
        <session>
          <ejb-name>ConfigurationBean</ejb-name>
          <concurrent-method>  
            <method>
              <method-name>*</method-name>
            </method>		    
            <access-timeout>
              <timeout>2000</timeout>
              <unit>Milliseconds</unit>
            </access-timeout> 
          </concurrent-method> 
        </session>
    
    

    The previous example results in all business methods of bean, ConfigurationBean, having a lock type of read and an access timeout value of 2,000 milliseconds.

  • Ensure that you understand the inheritance rules for the annotations that you have used.
  • Avoid reentrant locking behavior that can occur if a read lock method calls a write lock method in the same singleton session bean.

    Suppose the business method of a singleton session bean either directly or indirectly causes another business method of the singleton session bean to be started. If the first method is a write lock method, then that method can call any other business method of the singleton session bean without any special consideration. However, you might carefully implement a read lock method if it were to call another business method of that same singleton session bean class that is a write lock method. Then a javax.ejb.IllegalLoopbackException exception occurs.