package org.jboss.resteasy.test.cdi.injection; import static org.junit.Assert.assertEquals; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Collection; import java.util.Iterator; import javax.annotation.Resource; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.GenericType; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.RunAsClient; import org.jboss.arquillian.junit.Arquillian; import org.jboss.resteasy.test.cdi.injection.resource.CDIInjectionBook; import org.jboss.resteasy.test.cdi.injection.resource.CDIInjectionBookBag; import org.jboss.resteasy.test.cdi.injection.resource.CDIInjectionBookBagLocal; import org.jboss.resteasy.test.cdi.injection.resource.CDIInjectionBookCollection; import org.jboss.resteasy.test.cdi.injection.resource.CDIInjectionBookReader; import org.jboss.resteasy.test.cdi.injection.resource.CDIInjectionBookResource; import org.jboss.resteasy.test.cdi.injection.resource.CDIInjectionBookWriter; import org.jboss.resteasy.test.cdi.injection.resource.CDIInjectionDependentScoped; import org.jboss.resteasy.test.cdi.injection.resource.CDIInjectionNewBean; import org.jboss.resteasy.test.cdi.injection.resource.CDIInjectionResourceProducer; import org.jboss.resteasy.test.cdi.injection.resource.CDIInjectionScopeInheritingStereotype; import org.jboss.resteasy.test.cdi.injection.resource.CDIInjectionScopeStereotype; import org.jboss.resteasy.test.cdi.injection.resource.CDIInjectionStatefulEJB; import org.jboss.resteasy.test.cdi.injection.resource.CDIInjectionStereotypedApplicationScope; import org.jboss.resteasy.test.cdi.injection.resource.CDIInjectionStereotypedDependentScope; import org.jboss.resteasy.test.cdi.injection.resource.CDIInjectionUnscopedResource; import org.jboss.resteasy.test.cdi.util.Constants; import org.jboss.resteasy.test.cdi.util.Counter; import org.jboss.resteasy.test.cdi.util.PersistenceUnitProducer; import org.jboss.resteasy.test.cdi.util.UtilityProducer; import org.jboss.resteasy.util.HttpResponseCodes; import org.jboss.resteasy.utils.PortProviderUtil; import org.jboss.resteasy.utils.TestUtil; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.asset.EmptyAsset; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.Assert; import org.junit.Test; import org.junit.BeforeClass; import org.junit.AfterClass; import org.junit.Before; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; /** * @tpSubChapter CDI * @tpChapter Integration tests * @tpTestCaseDetails This is a collection of tests addressed to the interactions of * Resteasy, CDI, EJB, and so forth in the context of a JEE Application Server. * It tests the injection of a variety of beans into Resteasy objects. * @tpSince RESTEasy 3.0.16 */ @RunWith(Arquillian.class) @RunAsClient public class InjectionTest extends AbstractInjectionTestBase { protected static final Logger log = LogManager.getLogger(InjectionTest.class.getName()); static Client client; static ParameterizedType BookCollectionType = new ParameterizedType() { @Override public Type[] getActualTypeArguments() { return new Type[]{CDIInjectionBook.class}; } @Override public Type getRawType() { return Collection.class; } @Override public Type getOwnerType() { return null; } }; public class InjectionCollection implements ParameterizedType { @Override public Type[] getActualTypeArguments() { return new Type[]{CDIInjectionBook.class}; } @Override public Type getRawType() { return Collection.class; } @Override public Type getOwnerType() { return null; } } private static int invocationCounter; @SuppressWarnings(value = "unchecked") @Deployment public static Archive<?> createTestArchive() { WebArchive war = TestUtil.prepareArchive(InjectionTest.class.getSimpleName()); war.addClass(AbstractInjectionTestBase.class) .addClasses(CDIInjectionBook.class, CDIInjectionBookResource.class, Constants.class, UtilityProducer.class, BookCollectionType.getClass()) .addClasses(Counter.class, CDIInjectionBookCollection.class, CDIInjectionBookReader.class, CDIInjectionBookWriter.class) .addClasses(CDIInjectionDependentScoped.class, CDIInjectionStatefulEJB.class, CDIInjectionUnscopedResource.class) .addClasses(CDIInjectionBookBagLocal.class, CDIInjectionBookBag.class) .addClasses(CDIInjectionNewBean.class) .addClasses(CDIInjectionScopeStereotype.class, CDIInjectionScopeInheritingStereotype.class) .addClasses(CDIInjectionStereotypedApplicationScope.class, CDIInjectionStereotypedDependentScope.class) .addClasses(Resource.class, CDIInjectionResourceProducer.class, PersistenceUnitProducer.class) .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml") .addAsResource(InjectionTest.class.getPackage(), "persistence.xml", "META-INF/persistence.xml"); return TestUtil.finishContainerPrepare(war, null, (Class<?>[]) null); } private String generateURL(String path) { return PortProviderUtil.generateURL(path, InjectionTest.class.getSimpleName()); } @BeforeClass public static void init() { client = ClientBuilder.newClient(); } @AfterClass public static void close() { client.close(); } @Before public void preparePersistenceTest() throws Exception { log.info("Dumping old records."); WebTarget base = client.target(generateURL("/empty/")); Response response = base.request().post(Entity.text(new String())); invocationCounter++; response.close(); } /** * @tpTestDetails Addresses the correct handling of built-in scopes. E.g. * 1) Providers are in the application scope, whether they are annotated or not. * 2) Resources are in the request scope, annotated or not. * 3) Objects in the dependent scope, when injected into JAX-RS objects, are handled properly. * 4) Singletons in the application scope, when injected in request scoped JAX-RS resources as * EJB proxies or Weld proxies, are handled properly. * A side effect of 3) and 4) is to test that beans managed by CDI (managed beans, singleton beans, * stateless EJBs) are injected properly into JAX-RS objects. * @tpSince RESTEasy 3.0.16 */ @Test public void testVerifyScopes() throws Exception { log.info("starting testVerifyScopes()"); WebTarget base = client.target(generateURL("/verifyScopes/")); Response response = base.request().get(); invocationCounter++; log.info("First status: " + response.getStatus()); assertEquals(HttpResponseCodes.SC_OK, response.getStatus()); response.close(); response = base.request().get(); invocationCounter++; log.info("Second status: " + response.getStatus()); assertEquals(HttpResponseCodes.SC_OK, response.getStatus()); response.close(); } /** * @tpTestDetails Addresses the injection of managed beans, singletons, and stateless EJBs into JAX-RS objects. * Uses a singleton (BookCollection) to interact with an EntityManager. * @tpSince RESTEasy 3.0.16 */ @Test public void testEJBs() throws Exception { log.info("starting testEJBs()"); // Create book. WebTarget base = client.target(generateURL("/create/")); CDIInjectionBook book1 = new CDIInjectionBook("RESTEasy: the Sequel"); Response response = base.request().post(Entity.entity(book1, "application/test+xml")); invocationCounter++; assertEquals(HttpResponseCodes.SC_OK, response.getStatus()); log.info("Status: " + response.getStatus()); int id1 = response.readEntity(int.class); log.info("Id of response book: " + id1); Assert.assertEquals(Counter.INITIAL_VALUE, id1); response.close(); // Create another book. base = client.target(generateURL("/create/")); CDIInjectionBook book2 = new CDIInjectionBook("RESTEasy: It's Alive"); response = base.request().post(Entity.entity(book2, "application/test+xml")); invocationCounter++; assertEquals(HttpResponseCodes.SC_OK, response.getStatus()); log.info("Status: " + response.getStatus()); int id2 = response.readEntity(int.class); log.info("Id of response book: " + id2); Assert.assertEquals(Counter.INITIAL_VALUE + 1, id2); response.close(); // Retrieve first book. base = client.target(generateURL("/book/" + id1)); response = base.request().accept("application/test+xml").get(); invocationCounter++; log.info("Status: " + response.getStatus()); assertEquals(HttpResponseCodes.SC_OK, response.getStatus()); CDIInjectionBook result = response.readEntity(CDIInjectionBook.class); log.info("Book from response: " + book1); Assert.assertEquals(book1, result); response.close(); // Retrieve second book. base = client.target(generateURL("/book/" + id2)); response = base.request().accept("application/test+xml").get(); invocationCounter++; log.info("Status: " + response.getStatus()); assertEquals(HttpResponseCodes.SC_OK, response.getStatus()); result = response.readEntity(CDIInjectionBook.class); log.info("Book from response: " + book2); Assert.assertEquals(book2, result); response.close(); // Retrieve all books. base = client.target(generateURL("/books")); response = base.request().accept(MediaType.APPLICATION_XML).get(); invocationCounter++; log.info("Status: " + response.getStatus()); @SuppressWarnings("unchecked") Collection<CDIInjectionBook> books = response.readEntity(new GenericType<>(BookCollectionType)); log.info("Collection from response: " + books); Assert.assertEquals(2, books.size()); Iterator<CDIInjectionBook> it = books.iterator(); CDIInjectionBook b1 = it.next(); CDIInjectionBook b2 = it.next(); log.info("First book in list: " + b1); log.info("Second book in list: " + b2); Assert.assertTrue(book1.equals(b1) && book2.equals(b2) || book1.equals(b2) && book2.equals(b1)); response.close(); // Test EntityManager injected in BookResource base = client.target(generateURL("/entityManager")); response = base.request().post(Entity.text(new String())); invocationCounter++; log.info("Status: " + response.getStatus()); assertEquals(HttpResponseCodes.SC_OK, response.getStatus()); response.close(); } /** * @tpTestDetails This test verifies that a session scoped SFSB survives throughout the course of a session and is * re-injected into the request scoped BookResource over the course of the session. Also, it is destroyed * and replaced when an invocation is made on BookResource after the session ends. * @tpSince RESTEasy 3.0.16 */ @Test public void testSessionScope() throws Exception { log.info("starting testSessionScope()"); client.close(); client = ClientBuilder.newClient(); // Need to supply each ClientRequest with a single ClientExecutor to maintain a single // cookie cache, which keeps the session alive. //ClientExecutor executor = new ApacheHttpClient4Executor(); // Create a book, which gets stored in the session scoped BookBag. WebTarget base = client.target(generateURL("/session/add/")); CDIInjectionBook book1 = new CDIInjectionBook(13, "Dead Man Napping"); Response response = base.request().post(Entity.entity(book1, Constants.MEDIA_TYPE_TEST_XML)); invocationCounter++; log.info("status: " + response.getStatus()); Assert.assertEquals(HttpResponseCodes.SC_OK, response.getStatus()); response.close(); // Create another book, which should get stored in the same BookBag. base = client.target(generateURL("/session/add/")); CDIInjectionBook book2 = new CDIInjectionBook(Counter.INITIAL_VALUE, "Dead Man Dozing"); response = base.request().post(Entity.entity(book2, Constants.MEDIA_TYPE_TEST_XML)); invocationCounter++; log.info("status: " + response.getStatus()); Assert.assertEquals(HttpResponseCodes.SC_OK, response.getStatus()); response.close(); // Get the current contents of the BookBag, and verify that it holds both of the books sent in the // previous two invocations. When this method is called, the session is terminated. base = client.target(generateURL("/session/get/")); response = base.request().get(); invocationCounter++; log.info("status: " + response.getStatus()); Assert.assertEquals(HttpResponseCodes.SC_OK, response.getStatus()); @SuppressWarnings("unchecked") Collection<CDIInjectionBook> books = response.readEntity(new GenericType<>(BookCollectionType)); log.info("Collection from response: " + books); Assert.assertEquals(2, books.size()); Iterator<CDIInjectionBook> it = books.iterator(); CDIInjectionBook b1 = it.next(); CDIInjectionBook b2 = it.next(); log.info("First book in list: " + b1); log.info("Second book in list: " + b2); Assert.assertTrue(book1.equals(b1) && book2.equals(b2) || book1.equals(b2) && book2.equals(b1)); response.close(); // Verify that the BookBag has been replaced by a new, empty one for the new session. base = client.target(generateURL("/session/test/")); response = base.request().post(Entity.text(new String())); invocationCounter++; log.info("status: " + response.getStatus()); Assert.assertEquals(HttpResponseCodes.SC_OK, response.getStatus()); response.close(); } /** * @tpTestDetails Tests the injection of JMS Producers, Consumers, and Queues using producer fields and methods. * @tpSince RESTEasy 3.0.16 */ @Test public void testJMS() throws Exception { log.info("starting testJMS()"); // Send a book title. WebTarget base = client.target(generateURL("/produceMessage/")); String title = "Dead Man Lounging"; CDIInjectionBook book = new CDIInjectionBook(23, title); Response response = base.request().post(Entity.entity(book, Constants.MEDIA_TYPE_TEST_XML)); invocationCounter++; log.info("status: " + response.getStatus()); log.info(response.readEntity(String.class)); Assert.assertEquals(HttpResponseCodes.SC_OK, response.getStatus()); response.close(); // Verify that the received book title is the one that was sent. base = client.target(generateURL("/queue/consumeMessage/")); log.info("consuming book"); response = base.request().get(); invocationCounter++; log.info("status: " + response.getStatus()); Assert.assertEquals(HttpResponseCodes.SC_OK, response.getStatus()); Assert.assertEquals(title, response.readEntity(String.class)); response.close(); } /** * @tpTestDetails Verifies that BookResource.postConstruct() and preDestroy() are called for each invocation. * @tpSince RESTEasy 3.0.16 */ @Test public void testPostConstructPreDestroy() throws Exception { log.info("starting testPostConstructPreDestroy()"); // Send a book title. log.info("invocationCounter: " + invocationCounter); WebTarget base = client.target(generateURL("/getCounters/")); Response response = base.request().get(); log.info("status: " + response.getStatus()); String result = response.readEntity(String.class); log.info("Response: " + result); log.info("InvocationCounter: " + invocationCounter); Assert.assertEquals(HttpResponseCodes.SC_OK, response.getStatus()); String[] counters = result.split(":"); Assert.assertTrue(invocationCounter + 1 == Integer.valueOf(counters[0])); // invocations of postConstruct() Assert.assertTrue(invocationCounter == Integer.valueOf(counters[1])); // invocations of preDestroy() response.close(); } /** * @tpTestDetails Verifies that ResourceProducer disposer method has been called for Queue. * @tpSince RESTEasy 3.0.16 */ @Test public void testDisposer() throws Exception { log.info("starting testDisposer()"); WebTarget base = client.target(generateURL("/disposer/")); Response response = base.request().get(); invocationCounter++; log.info("status: " + response.getStatus()); Assert.assertEquals(HttpResponseCodes.SC_OK, response.getStatus()); response.close(); } }