Skip to content

Commit

Permalink
WFLY-822 Make sure the deployment's classloader is set as TCCL during…
Browse files Browse the repository at this point in the history
… the lifecycle of a MBean instance
  • Loading branch information
jaikiran authored and bstansberry committed Jul 11, 2013
1 parent 10fd9fd commit c1a6392
Show file tree
Hide file tree
Showing 14 changed files with 389 additions and 28 deletions.
6 changes: 4 additions & 2 deletions sar/src/main/java/org/jboss/as/service/AbstractService.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,17 @@ abstract class AbstractService implements Service<Object> {

private final Object mBeanInstance;
private final ServiceName duServiceName;
private final ClassLoader mbeanContextClassLoader;

/**
*
* @param mBeanInstance
* @param duServiceName the deployment unit's service name
*/
protected AbstractService(final Object mBeanInstance, final ServiceName duServiceName) {
protected AbstractService(final Object mBeanInstance, final ServiceName duServiceName, final ClassLoader mbeanContextClassLoader) {
this.mBeanInstance = mBeanInstance;
this.duServiceName = duServiceName;
this.mbeanContextClassLoader = mbeanContextClassLoader;
}

/** {@inheritDoc} */
Expand All @@ -60,7 +62,7 @@ protected void invokeLifecycleMethod(final Method method, final LifecycleContext
if (method != null) {
WritableServiceBasedNamingStore.pushOwner(duServiceName);
try {
final ClassLoader old = WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(mBeanInstance.getClass().getClassLoader());
final ClassLoader old = WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(this.mbeanContextClassLoader);
try {
method.invoke(mBeanInstance);
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ final class CreateDestroyService extends AbstractService {
private final ServiceComponentInstantiator componentInstantiator;
private ManagedReference managedReference;

CreateDestroyService(final Object mBeanInstance, final Method createMethod, final Method destroyMethod, ServiceComponentInstantiator componentInstantiator, final ServiceName duServiceName) {
super(mBeanInstance, duServiceName);
CreateDestroyService(final Object mBeanInstance, final Method createMethod, final Method destroyMethod, ServiceComponentInstantiator componentInstantiator,
final ServiceName duServiceName, final ClassLoader mbeanContextClassLoader) {
super(mBeanInstance, duServiceName, mbeanContextClassLoader);
this.createMethod = createMethod;
this.destroyMethod = destroyMethod;
this.componentInstantiator = componentInstantiator;
Expand Down
7 changes: 4 additions & 3 deletions sar/src/main/java/org/jboss/as/service/MBeanServices.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ final class MBeanServices {
* @param componentInstantiator
* @param duServiceName the deployment unit's service name
*/
MBeanServices(final String mBeanName, final Object mBeanInstance, final List<ClassReflectionIndex<?>> mBeanClassHierarchy, final ServiceTarget target,ServiceComponentInstantiator componentInstantiator, final ServiceName duServiceName) {
MBeanServices(final String mBeanName, final Object mBeanInstance, final List<ClassReflectionIndex<?>> mBeanClassHierarchy, final ServiceTarget target,ServiceComponentInstantiator componentInstantiator,
final ServiceName duServiceName, final ClassLoader mbeanContextClassLoader) {
if (mBeanClassHierarchy == null) {
throw SarMessages.MESSAGES.nullVar("mBeanName");
}
Expand All @@ -87,7 +88,7 @@ final class MBeanServices {

final Method createMethod = ReflectionUtils.getMethod(mBeanClassHierarchy, CREATE_METHOD_NAME, NO_ARGS, false);
final Method destroyMethod = ReflectionUtils.getMethod(mBeanClassHierarchy, DESTROY_METHOD_NAME, NO_ARGS, false);
createDestroyService = new CreateDestroyService(mBeanInstance, createMethod, destroyMethod,componentInstantiator, duServiceName);
createDestroyService = new CreateDestroyService(mBeanInstance, createMethod, destroyMethod,componentInstantiator, duServiceName, mbeanContextClassLoader);
createDestroyServiceName = ServiceNameFactory.newCreateDestroy(mBeanName);
createDestroyServiceBuilder = target.addService(createDestroyServiceName, createDestroyService);
if(componentInstantiator != null) {
Expand All @@ -97,7 +98,7 @@ final class MBeanServices {

final Method startMethod = ReflectionUtils.getMethod(mBeanClassHierarchy, START_METHOD_NAME, NO_ARGS, false);
final Method stopMethod = ReflectionUtils.getMethod(mBeanClassHierarchy, STOP_METHOD_NAME, NO_ARGS, false);
startStopService = new StartStopService(mBeanInstance, startMethod, stopMethod, duServiceName);
startStopService = new StartStopService(mBeanInstance, startMethod, stopMethod, duServiceName, mbeanContextClassLoader);
startStopServiceName = ServiceNameFactory.newStartStop(mBeanName);
startStopServiceBuilder = target.addService(startStopServiceName, startStopService);
startStopServiceBuilder.addDependency(createDestroyServiceName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@

package org.jboss.as.service;

import static org.jboss.msc.value.Values.cached;

import java.beans.PropertyEditor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
Expand Down Expand Up @@ -59,6 +57,9 @@
import org.jboss.msc.value.MethodValue;
import org.jboss.msc.value.Value;
import org.jboss.msc.value.Values;
import org.wildfly.security.manager.WildFlySecurityManager;

import static org.jboss.msc.value.Values.cached;

/**
* DeploymentUnit processor responsible for taking JBossServiceXmlDescriptor configuration and creating the
Expand Down Expand Up @@ -112,7 +113,7 @@ private void addServices(final ServiceTarget target, final JBossServiceConfig mB
final List<ClassReflectionIndex<?>> mBeanClassHierarchy = ReflectionUtils.getClassHierarchy(mBeanClassName, index, classLoader);
final Object mBeanInstance = newInstance(mBeanConfig, mBeanClassHierarchy, classLoader);
final String mBeanName = mBeanConfig.getName();
final MBeanServices mBeanServices = new MBeanServices(mBeanName, mBeanInstance, mBeanClassHierarchy, target, componentInstantiator, phaseContext.getDeploymentUnit().getServiceName());
final MBeanServices mBeanServices = new MBeanServices(mBeanName, mBeanInstance, mBeanClassHierarchy, target, componentInstantiator, phaseContext.getDeploymentUnit().getServiceName(), classLoader);

final JBossServiceDependencyConfig[] dependencyConfigs = mBeanConfig.getDependencyConfigs();
if (dependencyConfigs != null) {
Expand Down Expand Up @@ -189,25 +190,32 @@ private static Value<?> getValue(final JBossServiceAttributeConfig attributeConf
return new ImmediateValue<Object>(newValue(setterType, attributeConfig.getValue()));
}

private static Object newInstance(final JBossServiceConfig serviceConfig, final List<ClassReflectionIndex<?>> mBeanClassHierarchy, final ClassLoader classLoader) throws DeploymentUnitProcessingException {
final JBossServiceConstructorConfig constructorConfig = serviceConfig.getConstructorConfig();
final int paramCount = constructorConfig != null ? constructorConfig.getArguments().length : 0;
final Class<?>[] types = new Class<?>[paramCount];
final Object[] params = new Object[paramCount];

if (constructorConfig != null) {
final Argument[] arguments = constructorConfig.getArguments();
for (int i = 0; i < paramCount; i++) {
final Argument argument = arguments[i];
types[i] = ReflectionUtils.getClass(argument.getType(), classLoader);
params[i] = newValue(ReflectionUtils.getClass(argument.getType(), classLoader), argument.getValue());
private static Object newInstance(final JBossServiceConfig serviceConfig, final List<ClassReflectionIndex<?>> mBeanClassHierarchy, final ClassLoader deploymentClassLoader) throws DeploymentUnitProcessingException {
// set TCCL so that the MBean instantiation happens in the deployment's classloader
final ClassLoader oldTCCL = WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(deploymentClassLoader);
try {
final JBossServiceConstructorConfig constructorConfig = serviceConfig.getConstructorConfig();
final int paramCount = constructorConfig != null ? constructorConfig.getArguments().length : 0;
final Class<?>[] types = new Class<?>[paramCount];
final Object[] params = new Object[paramCount];

if (constructorConfig != null) {
final Argument[] arguments = constructorConfig.getArguments();
for (int i = 0; i < paramCount; i++) {
final Argument argument = arguments[i];
types[i] = ReflectionUtils.getClass(argument.getType(), deploymentClassLoader);
params[i] = newValue(ReflectionUtils.getClass(argument.getType(), deploymentClassLoader), argument.getValue());
}
}
}

final Constructor<?> constructor = mBeanClassHierarchy.get(0).getConstructor(types);
final Object mBeanInstance = ReflectionUtils.newInstance(constructor, params);
final Constructor<?> constructor = mBeanClassHierarchy.get(0).getConstructor(types);
final Object mBeanInstance = ReflectionUtils.newInstance(constructor, params);

return mBeanInstance;
return mBeanInstance;
} finally {
// switch back the TCCL
WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(oldTCCL);
}
}

private static Injector<Object> getPropertyInjector(final String propertyName, final List<ClassReflectionIndex<?>> mBeanClassHierarchy, final Service<?> service, final Value<?> value) {
Expand Down
4 changes: 2 additions & 2 deletions sar/src/main/java/org/jboss/as/service/StartStopService.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ final class StartStopService extends AbstractService {
private final Method startMethod;
private final Method stopMethod;

StartStopService(final Object mBeanInstance, final Method startMethod, final Method stopMethod, final ServiceName duServiceName) {
super(mBeanInstance, duServiceName);
StartStopService(final Object mBeanInstance, final Method startMethod, final Method stopMethod, final ServiceName duServiceName, final ClassLoader mbeanContextClassLoader) {
super(mBeanInstance, duServiceName, mbeanContextClassLoader);
this.startMethod = startMethod;
this.stopMethod = stopMethod;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2013, 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.sar.context.classloader;

/**
* @author: Jaikiran Pai
*/
public class ClassAInSarDeployment {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2013, 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.sar.context.classloader;

/**
* @author: Jaikiran Pai
*/
public class ClassBInSarDeployment {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2013, 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.sar.context.classloader;

/**
* @author: Jaikiran Pai
*/
public class ClassCInSarDeployment {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2013, 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.sar.context.classloader;

/**
* @author: Jaikiran Pai
*/
public class ClassDInSarDeployment {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package org.jboss.as.test.integration.sar.context.classloader;

import java.io.IOException;

import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
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.sar.context.classloader.mbean.MBeanInAModuleService;
import org.jboss.as.test.integration.sar.context.classloader.mbean.MBeanInAModuleServiceMBean;
import org.jboss.logging.Logger;
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.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
* 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 Logger logger = Logger.getLogger(MBeanTCCLTestCase.class);

private static final String EAR_NAME = "tccl-mbean-test-app";
private static final String SAR_NAME = "tccl-mbean-test-sar";

@ContainerResource
private ManagementClient managementClient;

/**
* .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");

logger.info("created deployment: " + ear.toString(true));
return ear;
}

/**
* 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 {
return JMXConnectorFactory.connect(managementClient.getRemoteJMXURL()).getMBeanServerConnection();

}

}
Loading

0 comments on commit c1a6392

Please sign in to comment.