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();
}
}