Java tutorial
/* * ClassLoaderTest.java * Created on Apr 11, 2013 * * Copyright 2013 Lithium Technologies, Inc. * Emeryville, California, U.S.A. All Rights Reserved. * * This software is the confidential and proprietary information * of Lithium Technologies, Inc. ("Confidential Information") * You shall not disclose such Confidential Information and shall * use it only in accordance with the terms of the license * agreement you entered into with Lithium. */ package lithium.classloadertest; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Future; import javax.annotation.PostConstruct; import lithium.classloadertest.FunctionalTestClassLoaderTest.BaseClass; import lithium.classloadertest.FunctionalTestClassLoaderTest.NeedToBeUnfinal; import lithium.classloadertest.TestClass.Parent; import lithium.classloadertest.annotation.ClassLoaderInit; import lithium.classloadertest.annotation.ProxyableClasses; import lithium.classloadertest.annotation.TestVisible; import lithium.classloadertest.spring.SpringApplicationContext; import org.easymock.EasyMock; import org.easymock.IMocksControl; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; /** * @author jeff.collins */ @RunWith(UnfinalizingTestRunner.class) @ProxyableClasses(value = { NeedToBeUnfinal.class, BaseClass.class }, classNames = { "lithium.classloadertest.TestClass$PrivateParentImpl" }) @ContextConfiguration(locations = { "/spring-test.xml" }) public class FunctionalTestClassLoaderTest extends FunctionalTestClassLoaderTestBase { static FunctionalTestClassLoader cl; static FunctionalTestClassLoader cl2; String s; @TestVisible InnerClass innerClassThatShouldBeVisibleToParent = null; @TestVisible boolean initialized = false; @Autowired @TestVisible private final SpringApplicationContext springApplicationContext = null; @TestVisible private final TestInterface finalClassInstance = new FinalClass(); @PostConstruct public void testInit() { s = "bar"; } @ClassLoaderInit public void customInitMethodBeforeSpring() { initialized = true; innerClassThatShouldBeVisibleToParent = new InnerClass(); int hash = hashCode(); System.setProperty("customInitMethodBeforeSpring-" + hash, "1"); } @BeforeClass public static void setup() { /** * Initialize the classloaders only once, but note that they need to be cleaned up so that they don't consume * a lot of memory throughout the whole test execution. * * Also understand that this will cause multiple tests to start having dependencies on each other. */ cl = new FunctionalTestClassLoader(FunctionalTestClassLoaderTest.class); cl2 = new FunctionalTestClassLoader(FunctionalTestClassLoaderTest.class); } @AfterClass public static void tearDown() { cl = null; cl2 = null; } public interface TestInterface { public String doSomeStuff(String myString); public String getData(); public OtherInnerClass makeOtherTestInnerClassInterface2(); public int doPrimitive(Integer i, int primInt); public void callWithComplexArg(OtherInnerClass otherInnerClass); public void getMe(); public void getMe(int x); } public class InnerClass implements TestInterface { public InnerClass() { } @Override public String doSomeStuff(String myString) { return "InnerClass: " + myString; } @Override public String getData() { return "data"; } @Override public OtherInnerClass makeOtherTestInnerClassInterface2() { return new OtherInnerClass(); } @Override public int doPrimitive(Integer i, int primInt) { return i + primInt; } @Override public void callWithComplexArg(OtherInnerClass otherInnerClass) { } @Override public void getMe() { } @Override public void getMe(int x) { } } public class OtherInnerClass { public OtherInnerClass() { } public String doSomeOtherStuff(String myString, TestInterface innerClass) { return "OtherInnerClass: " + myString + innerClass.getData(); } public NoDefaultConstructorInnerClass makeANoDefaultConstructorInstance() { return new NoDefaultConstructorInnerClass("hi"); } public NoDefaultConstructorInnerClassWithNative makeANoDefaultConstructorInstanceWithNative() { return new NoDefaultConstructorInnerClassWithNative(2, "test"); } } public class NoDefaultConstructorInnerClass { String msg; public NoDefaultConstructorInnerClass(String msg) { this.msg = msg; } public String callableMethod(String myString) { return msg; } } public class NoDefaultConstructorInnerClassWithNative { String msg; int x; public NoDefaultConstructorInnerClassWithNative(int x, String msg) { this.msg = msg; this.x = x; } public int callableMethod() { return x; } } public final class FinalClass implements TestInterface { @Override public String doSomeStuff(String myString) { return null; } @Override public String getData() { return null; } @Override public OtherInnerClass makeOtherTestInnerClassInterface2() { return null; } @Override public int doPrimitive(Integer i, int primInt) { return i + primInt; } @Override public void callWithComplexArg(OtherInnerClass otherInnerClass) { } @Override public void getMe() { } @Override public void getMe(int x) { } } public final class NeedToBeUnfinal { public final void call() { if (!(getClass().getClassLoader().getClass().getName() .equals(FunctionalTestClassLoader.class.getName()))) { throw new RuntimeException("Wrong classloader for call"); } } } public class BaseClass { public final String baseMethod() { return getClass().getClassLoader().getClass().getName(); } } public class SubClass extends BaseClass { } public SubClass getSubClass() { return new SubClass(); } public NeedToBeUnfinal getNeedToBeUnfinal() { return new NeedToBeUnfinal(); } public TestInterface getInnerClass() { return new InnerClass(); } public String testInstance(String sample) { return sample + s; } public String testMe(Object m) { return "foo"; } public static int testStatic() { return 2; } private void helperMethod() { finalClassInstance.callWithComplexArg(new OtherInnerClass()); } public TestInterface createMock() { IMocksControl control = EasyMock.createStrictControl(); TestInterface testInterface = control.createMock(TestInterface.class); control.replay(); return testInterface; } @Override public String getString() { String base = super.getString(); return "Child + " + base; } @Test public void privateTestMethodHelperTest() throws Exception { // verify that a private method proxies correctly FunctionalTestClassLoaderTest clTest = cl.getEntryClassInstance(); clTest.helperMethod(); } @Test public void testParallelExec() throws Exception { final FunctionalTestClassLoaderTest clTest = cl.getEntryClassInstance(); final FunctionalTestClassLoaderTest clTest2 = cl2.getEntryClassInstance(); @SuppressWarnings("unchecked") Future<Integer> futures[] = FunctionalTestClassLoader.parallelExec( new Callable<Integer>() { @Override public Integer call() throws Exception { return clTest.finalClassInstance.doPrimitive(1, 2); } }, new Callable<Integer>() { @Override public Integer call() throws Exception { return clTest2.finalClassInstance.doPrimitive(3, 4); } }); Assert.assertEquals("should have added", 3, (int) futures[0].get()); Assert.assertEquals("should have added", 7, (int) futures[1].get()); } @Test public void testMock() { FunctionalTestClassLoaderTest clTest = cl.getEntryClassInstance(); TestInterface itf = clTest.createMock(); try { itf.doSomeStuff("foo"); } catch (RuntimeException e) { // look for the easymock error Assert.assertEquals(AssertionError.class, e.getCause().getClass()); Assert.assertTrue(e.getCause().getMessage().contains("doSomeStuff(\"foo\")")); } } @Test public void testWasInitialized() { FunctionalTestClassLoaderTest clTest = cl.getEntryClassInstance(); Assert.assertTrue("Child class didn't get the @TestInit method called", clTest.initialized); } @Test public void testVisible() { FunctionalTestClassLoaderTest clTest = cl.getEntryClassInstance(); Assert.assertNotNull(clTest.springApplicationContext); Assert.assertNotNull(clTest.innerClassThatShouldBeVisibleToParent); Assert.assertTrue(clTest.initialized); } @Test public void testSpring() { FunctionalTestClassLoaderTest clTest = cl.getEntryClassInstance(); Assert.assertNotNull(clTest.springApplicationContext); SpringApplicationContext ctx = clTest.springApplicationContext; Assert.assertEquals("Should be in other classloader", FunctionalTestClassLoader.class.getName(), ctx.getAppContext().getClassLoader().getClass().getName()); } @Test public void testMultiple() { int retval = cl.<Integer>callStatic(FunctionalTestClassLoaderTest.class, "testStatic"); Assert.assertEquals("Should be 2", 2, retval); String retval2 = cl2.<String>call("testInstance", "foo"); Assert.assertEquals("Should have passed", "foobar", retval2); } @Test public void testWrapping() { FunctionalTestClassLoaderTest clTest = cl.getEntryClassInstance(); TestInterface innerClass = clTest.getInnerClass(); String retval = innerClass.doSomeStuff("test string"); Assert.assertEquals("should match", "InnerClass: test string", retval); OtherInnerClass otherTest = innerClass.makeOtherTestInnerClassInterface2(); retval = otherTest.doSomeOtherStuff("test string 2 ", innerClass); Assert.assertEquals("should match", "OtherInnerClass: test string 2 data", retval); // test parameter binding to Object (a different class) clTest.testMe(innerClass); FunctionalTestClassLoaderTest clTest2 = cl2.getEntryClassInstance(); TestInterface innerClass2 = clTest2.getInnerClass(); Assert.assertEquals("two inner classes must match", innerClass.getData(), innerClass2.getData()); NoDefaultConstructorInnerClass val = otherTest.makeANoDefaultConstructorInstance(); Assert.assertEquals("should be able to proxy existing no default arg constructor classes", "hi", val.callableMethod("test")); NoDefaultConstructorInnerClassWithNative val2 = otherTest.makeANoDefaultConstructorInstanceWithNative(); Assert.assertEquals("should be able to proxy existing no default arg constructor classes", 2, val2.callableMethod()); } @Test public void testUnfinalized() { FunctionalTestClassLoaderTest clTest = cl.getEntryClassInstance(); NeedToBeUnfinal unfinal = clTest.getNeedToBeUnfinal(); unfinal.call(); } @Test public void testBaseClassMethods() { FunctionalTestClassLoaderTest clTest = cl.getEntryClassInstance(); SubClass subclass = clTest.getSubClass(); Assert.assertEquals("Method should be in correct classloader", FunctionalTestClassLoader.class.getName(), subclass.baseMethod()); } @Test public void testPrimitiveTypes() { FunctionalTestClassLoaderTest clTest = cl.getEntryClassInstance(); TestInterface inner = clTest.getInnerClass(); int foo = inner.doPrimitive(1, 2); Assert.assertEquals("Integer should be roundtripped", 3, foo); } @Test public void testParallelLoading() { FunctionalTestClassLoader test1 = new FunctionalTestClassLoader(FunctionalTestClassLoaderTest.class); FunctionalTestClassLoader test2 = new FunctionalTestClassLoader(FunctionalTestClassLoaderTest.class); List<String> propToRemove = new ArrayList<String>(); for (Object key : System.getProperties().keySet()) { String keyString = (String) key; if (keyString.startsWith("customInitMethodBeforeSpring-")) { propToRemove.add(keyString); } } for (String prop : propToRemove) { System.getProperties().remove(prop); } FunctionalTestClassLoader.parallelSetup(test1, test2); int count = 0; for (Object key : System.getProperties().keySet()) { String keyString = (String) key; if (keyString.startsWith("customInitMethodBeforeSpring-")) { count++; } } Assert.assertEquals("Found count should be 2", 2, count); } @Test public void testCallingSuperMethod() { FunctionalTestClassLoaderTest clTest = cl.getEntryClassInstance(); String testString = clTest.getString(); Assert.assertEquals("Calling super.method should works", testString, "Child + Base"); } @Test public void testOverloads() { FunctionalTestClassLoaderTest clTest = cl.getEntryClassInstance(); TestInterface inner = clTest.getInnerClass(); inner.getMe(); inner.getMe(3); } @Test public void testErrorMessageOnInvalidClass() { FunctionalTestClassLoaderTest clTest = cl.getEntryClassInstance(); OtherInnerClass oic = new OtherInnerClass(); try { clTest.testMe(oic); Assert.fail("Allowed method call with foreign classloader"); } catch (RuntimeException ie) { String errorMessage = ie.getMessage(); Assert.assertTrue("Should have thrown error message about foreign classloader", errorMessage.startsWith("Invalid parameter 0")); } } public Parent getNonVisibleClassInstance() { return Parent.getAHiddenImplementationOfParent(); } @Test public void testNonVisibleClass() { FunctionalTestClassLoaderTest clTest = cl.getEntryClassInstance(); Parent parent = clTest.getNonVisibleClassInstance(); parent.overrideableMethod(); } }