Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migration of unmigrated content due to installation of a new plugin

...

...

Overview

For the first time in years EJB has a new bean type, the @Singleton. In my opinion, the As the name implies a javax.ejb.Singleton will replace a lot of what people are using @Stateless for today. The Singleton is essentially what you get if you take a Stateless bean and adjust the pool size to be exactly 1 resulting in there being exactly one instance of the Singleton bean in the application which can be invoked concurrently by multiple threads, like a servlet. It can do everything a Stateless can do such as support local and remote business interfaces, web services, security, transactions, and more. Additionally, the Singleton can get have its @PostConstruct method called with the application starts up and its @PreDestroy method called when the application shuts down. This allows it to serve as an application lifecycle listener which is something only Servlets could do before. It has an @Startup annotation which is similar in concept to the servlet <load-on-startup>, but unlike servlets it doesn't take a number as an argument. Instead, you can use an @DependsOn annotation to say which other Singletons you need and the container will ensure they start before you.

Concurrency

Singletons support two modes of concurrent access, Container-Managed Concurrency (the default) and Bean-Managed Concurrency.

With Bean-Managed Concurrency, annotated as @ConcurrencyManagement(BEAN), the container sends all invocations into the bean and lets the Singleton bean instance decide how and when to synchronize access, if at all. Here the 'synchronization' keyword is allowed as well as the full javax.util.concurrent set of libraries.

With Container-Managed Concurrency, annotated as @ConcurrencyManagement(CONTAINER), the container will enforce concurrency for you via locking method access to the bean. Two modes, called locks exist and can be assigned to both the bean class and methods of the bean class.

The first and the default is a "write" lock, annotated as @Lock(WRITE). Essentially with a write lock, the caller hold an exclusive lock on the bean for the duration of the method call and all other threads for that or any other method must wait.

The second option is a "read" lock, annotated as @Lock(READ). The read lock allows full concurrent access to the methods (assuming no write locks are held). The default mode of "write" essentially makes your bean a single-threaded bean, which is very slow. The more conservative @Lock(WRITE) as chosen as the default as this is how all the other bean types work (on a single thread may access a bean instance at any given time). Those that are aware of how to handle concurrent access can easily put @Lock(READ) on their bean class, thus changing the default, and then @Lock(WRITE) on specific methods if needed.

The locking modes of Container-Managed Concurrency map directly to the java.util.concurrent.ReadWriteLock API which looks like this:

Code Block
titlejava.util.concurrent.ReadWriteLock

public interface ReadWriteLock {
   /**
    * Returns the lock used for reading.
    *
    * @return the lock used for reading.
    */
   Lock readLock();

   /**
    * Returns the lock used for writing.
    *
    * @return the lock used for writing.
    */
   Lock writeLock();
}

Literally 100% of the Singleton locking we're talking about is taken from this interface. It's safe to imagine that under the covers the Singleton Container has an instance of ReadWriteLock which it uses to enforce the locking for all the Singleton bean's methods. Essentially:

  • @Lock(READ) == theSingletonReadWriteLock.readLock().lock()
  • @Lock(WRITE) == theSingletonReadWriteLock.writeLock().lock()

Startup and Startup Ordering

Singletons have an @Startup annotation which can be applied to the bean class. When used, the Container will instantiate the Singleton instance eagerly when the application starts up, otherwise the Container will instantiate the Singleton instance lazily when the bean is first accessed.

If one Singleton refers to another Singleton in the @PostConstruct or @PreDestroy method, there must be some measure taken to ensure the other Singleton exists and is started. This sort of ordering is achieved with the @DependsOn annotation which can be used to list the names of Singleton beans that must be started before the Singleton bean using the annotation.

Code Block

@DependsOn({"SingletonB", "SingletonC"})
@Singleton
public class SingletonA {

}

Circular references are not supported. If BeanA uses @DependsOn to point to BeanB and BeanB also uses @DependsOn to point at BeanA, the result is a deployment exception. Be aware that circular references can happen in less trivial ways such as A referring to B which refers to C which refers to D which refers back to A. We will detect and print all circular dependencies (called circuits) at deploy time.

Note that @DependsOn is only required (and should only be used) if a Singleton uses another Singleton in its @PostConstruct method or @PreDestroy method. Simply having a reference to another Singleton and using it in other business methods does not require an @DependsOn declaration. The @DependsOn allows the Container to calculate the correct startup order and shutdown order so that it can guarantee the Singletons you need are available in your @PostConstruct or @PreDestroy methods. All Singletons will automatically be available to your business methods regardless if @DependsOn is used. Because of the greater chance of creating circular dependencies, it is better not to use the @DependsOn annotation "just in case" and should only be used when truly needed.

XML and Annotation Overriding

Singletons can be declared in the ejb-jar.xml as follows:

...


    <session>
      <ejb-name>MySingletonBean</ejb-name>
      <ejb-class>org.superbiz.MySingletonBean</ejb-class>
      <session-type>Singleton</session-type>
      <load-on-startup>true</load-on-startup>
      <depends-on>
        <ejb-name>SingletonFoo</ejb-name>
        <ejb-name>SingletonBar</ejb-name>
      </depends-on>
    </session>

a session bean with a guarantee that there is at most one instance in the application.

What it gives you that is completely missing in EJB 3.0 and prior versions is the ability to have an EJB that is notified when the application starts and notified when the application stops. So you can do all sorts of things that you previously could only do with a load-on-startup servlet. It also gives you a place to hold data that pertains to the entire application and all users using it, without the need for a static. Additionally, Singleton beans can be invoked by several threads at one time similar to a Servlet.

See the Singleton Beans page for a full description of the javax.ejb.Singleton api.

The Code

PropertyRegistryBean

Here we see a bean that uses the Bean-Managed Concurrency option as well as the @Startup annotation which causes the bean to be instantiated by the container when the application starts. Singleton beans with @ConcurrencyManagement(BEAN) are responsible for their own thread-safety. The bean shown is a simple properties "registry" and provides a place where options could be set and retrieved by all beans in the application.

...

ComponentRegistryBean

Here we see a bean that uses the Container-Managed Concurrency option, the default. With @ConcurrencyManagement(CONTAINER) the container controls whether multi-threaded access should be allowed to the bean (@Lock(READ)) or if single-threaded access should be enforced (@Lock(WRITE)).

...

Unless specified explicitly on the bean class or a method, the default @Lock value is @Lock(WRITE). The code above uses the @Lock(READ) annotation on bean class to change the default so that multi-threaded access is granted by default. We then only need to apply the @Lock(WRITE) annotation to the methods that modify the state of the bean.

Essentially @Lock(READ) allows multithreaded access to the Singleton bean instance unless someone is invoking an @Lock(WRITE) method. With @Lock(WRITE), the thread invoking the bean will be guaranteed to have exclusive access to the Singleton bean instance for the duration of its invocation. This combination allows the bean instance to use data types that are not normally thread safe. Great care must still be used, though.

In the example we see ComponentRegistryBean using a java.util.HashMap which is not synchronized. To make this ok we do three things:

  1. Encapsulation. We don't expose the HashMap instance directly; including its iterators, key set, value set or entry set.
  2. We use @Lock(WRITE) on the methods that mutate the map such as the put() and remove() methods.
  3. We use @Lock(READ) on the get() and values() methods as they do not change the map state and are guaranteed not to be called at the same as any of the @Lock(WRITE) methods, so we know the state of the HashMap is no being mutated and therefore safe for reading.

The end result is that the threading model for this bean will switch from multi-threaded access to single-threaded access dynamically as needed depending on the which methods are being invoked. This gives Singletons a bit of an advantage over Servlets for processing multi-threaded requests.

See the Singleton Beans page for more advanced details on Container-Managed Concurrency.

Test Case

Example Code

...

...

Running

Running the example is fairly simple. In the "simple-singleton" directory run:

$ mvn clean install

Which should create output like the following.

...

Wiki Markup
{snippet:url=openejb3/examples/simple-singleton/src/test/java/org/superbiz/registry/PropertiesRegistryBeanTest.java|lang=java}

...