package org.jboss.as.test.integration.sar.context.classloader; import java.io.IOException; import javax.management.MBeanServerConnection; import javax.management.MBeanTrustPermission; import javax.management.ObjectName; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; 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.as.arquillian.api.ContainerResource; import org.jboss.as.arquillian.container.ManagementClient; import org.jboss.as.test.integration.common.DefaultConfiguration; import org.jboss.as.test.integration.sar.context.classloader.mbean.MBeanInAModuleService; import org.jboss.as.test.integration.sar.context.classloader.mbean.MBeanInAModuleServiceMBean; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.EnterpriseArchive; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.xnio.IoUtils; import static org.jboss.as.test.shared.integration.ejb.security.PermissionUtils.createPermissionsXmlAsset; /** * Tests that the MBean instance lifecycle has the correct TCCL set. The TCCL is expected to be the classloader of the deployment through which the MBean was deployed. * * @author: Jaikiran Pai * @see https://issues.jboss.org/browse/WFLY-822 */ @RunWith(Arquillian.class) @RunAsClient public class MBeanTCCLTestCase { private static final String EAR_NAME = "tccl-mbean-test-app"; private static final String SAR_NAME = "tccl-mbean-test-sar"; @ContainerResource private ManagementClient managementClient; private JMXConnector connector; /** * .ear * | * |---- META-INF/jboss-deployment-structure.xml * | * |---- .sar * | | * | |--- META-INF/jboss-service.xml (deploys the MBean whose class resides in a separate JBoss module) * | | * | |--- ClassA, ClassB, ClassC, ClassD (all of which will be attempted to be loaded from the MBean class which resides in a different JBoss module than this deployment) * | * | * |---- .jar (configured as a JBoss Module in jboss-deployment-structure.xml of the .ear) * | | * | |---- MBean class (which relies on TCCL to load the classes present in the .sar deployment) * * @return */ @Deployment public static Archive createDeployment() { // create a .sar which will contain a jboss-service.xml. The actual MBean class will reside in a module which will be added as a dependency on the .sar final JavaArchive sar = ShrinkWrap.create(JavaArchive.class, SAR_NAME + ".sar"); // add the jboss-service.xml to the META-INF of the .sar sar.addAsManifestResource(MBeanInAModuleService.class.getPackage(), "tccl-test-service.xml", "jboss-service.xml"); // add some (dummy) classes to the .sar. These classes will then be attempted to be loaded from the MBean class (which resides in a module) sar.addClasses(ClassAInSarDeployment.class, ClassBInSarDeployment.class, ClassCInSarDeployment.class, ClassDInSarDeployment.class); // now create a plain .jar containing the MBean class. This jar will be configured as a JBoss Module, in the jboss-deployment-structure.xml of the .ear to which this // .jar will be added final JavaArchive jar = ShrinkWrap.create(JavaArchive.class, "jar-containing-mbean-class.jar"); jar.addClasses(MBeanInAModuleService.class, MBeanInAModuleServiceMBean.class); // create the .ear with the .sar and the .jar and the jboss-deployment-structure.xml final EnterpriseArchive ear = ShrinkWrap.create(EnterpriseArchive.class, EAR_NAME + ".ear"); ear.addAsModule(sar); ear.addAsModule(jar); ear.addAsManifestResource(MBeanTCCLTestCase.class.getPackage(), "jboss-deployment-structure.xml", "jboss-deployment-structure.xml"); ear.addAsManifestResource(createPermissionsXmlAsset( // mbean [wildfly:name=tccl-test-mbean] needs the following permission new MBeanTrustPermission("register"), // MBeanInAModuleService#testClassLoadByTCCL() needs the following permission new RuntimePermission("getClassLoader")), "permissions.xml"); return ear; } @After public void closeConnector() { IoUtils.safeClose(connector); } /** * Tests the MBean was deployed successfully and can be invoked. The fact that the MBean deployed successfully is a sign that the TCCL access from within the MBean code, worked fine * * @throws Exception */ @Test public void testTCCLInMBeanInvocation() throws Exception { final MBeanServerConnection mBeanServerConnection = this.getMBeanServerConnection(); final ObjectName mbeanObjectName = new ObjectName("wildfly:name=tccl-test-mbean"); final int num1 = 3; final int num2 = 4; // invoke the operation on MBean final Integer sum = (Integer) mBeanServerConnection.invoke(mbeanObjectName, "add", new Object[]{num1, num2}, new String[]{Integer.TYPE.getName(), Integer.TYPE.getName()}); Assert.assertEquals("Unexpected return value from MBean: " + mbeanObjectName, num1 + num2, (int) sum); } private MBeanServerConnection getMBeanServerConnection() throws IOException { connector = JMXConnectorFactory.connect(managementClient.getRemoteJMXURL(), DefaultConfiguration.credentials()); return connector.getMBeanServerConnection(); } }