| = Singleton Startup Ordering |
| :index-group: Session Beans |
| :jbake-type: page |
| :jbake-status: status=published |
| |
| This examples shows in practice the `@Startup` and `@DependsOn` annotations on `singleton` EJB's. |
| |
| |
| |
| === Run the tests |
| [source,java] |
| ---- |
| mvn clean test |
| ---- |
| |
| |
| |
| === The scenario |
| |
| * The example is composed by three singleton beans: `SingletonA`, `SingletonB`, `SingletonC`. |
| * The three EJB's contains a `@PostConstruct` annotation for the `init` method that is executed after dependency injection is done to perform any initialization. This method is invoked before the class is put into service. |
| * The `init` method store the name of the bean class that is been initialized in the `Supervisor` bean. |
| * The `Supervisor` bean is annotated with `@ApplicationScoped` to be able to share the list of bean names stored in the `records` attribute. |
| * `SingletonA` and `SingletonB` are annotated with `@Startup` which means they are going to initialized upon application startup. `SingletonC` will be initialized until the bean is going to be used in later injection point. |
| * `SingletonB` is annotated with `@DependsOn("SingletonA")` to enforce a initialization order with respect to `SingletonA`. |
| |
| |
| |
| `SingletonA.java`: Singleton EJB annotated with `@Startup`. It depends on the EJB `Supervisor`. |
| |
| [source,java] |
| ---- |
| package org.foo; |
| |
| 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`: Singleton EJB annotated with `@Startup` and `DependsOn`. It depends on the EJB `Supervisor`. |
| |
| [source,java] |
| ---- |
| package org.foo; |
| |
| 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`: Singleton EJB. It depends on the EJB `Supervisor`. |
| |
| [source,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`: Applicaiton scoped Bean that keep track of a list of Bean Names. |
| |
| [source,java] |
| ---- |
| import javax.enterprise.context.ApplicationScoped; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| @ApplicationScoped |
| public class Supervisor { |
| private final List<String> records = new ArrayList<>(); |
| |
| public void addRecord(String beanClass){ |
| records.add(beanClass); |
| } |
| |
| public String getRecord(){ |
| return records.toString(); |
| } |
| } |
| ---- |
| |
| |
| === The tests |
| |
| * The class `TestSingletonStartupOrder.java` contains two test that are executed in order via the annotation `@FixMethodOrder(MethodSorters.NAME_ASCENDING)` |
| * `firstTest`: assert true if and only if the records stored in the `Supervisor.record` are equals to `[SingletonA, 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`. |
| * `secondTest`: This test inject `SingletonC` as a parameter in the tests, therefore it asserts to true if and only if the records stored in the `Supervisor.record` are equals to `[SingletonA, SingletonB, SingletonC]` |
| |
| `TestSingletonStartupOrder.java` |
| [source,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/>"), "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]")); |
| } |
| } |
| ---- |
| |
| === About the Test architecture |
| |
| The test cases from this project are built using Arquillian and TomEE |
| Remote. The arquillian configuration can be found in |
| `src/test/resources/arquillian.xml` |