Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package lithium.classloadertest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import lithium.classloadertest.annotation.ClassLoaderInit;
import lithium.classloadertest.annotation.TestVisible;
import lithium.classloadertest.common.TransformingURLClassLoader;
import lithium.classloadertest.impl.UnfinalizingClassTransformer;
import net.sf.cglib.core.CodeGenerationException;
import net.sf.cglib.core.DefaultNamingPolicy;
import net.sf.cglib.core.Predicate;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.InvocationHandler;
import net.sf.cglib.proxy.NoOp;
import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestContextManager;
/**
* FunctionalTestClassLoader creates an ability to dynamically proxy calls into a child ClassLoader from a parent ClassLoader.
* This makes it possible to load classes independently in multiple classloaders to create a simulation of a multi-node
* environment where all classes, and statics are separated from each other, and then drive the calls to the various
* ClassLoader instances from an overall "parent" testcase.
*
* To make writing tests easier, a FunctionalTestClassLoader will provide an "entryObject", which is a proxy to an instance
* of the entryClass in the contained ClassLoader. Note that if this is a junit test, the test *is not* run in the
* other ClassLoader. The test only runs in the current ClassLoader so that it can control multiple instances of
* the components under test.
*
* This entryObject proxy allows the client to retrieve any other object by calling methods or accessing properties on the
* entryObject. Each returned object will itself be a proxy of another object.
*
* Because testing large scale components can require loading a lot of classes, a typical pattern for a test suite that
* uses FunctionaTestClassLoader would look like this to conserve resources:
*
* @BeforeClass
* public static void setup() {
* cl = new FunctionalTestClassLoader(FunctionalTestClassLoaderTest.class);
* cl2 = new FunctionalTestClassLoader(FunctionalTestClassLoaderTest.class);
* }
*
* @AfterClass
* public static void tearDown() {
* cl = null;
* cl2 = null;
* }
*
* This class is also designed to work with the Spring {@link ContextConfiguration} annotation in that a FunctionalTestClassLoader
* that is given an entryClass with this annotation will load Spring if the class has this annotation. This is useful
* for tests of classes that leverage Spring. Use of the {@link ContextConfiguration} will be interpreted inside the
* FunctionalTestClassLoader child instance. This convenience allows callers to easily initialize the Spring container
* once for each classloader.
*
* When using {@link ContextConfiguration} a special @TestVisible annotation can be used on classes that are passed to
* FunctionalTestClassLoader. This annotation will cause field values in the entryObject to be copied up to the
* parent proxy instance. This allows Spring @Autowired fields to be visible in the parent test context.
*
* For example:
*
* @TestVisible
* @Autowired
* private final MyInterface myDependencyInjectedValue = null;
*
* ...
* {
* ...
* myTestClass.myDependencyInjectedValue.getValue();
* ...
* }
*
* If initialization needs to be performed in the FunctionalTestClassLoader, the {link @ClassLoaderInit} annotation can be
* placed on a single instance method on the entryClass with no arguments. This method will be invoked before
* Spring is initialized. This can be useful to set any custom state necessary before Spring is loaded.
*
* Another capability of the FunctionalTestClassLoader is the ability to reload specified classes without final
* designation and to force all members and classes to be public. Because of the expense of creating multiple
* new ClassLoaders for testing multi-node behavior, it makes sense to avoid the creation of yet another ClassLoader with
* other mocking tools (PowerMock etc.) since we just paid the cost of reloading the classes under
* test.
*
* By default, the FunctionalTestClassLoader will automatically unfinalize the entryClass. To unfinalize other classes
* for mocking, specify them in the second constructor argument.
*
* Only classses that will be mocked within FunctionalTestClassLoader helper methods in the context of a multi-node test
* need be specified. For instance:
*
* myClassLoader1 = new FunctionalTestClassLoader(FunctionalTestClassLoaderTest.class, new Class>[]{
* MyFinalConcreteClass.class
* });
*
* @author jeff.collins
*/
public class FunctionalTestClassLoader extends TransformingURLClassLoader {
/*
* The base functionality of Enhancer in CGLib is used, but modified slightly to change some behavior in order to accomodate
* proxy-ing of concrete base classes. Instead of allowing classes to inherit from a common base class using the
* "dynamic Proxy" approach - which only works with interfaces, the generated classes inherit from the actual local base
* class and implement all the same interfaces similar to EasyMock. This allows roundtripping through to the child classloader.
*/
private final Class> entryClass;
private Object entryObject;
private static final String LITHIUM_TEST_PROXY = "$Li_Pxy$";
private static final String JAVA_LANG_REFLECT_PROXY_PREFIX = "$Proxy";
private static final String CGLIB_TAG = "$$EnhancerByCGLIB$$";
private HashMap>> methodCache = new HashMap>>();
private static Logger logger = LoggerFactory.getLogger(FunctionalTestClassLoader.class);
/**
* Creates a new instance of the FunctionalTestClassLoader. The entry class will be loaded on demand
* during later interactions.
*
* @param entryClass the entry class to load later, used to create the instance returned by {@link #getEntryClassInstance()}
*/
public FunctionalTestClassLoader(Class> entryClass) {
super(new Class>[] {entryClass}, new UnfinalizingClassTransformer());
this.entryClass = entryClass;
loadTransformedClass(FunctionalTestClassLoader.class.getName());
}
/**
* Creates a new instance of the FunctionalTestClassLoader. The entry class will be loaded on demand
* during later interactions. Any classes specified in the classesToUnfinalize array will be loaded
* immediately and stripped of private and final designations on all class and method definitions. This
* allows tests to be written that mock final classes and methods and invoke private methods. This is
* done to avoid the cost of loading yet another classloader instance using a tool like PowerMock to do
* the same thing, since we're already loading a new ClassLoader.
*
* @param entryClass the entry class to load later, used to create the instance returned by {@link #getEntryClassInstance()}
* @param classesToUnfinalize classes to filter and load without final or private designations
*/
public FunctionalTestClassLoader(Class> entryClass, Class> classesToUnfinalize[]) {
super(classesToUnfinalize, new UnfinalizingClassTransformer());
this.entryClass = entryClass;
loadTransformedClass(entryClass.getName());
loadTransformedClass(FunctionalTestClassLoader.class.getName());
}
/**
* Reflective call to the class loader based on a previously returned instance.
* Normally not necessary if you're using the proxy-based approach, but
* can be used if there are corner-case issues with a particular test case.
*
* @param the type of the return
* @param methodName the name of the method to invoke in the other classloader on the instance
* @param args the arguments to pass
* @return the result of the method call
*/
public T call(String methodName, Object... args) {
ensureInitialized();
return this.call(entryObject, entryObject.getClass().getName(), methodName, args);
}
private void ensureInitialized() {
if (entryObject == null) {
entryObject = callStatic(FunctionalTestClassLoader.class, "getEntryClassInstance", this, entryClass);
}
}
/**
* Called reflectively by {@link #ensureInitialized()} to set up the entry class instance in the other classloader.
*
* @param entryClass
* @return the new instance
*/
@SuppressWarnings("unused")
private static Object getEntryClassInstance(ClassLoader classLoader, Class> entryClass) {
try {
Class> localClass = getClassFromClassLoader(entryClass, classLoader);
Object testInstance = localClass.newInstance();
Method m = getMethodForAnnotation(localClass, ClassLoaderInit.class);
if (m != null) {
m.invoke(testInstance);
}
if (localClass.isAnnotationPresent(ContextConfiguration.class)) {
createTestContextManager(testInstance);
}
return testInstance;
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof ClassLoaderException) {
logger.error("Failed getting entry object", e.getTargetException());
throw (ClassLoaderException)e.getTargetException();
}
throw new ClassLoaderException("Failed getting entry object", e.getTargetException());
} catch (Exception e) {
throw new ClassLoaderException("Failed getting entry object", e);
}
}
/**
* Initializes the setup of each FunctionalTestClassLoader by accessing the {@link #getEntryClassInstance()} method
* for each instance on a separate thread. This makes clasloading run in parallel, which will make the runtime
* similar to a single classloading test.
*
* @param args the list of FunctionalTestClassLoader objects to initialize
*/
public static void parallelSetup(final FunctionalTestClassLoader... args) {
List> tasks = new ArrayList>();
ExecutorService executorService = Executors.newFixedThreadPool(args.length);
for (int i = 0; i < args.length; i++) {
final int var = i;
tasks.add(new Callable