/* * JBoss, Home of Professional Open Source. * Copyright (c) 2012, 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.management.deployments; import java.io.IOException; import java.util.Hashtable; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import javax.naming.Context; import javax.naming.InitialContext; 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.arquillian.junit.InSequence; import org.jboss.as.arquillian.api.ContainerResource; import org.jboss.as.arquillian.api.ServerSetup; import org.jboss.as.arquillian.container.ManagementClient; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.ejb3.subsystem.EJB3Extension; import org.jboss.as.ejb3.subsystem.deployment.EJBComponentType; import org.jboss.as.test.shared.TimeoutUtil; import org.jboss.dmr.ModelNode; import org.jboss.dmr.Property; import org.jboss.shrinkwrap.api.Archive; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM; import static org.jboss.as.test.integration.ejb.management.deployments.EjbJarRuntimeResourceTestBase.MODULE_NAME; import static org.jboss.as.test.integration.ejb.management.deployments.EjbJarRuntimeResourceTestBase.componentAddress; import static org.jboss.as.test.integration.ejb.management.deployments.EjbJarRuntimeResourceTestBase.execute; import static org.jboss.as.test.integration.ejb.management.deployments.EjbJarRuntimeResourceTestBase.executeOperation; import static org.jboss.as.test.integration.ejb.management.deployments.EjbJarRuntimeResourceTestBase.getEJBJar; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** * Tests whether the invocation statistics actually make sense. * * @author <a href="mailto:cdewolf@redhat.com">Carlo de Wolf</a> */ @RunWith(Arquillian.class) @RunAsClient @ServerSetup(EjbInvocationStatisticsTestCaseSetup.class) public class EjbInvocationStatisticsTestCase { @ContainerResource private ManagementClient managementClient; private Boolean statisticsEnabled; private static InitialContext context; @After public void after() throws IOException { setEnableStatistics(managementClient, statisticsEnabled); } @Before public void before() throws IOException { statisticsEnabled = getEnableStatistics(managementClient); setEnableStatistics(managementClient, true); assertEquals(Boolean.TRUE, getEnableStatistics(managementClient)); } @BeforeClass public static void beforeClass() throws Exception { final Hashtable props = new Hashtable(); props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming"); context = new InitialContext(props); } @Deployment public static Archive<?> deployment() { return getEJBJar(); } private static Boolean getEnableStatistics(final ManagementClient managementClient) throws IOException { final ModelNode address = PathAddress.pathAddress(PathElement.pathElement(SUBSYSTEM, EJB3Extension.SUBSYSTEM_NAME)).toModelNode(); address.protect(); final ModelNode value = readAttribute(managementClient, address, "enable-statistics"); if (value.isDefined()) return value.asBoolean(); return null; } private static ModelNode readAttribute(final ManagementClient managementClient, final ModelNode address, final String attributeName) throws IOException { final ModelNode op = new ModelNode(); op.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.READ_ATTRIBUTE_OPERATION); op.get(ModelDescriptionConstants.OP_ADDR).set(address); op.get(ModelDescriptionConstants.NAME).set(attributeName); return execute(managementClient, op); } private static void setEnableStatistics(final ManagementClient managementClient, final Boolean enableStatistics) throws IOException { final ModelNode address = PathAddress.pathAddress(PathElement.pathElement(SUBSYSTEM, EJB3Extension.SUBSYSTEM_NAME)).toModelNode(); address.protect(); if (enableStatistics == null) undefineAttribute(managementClient, address, "enable-statistics"); else writeAttributeBoolean(managementClient, address, "enable-statistics", enableStatistics); } @Test public void testSingleton() throws Exception { validateBean(EJBComponentType.SINGLETON, ManagedSingletonBean.class); } @Test //TODO Elytron - ejb-client4 integration public void testSFSB() throws Exception { validateBean(EJBComponentType.STATEFUL, ManagedStatefulBean.class); } @Test public void testSLSB() throws Exception { validateBean(EJBComponentType.STATELESS, ManagedStatelessBean.class); } @Test @InSequence(1) // this needs to run after testSingleton because testSingleton doesn't expect any previously made invocations public void testSingletonWaitTime() throws Exception { validateWaitTimeStatistic(EJBComponentType.SINGLETON, ManagedSingletonBean.class); } /* Invoke the singleton bean multiple times at once. The wait-time statistic should show a number greater than zero, because the singleton uses a write lock. */ private void validateWaitTimeStatistic(final EJBComponentType type, final Class<?> beanClass) throws Exception { final String name = beanClass.getSimpleName(); final BusinessInterface bean = (BusinessInterface) context.lookup("ejb:/" + MODULE_NAME + "//" + name + "!" + BusinessInterface.class.getName() + (type == EJBComponentType.STATEFUL ? "?stateful" : "")); final Runnable invocationRunnable = new Runnable() { @Override public void run() { bean.doIt(); } }; // perform 4 invocations at once ExecutorService threadPool = Executors.newFixedThreadPool(4); for(int i=0; i<4; i++) { threadPool.submit(invocationRunnable); } threadPool.shutdown(); threadPool.awaitTermination(TimeoutUtil.adjust(25), TimeUnit.SECONDS); // check the wait-time statistic final ModelNode address = componentAddress(EjbJarRuntimeResourcesTestCase.BASE_ADDRESS, type, name).toModelNode(); address.protect(); { final ModelNode result = executeOperation(managementClient, ModelDescriptionConstants.READ_RESOURCE_OPERATION, address); assertTrue(result.get("wait-time").asLong() > 0L); } } private void validateBean(final EJBComponentType type, final Class<?> beanClass) throws Exception { final String name = beanClass.getSimpleName(); final ModelNode address = componentAddress(EjbJarRuntimeResourcesTestCase.BASE_ADDRESS, type, name).toModelNode(); address.protect(); { final ModelNode result = executeOperation(managementClient, ModelDescriptionConstants.READ_RESOURCE_OPERATION, address); assertEquals(0L, result.get("execution-time").asLong()); assertEquals(0L, result.get("invocations").asLong()); assertEquals(0L, result.get("peak-concurrent-invocations").asLong()); assertEquals(0L, result.get("wait-time").asLong()); assertEquals(0L, result.get("methods").asInt()); if (type.equals(EJBComponentType.STATEFUL)) { assertEquals(0L, result.get("cache-size").asLong()); assertEquals(0L, result.get("passivated-count").asLong()); assertEquals(0L, result.get("total-size").asLong()); } } final BusinessInterface bean = (BusinessInterface) context.lookup("ejb:/" + MODULE_NAME + "//" + name + "!" + BusinessInterface.class.getName() + (type == EJBComponentType.STATEFUL ? "?stateful" : "")); bean.doIt(); { ModelNode result = executeOperation(managementClient, ModelDescriptionConstants.READ_RESOURCE_OPERATION, address); assertTrue(result.get("execution-time").asLong() >= 50L); assertEquals(1L, result.get("invocations").asLong()); assertEquals(1L, result.get("peak-concurrent-invocations").asLong()); assertTrue(result.get("wait-time").asLong() >= 0L); assertEquals(1L, result.get("methods").asInt()); final List<Property> methods = result.get("methods").asPropertyList(); assertEquals("doIt", methods.get(0).getName()); final ModelNode invocationValues = methods.get(0).getValue(); assertTrue(invocationValues.get("execution-time").asLong() >= 50L); assertEquals(1L, invocationValues.get("invocations").asLong()); assertTrue(invocationValues.get("wait-time").asLong() >= 0L); if (type.equals(EJBComponentType.STATEFUL)) { assertEquals(1L, result.get("cache-size").asLong()); assertEquals(0L, result.get("passivated-count").asLong()); assertEquals(1L, result.get("total-size").asLong()); // Create a second bean forcing the first bean to passivate BusinessInterface bean2 = (BusinessInterface) context.lookup("ejb:/" + MODULE_NAME + "//" + name + "!" + BusinessInterface.class.getName() + (type == EJBComponentType.STATEFUL ? "?stateful" : "")); bean2.doIt(); // Eviction is asynchronous, so wait a bit for this to take effect Thread.sleep(500); result = executeOperation(managementClient, ModelDescriptionConstants.READ_RESOURCE_OPERATION, address); assertEquals(1L, result.get("cache-size").asLong()); assertEquals(1L, result.get("passivated-count").asLong()); assertEquals(2L, result.get("total-size").asLong()); // remove bean from container bean.remove(); bean2.remove(); result = executeOperation(managementClient, ModelDescriptionConstants.READ_RESOURCE_OPERATION, address); assertEquals(0L, result.get("cache-size").asLong()); assertEquals(0L, result.get("passivated-count").asLong()); assertEquals(0L, result.get("total-size").asLong()); } } } private static ModelNode undefineAttribute(final ManagementClient managementClient, final ModelNode address, final String attributeName) throws IOException { final ModelNode op = new ModelNode(); op.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.UNDEFINE_ATTRIBUTE_OPERATION); op.get(ModelDescriptionConstants.OP_ADDR).set(address); op.get(ModelDescriptionConstants.NAME).set(attributeName); return execute(managementClient, op); } private static ModelNode writeAttributeBoolean(final ManagementClient managementClient, final ModelNode address, final String attributeName, final boolean value) throws IOException { final ModelNode op = new ModelNode(); op.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION); op.get(ModelDescriptionConstants.OP_ADDR).set(address); op.get(ModelDescriptionConstants.NAME).set(attributeName); op.get(ModelDescriptionConstants.VALUE).set(value); return execute(managementClient, op); } }