package org.marketcetera.strategyagent; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.marketcetera.strategyagent.ContextCLTestFactoryBase.*; import static org.marketcetera.strategyagent.ContextCLTestModuleBase.*; import static org.marketcetera.strategyagent.JarClassLoaderTest.createJar; import java.io.File; import java.util.Properties; import java.util.concurrent.Callable; import javax.management.JMX; import javax.management.ObjectName; import org.junit.After; import org.junit.Test; import org.marketcetera.module.*; /* $License$ */ /** * Verifies that the context class loader is correctly set when * invoking various factory / module methods within strategy agent. * * @author anshul@marketcetera.com * @version $Id: ContextClassLoaderTest.java 16841 2014-02-20 19:59:04Z colin $ * @since 1.5.0 */ public class ContextClassLoaderTest extends StrategyAgentTestBase { /** * Runs after each test. * * @throws Exception if there were unexpected errors. */ @After public void cleanup() throws Exception { shutdownSa(); } /** * Verifies that the context class loader is correctly setup when * the module methods are invoked * * @throws Exception if there were errors. */ @Test public void contextLoading() throws Exception { Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); // Verify initial factory state assertCLEquals(null, sConstructClassLoader, sCreateClassLoader, sFactoryGetAttributeLoader, sFactorySetAttributeLoader, sFactoryOperationLoader); assertCLEquals(null, sStartLoader, sStopLoader, sRequestLoader, sCancelLoader, sReceiveLoader, sGetAttributeLoader, sSetAttributeLoader, sOperationLoader, sFlowSupportLoader); // Create a subclass of ContextCLTestFactoryBase into a jar in the jars subdirectory String newSubclass = getClass().getPackage().getName() + ".ContextCLFactory"; byte[] classBytes = generateSubclass(ContextCLTestFactoryBase.class, newSubclass); JarClassLoaderTest.JarContents jc = new JarClassLoaderTest.JarContents(transformName(newSubclass) + ".class", classBytes); // Create a subclass of ContextCLTestModuleBase into the jar as well String newModuleSubclass = ContextCLTestFactoryBase.MODULE_SUBCLASS_NAME; byte[] moduleClassBytes = generateSubclassURNConstructor(ContextCLTestModuleBase.class, newModuleSubclass); JarClassLoaderTest.JarContents moduleClass = new JarClassLoaderTest.JarContents(transformName(newModuleSubclass) + ".class", moduleClassBytes); // Create the factory file to load this factory via the service loader createJar("clprovider.jar", new JarClassLoaderTest.JarContents[] { jc, moduleClass, new JarClassLoaderTest.JarContents("META-INF/services/" + ModuleFactory.class.getName(), newSubclass.getBytes()) }); final ModuleURN instanceURN = new ModuleURN(ContextCLTestFactoryBase.PROVIDER_URN, "con"); // Create the properties file for testing default parameter setting Properties properties = new Properties(); properties.setProperty("Attribute", "does not matter"); properties.setProperty(".Attribute", "does not matter"); savePropertiesForProvider(instanceURN, properties); String parameter = ContextCLTestFactoryBase.PROVIDER_URN + ";" + instanceURN.getValue(); File f = createFileWithText("createModule;" + parameter); // verify that the context classloader is same as the current classloader assertSame(getClass().getClassLoader(), Thread.currentThread().getContextClassLoader()); createSaWith(f.getAbsolutePath()); assertTrue(sa.isRunning()); // verify that the context classloader is now different ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); assertNotSame(getClass().getClassLoader(), contextLoader); // verify factory class loaders assertCLEquals(contextLoader, sConstructClassLoader, sCreateClassLoader, sFactorySetAttributeLoader); assertEquals(null, sFactoryGetAttributeLoader, sFactoryOperationLoader); sFactorySetAttributeLoader = null; final ContextCLFactoryMXBean factoryBean = JMX.newMXBeanProxy(getMBeanServer(), ContextCLTestFactoryBase.PROVIDER_URN.toObjectName(), ContextCLFactoryMXBean.class); invokeClearCtxCL(new Callable<Object>() { public Object call() throws Exception { factoryBean.operation(); factoryBean.setAttribute("value"); return factoryBean.getAttribute(); } }); assertCLEquals(contextLoader, sFactoryGetAttributeLoader, sFactoryOperationLoader, sFactorySetAttributeLoader); final ModuleManagerMXBean mmBean = JMX.newMXBeanProxy(getMBeanServer(), new ObjectName(ModuleManager.MODULE_MBEAN_NAME), ModuleManagerMXBean.class); //verify autostarted module verifyModuleCtxLoaders(mmBean, instanceURN); // Clear all module loaders sStartLoader = sStopLoader = sRequestLoader = sCancelLoader = sReceiveLoader = sGetAttributeLoader = sSetAttributeLoader = sOperationLoader = sFlowSupportLoader = null; // Clear factory create loader sCreateClassLoader = null; // delete, recreate & restart the module invokeClearCtxCL(new Callable<Object>() { public Object call() throws Exception { mmBean.deleteModule(instanceURN.toString()); return mmBean.createModule(instanceURN.parent().toString(), instanceURN.toString()); } }); // verify factory create loader assertEquals(contextLoader, sCreateClassLoader); verifyModuleCtxLoaders(mmBean, instanceURN); } /** * Verifies that the given module functions and the context classloader is correct. * * @param inMgrBean a <code>ModuleManagerMXBean</code> value * @param inInstanceURN a <code>ModuleURN</code> value * @throws Exception if an unexpected error occurs */ private void verifyModuleCtxLoaders(final ModuleManagerMXBean inMgrBean, final ModuleURN inInstanceURN) throws Exception { ClassLoader contextLoader = loader; // run the module through data flows invokeClearCtxCL(new Callable<Object>() { public Object call() throws Exception { //as an emitter DataFlowID flowID = inMgrBean.createDataFlow(inInstanceURN.toString()); inMgrBean.cancel(flowID.toString()); // as a receiver flowID = inMgrBean.createDataFlow(CopierModuleFactory.INSTANCE_URN + ";a string^" + inInstanceURN); // wait until the data has flowed DataFlowInfo flowInfo; do { Thread.sleep(500); flowInfo = inMgrBean.getDataFlowInfo(flowID.toString()); assertEquals(3, flowInfo.getFlowSteps().length); } while (flowInfo.getFlowSteps()[0].getNumEmitted() < 0); inMgrBean.cancel(flowID.toString()); // stop the module inMgrBean.stop(inInstanceURN.toString()); return null; } }); // verify module class loaders assertCLEquals(contextLoader, sStartLoader, sStopLoader, sRequestLoader, sCancelLoader, sReceiveLoader, sFlowSupportLoader, sSetAttributeLoader); final ContextCLModuleMXBean moduleBean = JMX.newMXBeanProxy(getMBeanServer(), inInstanceURN.toObjectName(), ContextCLModuleMXBean.class); invokeClearCtxCL(new Callable<Object>() { public Object call() throws Exception { moduleBean.operation(); moduleBean.setAttribute("value"); return moduleBean.getAttribute(); } }); assertCLEquals(contextLoader, sStartLoader, sStopLoader, sRequestLoader, sCancelLoader, sReceiveLoader, sGetAttributeLoader, sSetAttributeLoader, sOperationLoader, sFlowSupportLoader); } /** * Verifies that the given expected classloader matches each of the supplied actual classloaders. * * @param inExpected a <code>ClassLoader</code> value * @param inActuals a <code>ClassLoader[]</code> value */ private static void assertCLEquals(ClassLoader inExpected, ClassLoader... inActuals) { for(int i = 0; i < inActuals.length; i++) { ClassLoader actual = inActuals[i]; assertEquals("Expected<" + inExpected + ">Actual [" + i + "]<" + actual + ">", inExpected, actual); } } /** * Invokes the given block of code using the current context classloader. * * @param inCallable a <code>Callable<T></code> value * @return a <code>T</code> value * @throws Exception if an unexpected error occurs */ private static <T> T invokeClearCtxCL(final Callable<T> inCallable) throws Exception { Thread thread = Thread.currentThread(); ClassLoader loader = thread.getContextClassLoader(); try { thread.setContextClassLoader(inCallable.getClass().getClassLoader()); return inCallable.call(); } finally { thread.setContextClassLoader(loader); } } }