Singleton EJB tutorial

If you need to make sure you are exposing an Enterprise Java  Bean as a Singleton, there is a simple approach which requires adding the @Singleton annotation on your EJB.
 
As the name implies a javax.ejb.Singleton is a session bean with a guarantee that there is at most one instance in the application. Until now if you wanted to expose a Service as Singleton you had to use either Service POJOS (See an article here JBoss MBeans POJO ) or tweak the EJB pool maximum size.

All of these approach worked, however they were not portable because they used JBoss annotations/configuration to do the trick.

Making an EJB as Singleton is pretty easy: just add the @java.ejb.Singleton annotation and that’s all.

Here’s an example:

@Singleton
@Startup
public class UserRegistry {

        public ArrayList<String> listUsers;
        @PostConstruct
        public void init() {
                listUsers = new ArrayList<String>();
                listUsers.add("administrator");

        }

    	public void addUser(String username) {
    		listUsers.add(username);
    	}
    	public void removeUser(String username) {
    		listUsers.remove(username);
    	}

        public ArrayList<String> getListUsers() {
            return listUsers;
    }
}

In this example, the EJB exposes some methods to manage an in-memory cache based on an ArrayList.

Notice also the @Startup annotation which is not mandatory but can be used to signal to the container to invoke the @PostConstruct method just after the Bean has been created. So, if you need a proper Bean initialization, just add a @Startup annotation at Class level and a @PostConstruct at method level.

Testing the Singleton EJB is pretty simple, here’s a Servlet client:

@WebServlet("/servlet")
public class ServletClient extends HttpServlet {

    @EJB  UserRegistry ejb;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");
        PrintWriter out = resp.getWriter();
        ejb.addUser("frank");
        out.println("List of users:")
        ArrayList<String> list = ejb.getListUsers();       
        for (String s:list) {
            out.println(s);
        }    	

    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

Please note that you won’t be able to look up remotely a Singleton EJB. If you need to access the Singleton EJB remotely you should include a wrapper Stateless Bean with a remote interface.

Managing concurrency in EJB singleton

Singleton session beans are designed for concurrent access, situations in which many clients need to access a single instance of a session bean at the same time.

There are two ways to manage concurrent access to Singleton EJBs. Let’s see them both.

Singleton Container-Managed Concurrency

If a singleton uses container-managed concurrency, the EJB container will manage access to the business methods of the singleton. This is the default if you don’t use any @ConcurrencyManagement annotation.

You can use the javax.ejb.Lock and javax.ejb.LockType annotation to specify the access level of the singleton’s business methods.

  • Annotate a method with @Lock(LockType.READ) if the method can be concurrently accessed.
  • Annotate a method with @Lock(LockType.WRITE) if the singleton session bean should be locked to other clients while a client is calling that method.

Typically, the @Lock(LockType.WRITE) annotation is used when clients are modifying the state of the singleton

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

@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
@Startup
public class UserRegistry {

        public ArrayList<String> listUsers;
        @PostConstruct
        public void init() {
                listUsers = new ArrayList<String>();
                listUsers.add("administrator");

        }

        @Lock(LockType.WRITE)
        @AccessTimeout(value=60, timeUnit=SECONDS)
    	public void addUser(String username) {
    		listUsers.add(username);
    	}
        @Lock(LockType.WRITE)
        @AccessTimeout(value=60, timeUnit=SECONDS)
    	public void removeUser(String username) {
    		listUsers.remove(username);
    	}
        @Lock(LockType.READ)
        public ArrayList<String> getListUsers() {
            return listUsers;
    }
}

You can specify how long the locks should be held by using the javax.ejb.AccessTimeout annotation.

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

The following excerpt shows how to apply a 1 minute timeout lock for some of your business methods:

@Lock(LockType.WRITE)
@AccessTimeout(value=60, timeUnit=SECONDS)
public void addUser(String username) {
    listUsers.add(username);
}
@Lock(LockType.WRITE)
@AccessTimeout(value=60, timeUnit=SECONDS)
public void removeUser(String username) {
    listUsers.remove(username);
}
Singleton 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 is now responsible for ensuring that the state of the singleton is synchronized across all clients. Typically, if you want to code EJB singletons with bean-managed concurrency, you should use synchronization primitives, such as synchronization and volatile, to prevent errors during concurrent access.

Add a @ConcurrencyManagement annotation with the type set to ConcurrencyManagementType.BEAN at the class level of the singleton to specify bean-managed concurrency:

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

Handling failures in a Singleton Session Bean

If an EJB singleton encounters an error when initialized by the EJB container, that singleton instance will be destroyed and you won’t be able to access it.

However, once it is initialized, a singleton EJB, 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.

Source code for this tutorial: https://github.com/fmarchioni/mastertheboss/tree/master/ejb/ejb-singleton

Found the article helpful? if so please follow us on Socials