lithium.classloadertest.FunctionalTestClassLoaderTest.java Source code

Java tutorial

Introduction

Here is the source code for lithium.classloadertest.FunctionalTestClassLoaderTest.java

Source

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

}