mvn clean test
Ordem de Inicialização Singleton
Estes exemplos mostram na prática as anotações @Startup
e @DependsOn
em EJB’s singleton
.
Executando os testes
O cenário
-
O exemplo é composto por três beans singleton :
SingletonA
,SingletonB
,SingletonC
. -
Os três EJB’s contém uma anotação
@PostConstruct
para o método `init`que é executado depois da injeção de dependência estar terminada, para otimizar qualquer inicialização. O método é invocado antes que classe seja colocada no serviço. -
O método
init
armazena o nome do classe bean que foi inicializada no beanSupervisor
. -
O bean
Supervisor
está anotado com@ApplicationScoped
para ser capaz de compartilhar a lista de nomes do bean armazenadas no atributorecords
. -
SingletonA
eSingletonB
são anotados com@Startup
o que siginifica, que eles serão inicializados na inicialização do aplicativo.SingletonC
será inicializado, até que o bean seja usado, no ponto de injeção posterior. -
SingletonB
é anotado com@DependsOn("SingletonA")
para obrigar uma inicialização ordenada com relação aoSingletonA
.
SingletonA.java
: Singleton EJB anotado com @Startup
. Depende do Supervisor
EJB.
package org.foo; import jakarta.annotation.PostConstruct; import jakarta.ejb.Singleton; import jakarta.ejb.Startup; import jakarta.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 anotado com @Startup
e DependsOn
. Depende do Supervisor
EJB.
package org.foo; import jakarta.annotation.PostConstruct; import jakarta.ejb.DependsOn; import jakarta.ejb.Singleton; import jakarta.ejb.Startup; import jakarta.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. Depende do Supervisor
EJB.
import jakarta.annotation.PostConstruct; import jakarta.ejb.Singleton; import jakarta.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
: Bean com escopo de aplicação que mantém uma lista de Nomes do Bean.
import jakarta.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(); } }
Os testes
-
A classe
TestSingletonStartupOrder.java
contem dois testes, que são executados em ordem, via a anotação@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-
primeiroTest
: afirmar verdadeiro se e somente, se os registros armazenados noSupervisor.record
forem iguais para[SingletonA, SingletonB]
. Observe que a ordem também é validada. Neste teste, nós não esperamos verSingletonC
inicializado, uma vez que não está anotado com@Startup
. -
segundoTest
: Este teste injetaSingletonC
como um parâmetro nos testes, portanto, é afirmado verdadeiro se e somente, se os registros armazenados noSupervisor.record
forem iguais para[SingletonA, SingletonB, SingletonC]
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/>"), "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]")); } }
Sobre a arquitetura de Teste
Os casos de teste deste projeto, estão construidos, usando Arquillian e TomEE
Remoto. A configuração arquillian pode ser encontrada em
src/test/resources/arquillian.xml
== APIs Used