Please wait. This can take some minutes ...
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.
org.test4j.mock.stub.Impostor Maven / Gradle / Ivy
package org.test4j.mock.stub;
import g_cglib.net.sf.cglib.core.CodeGenerationException;
import g_cglib.net.sf.cglib.core.DefaultNamingPolicy;
import g_cglib.net.sf.cglib.core.NamingPolicy;
import g_cglib.net.sf.cglib.core.Predicate;
import g_cglib.net.sf.cglib.proxy.*;
import g_objenesis.org.objenesis.Objenesis;
import g_objenesis.org.objenesis.ObjenesisStd;
import org.test4j.mock.faking.util.ClassLoad;
import org.test4j.mock.faking.util.TypeUtility;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static java.lang.reflect.Modifier.isAbstract;
/**
* 欺骗者, 使用Enhancer来创建Stub或Proxy对象
*
* @author darui.wu
*/
public class Impostor {
private Impostor() {
}
private static final NamingPolicy NAMING_POLICY = new DefaultNamingPolicy() {
@Override
public String getClassName(String prefix, String source, Object key, Predicate names) {
return "org.test4j.stub." + super.getClassName(prefix, source, key, names);
}
};
private static final Objenesis objenesis = new ObjenesisStd();
/**
* 生成stub class类
*
* @param isStub true: stub object; false: proxy object
* @param baseType
* @param interfaces
* @return
*/
static Class createStubClass(boolean isStub, Class baseType, Class... interfaces) {
if (baseType == Object.class) {
baseType = ObjectStub.class;
}
Enhancer enhancer = new Enhancer() {
@Override
protected void filterConstructors(Class sc, List constructors) {
// Don't filter
}
};
enhancer.setUseCache(false);
enhancer.setClassLoader(ClassLoad.loadersOf(baseType, interfaces));
enhancer.setUseFactory(true);
if (baseType.isInterface()) {
enhancer.setSuperclass(ObjectStub.class);
enhancer.setInterfaces(toArray(baseType, interfaces));
} else {
enhancer.setSuperclass(baseType);
enhancer.setInterfaces(interfaces);
}
if (isStub) {
enhancer.setCallbackTypes(new Class[]{MethodInterceptor.class, NoOp.class});
enhancer.setCallbackFilter(method -> isAbstract(method.getModifiers()) ? 0 : 1);
} else {
enhancer.setCallbackTypes(new Class[]{InvocationHandler.class, NoOp.class});
enhancer.setCallbackFilter(method -> method.isBridge() ? 1 : 0);
}
if (baseType.getSigners() != null) {
enhancer.setNamingPolicy(NAMING_POLICY);
}
try {
return enhancer.createClass();
} catch (CodeGenerationException e) {
throw new IllegalArgumentException("could not proxy " + baseType, e);
}
}
/**
* 创建proxy对象
*
* @param invokable
* @param baseType
* @param interfaces
* @return
*/
public static T proxy(final ProxyInvokable invokable, Class baseType, Class... interfaces) {
return enhancer(
(InvocationHandler) (receiver, method, args) -> invokable.invoke(new ProxyInvocation(receiver, method, args)),
null, baseType, interfaces);
}
/**
* 创建fake代理桩
*
* @param baseType
* @param args
* @param
* @return
*/
public static T fake(Class baseType, Object... args) {
return stub(new FakeInterceptor(baseType), args, baseType, IFakeStub.class);
}
/**
* 创建stub对象
*
* @param interceptor
* @param baseType
* @param interfaces
* @param
* @return
*/
public static T stub(final MethodInterceptor interceptor, Object[] args, Class baseType, Class... interfaces) {
return enhancer(interceptor, args == null ? new Object[0] : args, baseType, interfaces);
}
static T enhancer(final Callback callback, Object[] args, Class baseType, Class... interfaces) {
if (baseType.isPrimitive()) {
throw new IllegalArgumentException(baseType.getName() + " is primitive then it can't be proxied.");
} else if (Modifier.isFinal(baseType.getModifiers())) {
throw new IllegalArgumentException(baseType.getName() + " is final type then it can't be proxied.");
}
setConstructorsAccessible(baseType, true);
Class proxyClass = createStubClass(args != null, baseType, interfaces);
Factory factory;
if (args == null || args.length == 0 || baseType.isInterface()) {
factory = (Factory) objenesis.newInstance(proxyClass);
} else {
factory = newInstance(proxyClass, baseType, args);
}
factory.setCallbacks(new Callback[]{callback, NoOp.INSTANCE});
return (T) baseType.cast(factory);
}
private static Factory newInstance(Class proxyClass, Class baseType, Object[] args) {
Constructor[] constructors = proxyClass.getDeclaredConstructors();
Constructor constructor = findMatchConstructor(constructors, args);
if (constructor == null) {
throw new RuntimeException("not found constructor for args:" + describe(args));
}
try {
return (Factory) constructor.newInstance(args);
} catch (Exception e) {
throw new RuntimeException("New instance[" + baseType.getName() + "] for args:" + describe(args) + " error.", e);
}
}
private static Class[] toArray(Class first, Class... rest) {
Class[] all = new Class[rest.length + 1];
all[0] = first;
System.arraycopy(rest, 0, all, 1, rest.length);
return all;
}
private static void setConstructorsAccessible(Class mockedType, boolean accessible) {
for (Constructor> constructor : mockedType.getDeclaredConstructors()) {
constructor.setAccessible(accessible);
}
}
public static boolean canProxy(Class type) {
return !type.isPrimitive() && !Modifier.isFinal(type.getModifiers());
}
/**
* 返回符合args类型的构造函数参数类型列表
*
* @param constructors
* @param args
* @return
*/
public static Constructor findMatchConstructor(Constructor[] constructors, Object[] args) {
for (Constructor constructor : constructors) {
int count = constructor.getParameterCount();
if (count != args.length) {
continue;
}
Class[] types = constructor.getParameterTypes();
Constructor matched = constructor;
for (int index = 0; index < count; index++) {
if (args[index] == null) {
continue;
}
Class pType = types[index];
Class aType = args[index].getClass();
if (TypeUtility.isAssignable(pType, aType)) {
continue;
}
matched = null;
break;
}
if (matched != null) {
return matched;
}
}
return null;
}
public static String describe(Object[] args) {
if (args == null) {
return null;
}
return Arrays.stream(args).map(String::valueOf).collect(Collectors.joining(",", "(", ")"));
}
}