Tomitribe support customers often use Java Singleton EJBs, and while powerful the sequence of startup and initialize can be confusing. This tutorial will help explain how Singleton EJB’s can be eagerly initialized at application startup and how we can define a priority during this process.

Review EJB Annotations

Before jumping into the code let’s do a recap of the following annotations used in this blog post:

@Singleton Component-defining annotation for a singleton session bean.

Component-defining annotation for a singleton session bean. @Startup Marks a singleton bean for eager initialization during the application startup sequence.

Marks a singleton bean for eager initialization during the application startup sequence. @DependsOn Used to express an initialization dependency between singleton components. The container ensures that all singleton beans with which a singleton has a DependsOn relationship have been initialized before the singleton’s PostConstruct method is called.

Used to express an initialization dependency between singleton components. The container ensures that all singleton beans with which a singleton has a relationship have been initialized before the singleton’s method is called. @PostConstruct Used on a method that needs to be executed after dependency injection is done to perform any initialization. This method must be invoked before the class is put into service.

ApplicationScoped Specifies that a bean is application scoped.

Life cycle of a singleton EJB

Now let’s refresh our memory with the life cycle of a Singleton before it gets ready to execute some business logic:

Figure 1

Figure 1 depicts how the EJB container checks for Dependency Injection and then for a @PostConstruct annotation in order to initialize the Singleton before its methods get invoked by a caller bean.

Singleton Session Bean Example

The Singleton Startup Ordering example has three Singleton beans: SingletonA , SingletonB , and SingletonC . Each singleton has a method called init annotated with @PostConstruct .

The init method stores the singleton class name in the List records from an ApplicationScoped Bean called Supervisor . Figure 2 depicts this process:

Figure 2

SingletonA and SingletonB are annotated with @Startup which means they are going to be initialized upon application startup.

and are annotated with which means they are going to be initialized upon application startup. SingletonC will not be initialized until the bean is invoked in the application.

will not be initialized until the bean is invoked in the application. SingletonB is annotated with @DependsOn("SingletonA") to enforce an initialization order with respect to SingletonA .

SingletonA.java

import javax.annotation.PostConstruct; import javax.ejb.Singleton; import javax.ejb.Startup; import javax.inject.Inject; import java.util.logging.Logger; @Singleton @Startup public class SingletonA { @Inject Supervisor supervisor; private final static Logger LOGGER = Logger.getLogger(SingletonA.class.getName()); @PostConstruct public void init() { LOGGER.info("Hi from init in class: " + this.getClass().getName()); supervisor.addRecord(this.getClass().getSimpleName()); } }

SingletonB.java

import javax.annotation.PostConstruct; import javax.ejb.DependsOn; import javax.ejb.Singleton; import javax.ejb.Startup; import javax.inject.Inject; import java.util.logging.Logger; @Singleton @Startup @DependsOn("SingletonA") public class SingletonB { @Inject Supervisor supervisor; private final static Logger LOGGER = Logger.getLogger(SingletonB.class.getName()); @PostConstruct public void init() { LOGGER.info("Hi from init in class: " + this.getClass().getName()); supervisor.addRecord(this.getClass().getSimpleName()); } }

SingletonC.java

import javax.annotation.PostConstruct; import javax.ejb.Singleton; import javax.inject.Inject; import java.util.logging.Logger; @Singleton public class SingletonC { @Inject Supervisor supervisor; private final static Logger LOGGER = Logger.getLogger(SingletonC.class.getName()); @PostConstruct public void init() { LOGGER.info("Hi from init in class: " + this.getClass().getName()); supervisor.addRecord(this.getClass().getSimpleName()); } public String hello() { return "Hello from SingletonC.class"; } }

Supervisor.java

import javax.enterprise.context.ApplicationScoped; import java.util.ArrayList; import java.util.List; @ApplicationScoped public class Supervisor { private final List records = new ArrayList<>(); public void addRecord(String beanClass){ records.add(beanClass); } public String getRecord(){ return records.toString(); } }

Example test explained

The class TestSingletonStartupOrder.java contains two tests that are executed in alphabetical order via the annotation @FixMethodOrder(MethodSorters.NAME_ASCENDING) . For the test, we are going to use TomEE Arquillian and JUnit as our test framework.

TestSingletonStartupOrder.java

import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; import org.foo.SingletonA; import org.foo.SingletonB; import org.foo.SingletonC; import org.foo.Supervisor; import java.util.logging.Logger; import static junit.framework.TestCase.assertTrue; @RunWith(Arquillian.class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestSingletonStartupOrder { private final static Logger LOGGER = Logger.getLogger(TestSingletonStartupOrder.class.getName()); @Deployment() public static WebArchive createDeployment() { final WebArchive webArchive = ShrinkWrap.create(WebArchive.class, "test.war") .addClass(SingletonA.class) .addClass(SingletonB.class) .addClass(SingletonC.class) .addClass(Supervisor.class) .addAsWebInfResource(new StringAsset(""), "beans.xml"); return webArchive; } @Test public void firstTest(Supervisor supervisor) { LOGGER.info("SUPERVISOR: [" + supervisor.getRecord() + "]"); assertTrue(supervisor.getRecord().equals("[SingletonA, SingletonB]")); } @Test public void secondTest(Supervisor supervisor, SingletonC singletonC) { LOGGER.info(singletonC.hello()); LOGGER.info("SUPERVISOR: [" + supervisor.getRecord() + "]"); assertTrue(supervisor.getRecord().equals("[SingletonA, SingletonB, SingletonC]")); } }

The assertion in the firstTest() will be true only if the records stored in the Supervisor.records are SingletonA and SingletonB . Notice that the order is validated too. In this test, we don’t expect to see SingletonC initialized since it’s not annotated with @Startup . Figure 3 depicts the order of records after the test is executed.

Figure 3

The secondTest injects SingletonC as a parameter in the tests, therefore it’s init() method will be executed and Supervisor.records will now have an entry for SingletonC . Figure 4 depicts the order of records after the test is executed.

Figure 4

The following sequence Diagram depicts the test workflow:

When you execute the tests, the following INFO log messages appear:

Highlighted in blue you can see how during server startup both SingletonA and SingletonB are initialized.

you can see how during server startup both and are initialized. Highlighted in orange is the message from the firstTest method.

is the message from the method. Highlighted in green is the message from the secondTest method when SingletonC is initialized, the output from the SingletonC.hello() method and the status of Supervisor.records .

..

INFO – OpenWebBeans Container is starting…

INFO – OpenWebBeans Container has started, it took 898 ms.

INFO – Created Ejb(deployment-id=SingletonA, ejb-name=SingletonA, container=Default Singleton Container)

INFO – Created Ejb(deployment-id=SingletonB, ejb-name=SingletonB, container=Default Singleton Container)

INFO – Created Ejb(deployment-id=SingletonC, ejb-name=SingletonC, container=Default Singleton Container)

INFO – Hi from init in class: org.foo.SingletonA

INFO – Started Ejb(deployment-id=SingletonA, ejb-name=SingletonA, container=Default Singleton Container)

INFO – Hi from init in class: org.foo.SingletonB

INFO – Started Ejb(deployment-id=SingletonB, ejb-name=SingletonB, container=Default Singleton Container)

INFO – Started Ejb(deployment-id=SingletonC, ejb-name=SingletonC, container=Default Singleton Container)

INFO – Deployed Application(path=/Users/cesar/git/tomee/examples/singleton-startup-ordering/target/arquillian/0/test)

INFO – Using org.apache.myfaces.ee.MyFacesContainerInitializer

INFO – Using InjectionProvider org.apache.myfaces.spi.impl.CDIAnnotationDelegateInjectionProvider

INFO – ServletContext initialized.

INFO – MyFaces Core has started, it took [866] ms. INFO – SUPERVISOR: [[SingletonA, SingletonB]]

INFO – Hi from init in class: org.foo.SingletonC

INFO – Hello from SingletonC.class

INFO – SUPERVISOR: [[SingletonA, SingletonB, SingletonC]]

…

Conclusion