/* * JBoss, Home of Professional Open Source. * Copyright 2011, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.test.integration.ejb.pool.override; import static org.jboss.as.test.shared.integration.ejb.security.PermissionUtils.createPermissionsXmlAsset; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import javax.ejb.EJBException; import javax.naming.InitialContext; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; import org.jboss.as.arquillian.api.ServerSetup; import org.jboss.as.arquillian.api.ServerSetupTask; import org.jboss.as.arquillian.container.ManagementClient; import org.jboss.as.controller.client.helpers.ClientConstants; import org.jboss.as.test.integration.ejb.remote.common.EJBManagementUtil; import org.jboss.dmr.ModelNode; import org.jboss.logging.Logger; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; /** * Tests {@link org.jboss.ejb3.annotation.Pool} annotation usage and the <pool> * element usage in jboss-ejb3.xml for EJBs. * * @author Jaikiran Pai */ @RunWith(Arquillian.class) @ServerSetup({PoolOverrideTestCase.PoolOverrideTestCaseSetup.class, PoolOverrideTestCase.AllowPropertyReplacementSetup.class}) public class PoolOverrideTestCase { private static final Logger logger = Logger.getLogger(PoolOverrideTestCase.class); static class PoolOverrideTestCaseSetup implements ServerSetupTask { @Override public void setup(final ManagementClient managementClient, final String containerId) throws Exception { EJBManagementUtil.createStrictMaxPool(managementClient.getControllerClient(), PoolAnnotatedEJB.POOL_NAME, 1, 10, TimeUnit.MILLISECONDS); EJBManagementUtil.createStrictMaxPool(managementClient.getControllerClient(), PoolSetInDDBean.POOL_NAME_IN_DD, 1, 10, TimeUnit.MILLISECONDS); EJBManagementUtil.createStrictMaxPool(managementClient.getControllerClient(), PoolAnnotatedAndSetInDDBean.POOL_NAME, 1, 10, TimeUnit.MILLISECONDS); EJBManagementUtil.createStrictMaxPool(managementClient.getControllerClient(), PoolAnnotatedWithExpressionEJB.POOL_NAME, 1, 10, TimeUnit.MILLISECONDS); } @Override public void tearDown(final ManagementClient managementClient, final String containerId) throws Exception { EJBManagementUtil.removeStrictMaxPool(managementClient.getControllerClient(), PoolAnnotatedEJB.POOL_NAME); EJBManagementUtil.removeStrictMaxPool(managementClient.getControllerClient(), PoolSetInDDBean.POOL_NAME_IN_DD); EJBManagementUtil.removeStrictMaxPool(managementClient.getControllerClient(), PoolAnnotatedAndSetInDDBean.POOL_NAME); EJBManagementUtil.removeStrictMaxPool(managementClient.getControllerClient(), PoolAnnotatedWithExpressionEJB.POOL_NAME); } } static class AllowPropertyReplacementSetup implements ServerSetupTask { @Override public void setup(ManagementClient managementClient, String s) throws Exception { final ModelNode enableSubstitutionOp = new ModelNode(); enableSubstitutionOp.get(ClientConstants.OP_ADDR).set(ClientConstants.SUBSYSTEM, "ee"); enableSubstitutionOp.get(ClientConstants.OP).set(ClientConstants.WRITE_ATTRIBUTE_OPERATION); enableSubstitutionOp.get(ClientConstants.NAME).set("annotation-property-replacement"); enableSubstitutionOp.get(ClientConstants.VALUE).set(true); try { applyUpdate(managementClient, enableSubstitutionOp); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void tearDown(ManagementClient managementClient, String s) throws Exception { final ModelNode disableSubstitution = new ModelNode(); disableSubstitution.get(ClientConstants.OP_ADDR).set(ClientConstants.SUBSYSTEM, "ee"); disableSubstitution.get(ClientConstants.OP).set(ClientConstants.WRITE_ATTRIBUTE_OPERATION); disableSubstitution.get(ClientConstants.NAME).set("annotation-property-replacement"); disableSubstitution.get(ClientConstants.VALUE).set(false); try { applyUpdate(managementClient, disableSubstitution); } catch (Exception e) { throw new RuntimeException(e); } } private void applyUpdate(final ManagementClient managementClient, final ModelNode update) throws Exception { ModelNode result = managementClient.getControllerClient().execute(update); if (result.hasDefined(ClientConstants.OUTCOME) && ClientConstants.SUCCESS.equals(result.get(ClientConstants.OUTCOME).asString())) { } else if (result.hasDefined(ClientConstants.FAILURE_DESCRIPTION)) { final String failureDesc = result.get(ClientConstants.FAILURE_DESCRIPTION).toString(); throw new RuntimeException(failureDesc); } else { throw new RuntimeException("Operation not successful; outcome = " + result.get("outcome")); } } } @Deployment public static Archive createDeployment() { final JavaArchive jar = ShrinkWrap.create(JavaArchive.class, "ejb-pool-override-test.jar"); jar.addPackage(PoolAnnotatedEJB.class.getPackage()); jar.addAsManifestResource(PoolOverrideTestCase.class.getPackage(), "jboss-ejb3.xml", "jboss-ejb3.xml"); jar.addAsManifestResource(PoolOverrideTestCase.class.getPackage(), "jboss.properties", "jboss.properties"); jar.addAsResource(createPermissionsXmlAsset(new RuntimePermission("modifyThread")), "META-INF/jboss-permissions.xml"); return jar; } /** * Test that a stateless bean configured with the {@link org.jboss.ejb3.annotation.Pool} annotation * is processed correctly and the correct pool is used by the bean * * @throws Exception */ @Test public void testSLSBWithPoolAnnotation() throws Exception { final PoolAnnotatedEJB bean = InitialContext.doLookup("java:module/" + PoolAnnotatedEJB.class.getSimpleName()); this.testSimulatenousInvocationOnEJBsWithSingleInstanceInPool(bean); } @Test public void testSLSBWithPoolAnnotationWithExpression() throws Exception { final PoolAnnotatedWithExpressionEJB bean = InitialContext.doLookup("java:module/" + PoolAnnotatedWithExpressionEJB.class.getSimpleName()); this.testSimulatenousInvocationOnEJBsWithSingleInstanceInPool(bean); } /** * Test that a stateless bean configured with a pool reference in the jboss-ejb3.xml is processed correctly * and the correct pool is used by the bean * * @throws Exception */ @Test public void testSLSBWithPoolReferenceInDD() throws Exception { final PoolSetInDDBean bean = InitialContext.doLookup("java:module/" + PoolSetInDDBean.class.getSimpleName()); this.testSimulatenousInvocationOnEJBsWithSingleInstanceInPool(bean); } /** * Test that a stateless bean which has been annotated with a {@link org.jboss.ejb3.annotation.Pool} annotation * and also has a jboss-ejb3.xml with a bean instance pool reference, is processed correctly and the deployment * descriptor value overrides the annotation value. To make sure that the annotation value is overriden by the * deployment descriptor value, the {@link PoolAnnotatedAndSetInDDBean} is annotated with {@link org.jboss.ejb3.annotation.Pool} * whose value points to a non-existent pool name * * @throws Exception */ @Test public void testPoolConfigInDDOverridesAnnotation() throws Exception { final PoolAnnotatedAndSetInDDBean bean = InitialContext.doLookup("java:module/" + PoolAnnotatedAndSetInDDBean.class.getSimpleName()); this.testSimulatenousInvocationOnEJBsWithSingleInstanceInPool(bean); } /** * Submits 2 {@link Callable}s to a {@link java.util.concurrent.Executor} to invoke on the {@link AbstractSlowBean bean} * simulatenously. The bean is backed by a pool with <code>maxPoolSize</code> of 1 and the bean method "sleeps" * for 1 second. So the second invocation waits for the first to complete. The pool for these beans is configured * such that the instance acquisition timeout on the pool is (very) low compared to the time the bean method processing/sleep * time. As a result, the second invocation is expected to fail. This method just validates that the correct pool is being used * by the bean and not the default pool whose instance acquisition timeout is greater and these custom pools. * * @param bean The bean to invoke on * @throws Exception */ private void testSimulatenousInvocationOnEJBsWithSingleInstanceInPool(final AbstractSlowBean bean) throws Exception { final ExecutorService executorService = Executors.newFixedThreadPool(2); Future<Void> firstBeanInvocationResult = null; Future<Void> secondBeanInvocationResult = null; try { final PooledBeanInvoker firstBeanInvoker = new PooledBeanInvoker(bean, 1000); firstBeanInvocationResult = executorService.submit(firstBeanInvoker); final PooledBeanInvoker secondBeanInvoker = new PooledBeanInvoker(bean, 1000); secondBeanInvocationResult = executorService.submit(secondBeanInvoker); } finally { executorService.shutdown(); } boolean firstInvocationFailed = false; boolean secondInvocationFailed = false; try { firstBeanInvocationResult.get(5, TimeUnit.SECONDS); } catch (ExecutionException ee) { if (ee.getCause() instanceof EJBException) { logger.trace("Got EJBException for first invocation ", ee.getCause()); firstInvocationFailed = true; } else { throw ee; } } try { secondBeanInvocationResult.get(5, TimeUnit.SECONDS); } catch (ExecutionException ee) { if (ee.getCause() instanceof EJBException) { logger.trace("Got EJBException for second invocation ", ee.getCause()); secondInvocationFailed = true; } else { throw ee; } } // if both failed, then it's an error if (firstInvocationFailed && secondInvocationFailed) { Assert.fail("Both first and second invocations to EJB failed. Only one was expected to fail"); } // if none failed, then it's an error too if (!firstInvocationFailed && !secondInvocationFailed) { Assert.fail("Both first and second invocations to EJB passed. Only one was expected to pass"); } } /** * Invokes the {@link AbstractSlowBean#delay(long)} bean method */ private class PooledBeanInvoker implements Callable<Void> { private AbstractSlowBean bean; private long beanProcessingTime; PooledBeanInvoker(final AbstractSlowBean bean, final long beanProcessingTime) { this.bean = bean; this.beanProcessingTime = beanProcessingTime; } @Override public Void call() throws Exception { this.bean.delay(this.beanProcessingTime); return null; } } }