How do I create a proxy and invoke default interface methods as if they were implemented by the proxy super-class? For example:
interface Foo {
default int returnSomething() {
return 1;
}
}
interface Bar extends Foo {
default int returnSomethingMore() {
return returnSomething();
}
}
class Super implements Foo {
#Override
public int returnSomething() {
return 2;
}
}
I need a proxy of Bar, which will use the Super::returnSomething implementation when calling Bar::returnSomethingMore.
I tried this:
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Super.class);
enhancer.setInterfaces(new Class[] { Bar.class });
enhancer.setCallback((obj, method, args, proxy) -> {
if (method.getName().equals("returnSomethingMore")) {
return proxy.invokeSuper(obj, args);
// -> NoSuchMethodError
return proxy.invoke(obj, args);
// -> StackOverflowError
Class<?> declaringClass = method.getDeclaringClass();
Lookup lookup = MethodHandles.lookup();
return MethodHandles
.privateLookupIn(declaringClass, lookup)
.unreflectSpecial(method, declaringClass)
.bindTo(obj)
.invokeWithArguments(args);
// -> returns 1, not 2
}
});
How do I create a proxy object whose returnSomethingMore method returns 2?
I've dropped cglib (which thanks to SO's tag I've known it's no longer in active development) and adopted ByteBuddy which gave me the proxy that behaves as I need:
#Test
public void defaultInterfaceMethodTest() {
Class<? extends Super> loaded = new ByteBuddy()
.subclass(Super.class)
.implement(Bar.class).make()
.load(this.getClass().getClassLoader()).getLoaded();
Bar newInstance = (Bar) loaded.getConstructor().newInstance();
int shouldBeTwo = newInstance.returnSomethingMore();
Assert.assertEquals(2, shouldBeTwo);
}
Related
OBS: My code is java 8.
For example, I have this stacktrace:
MainClass:MethodA() calls
--------ClassB:MethodB() calls
------------ClassC:MethodC() #Cacheable calls
----------------ClassFinal:MethodD()
MethodA() -> MethodB() -> MethodC() -> MethodD();
In my example, ClassC:methodC() it's noted with #Cacheable.
I need get this annotation in ClassFinal:MethodD(), something like this:
public void MethodD() {
Cacheable cacheable = ...
}
I already done this using reflection, but it isn't work with overload:
public static <T extends Annotation> T getStacktraceAnnotation(Class<T> type) {
int currStack = 0;
T result = null;
//
//
StackTraceElement[] stackTraceElementArray = Thread.currentThread().getStackTrace();
//
//
for (java.lang.StackTraceElement curr : stackTraceElementArray) {
try {
Method[] methods = Class.forName(curr.getClassName()).getMethods();
for (Method currMethod : methods)
if (currMethod.getName().equals(curr.getMethodName())) {
result = currMethod.getAnnotation(type);
if (result != null) break;
}
//
//
if (result != null || currStack++ > 6) break;
} catch(Exception exc) {
// Nothing!
}
}
//
//
return result;
}
Real stacktace of my program:
fff.commons.serverless.abstracts.v2.AbstractCommonIntegrationHttp.sendGet(AbstractCommonIntegrationHttp.java:320)
fff.commons.serverless.abstracts.v2.Teste.a(Teste.java:14)
fff.commons.serverless.abstracts.v2.Teste.main(Teste.java:18)
Teste.a(Teste.java:14) is noted with #Cachable
And I need get this anottation in sendGet(AbstractCommonIntegrationHttp.java:320)
My annotation:
#Retention(RUNTIME)
#Target({ TYPE, METHOD })
#Inherited
public #interface Cacheable {
int secs() default 15;
}
I strongly suggest using a StackWalker instead:
private static final StackWalker SW = StackWalker
.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
public static <T extends Annotation> T getStacktraceAnnotation(Class<T> type) {
return SW.walk(s -> s.map(sf -> getMethodAnnotation(type, sf)).filter(Objects::nonNull)
.findFirst()).orElseThrow();
}
private static <T extends Annotation> T getMethodAnnotation(Class<T> annotationClass,
StackFrame sf) {
try {
return sf.getDeclaringClass()
.getDeclaredMethod(sf.getMethodName(), sf.getMethodType().parameterArray())
.getAnnotation(annotationClass);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
The StackWalker.StackFrame contains the information needed to distinguish between different overloads - namely the method type.
A StackTraceElement only contains the line number - which might be good enough if you parse the corresponding class file yourself - but this gets quickly out of hand.
So, I've got an object constructor:
public Func(Function<Var[], Var<T>> function, Var... arguments) {
// Function is a standart 1.8 class
//...
//secret stuff
}
I call it like that:
return new Func(new Function<Var[], Var>() {
#Override
public Var apply(Var[] args) {
return instance.getAttribute(args[0].value());
}
}, arguments[0].unpack(instance) // unpack(...) returns Var object
);
And it works. Now, my IDE (Intellij IDEA) suggests me to replace Function declaration with lambda. Okay, lets do it:
return new Func(
args -> instance.getAttribute(args[0].value()),
arguments[0].unpack(instance)
);
Now I have an error on args:
Array type expected; found: 'java.lang.Object'
So, apparently args now is Object. Why? Is that a bug in IDE or what?
Entire code:
Template:
public class Template {
public static void main(String[] args) {
SomeClass someClass = new SomeClass();
System.out.println(someMethod(someClass).value());
}
private static class SomeClass {
Var[] var = new Var[12];
SomeClass() {
var = new Var[12];
for ( int i = 0; i < var.length; i++) {
var[i] = new Var<>(i * 4);
}
}
Var getAttribute(int index) {
return var[index];
}
}
public static Var someMethod(SomeClass instance) {
return new Func(new Function<Var[], Var>() {
#Override
public Var apply(Var[] args) {
return instance.getAttribute((int)args[0].value());
}
}, new Var(4));
}
}
Var.java:
public class Var<T> {
private T value;
public Var(T value) {
this.value = value;
}
public T value() {
return value;
}
}
Func.java:
public class Func<T> extends Var<T> {
private Function<Var[], Var<T>> function;
private Var[] args;
public Func(Function<Var[], Var<T>> function, Var... args) {
super(null);
this.function = function;
this.args = args;
}
#Override
public T value() {
return function.apply(args).value();
}
}
The error message appers also in the Eclipse-IDE:
The type of the expression must be an array type but it resolved to Object
I think it is not an IDE-bug, neither in IntelliJ nor in Eclipse. The Compiler needs for the processing of a lambda expression always a target type which is a functional interface.
In the case of
args -> instance.getAttribute((int)args[0].value())
the target type is determined by the first argument of the Func-constructor
Function<Var[], Var<T>> function
However, this functional interface is a generic interface. Java compiles generics using type erasure which means the replacement of the generic parameter-types by the Object-type. Thus, the interface is compiled like
interface Function {
public Object apply(Object args);
}
and this is applied as target type. Thus, for args an Object-type instead of a Var[]-type is expected which results in an error message.
In case of an anonymous class this is different since more informations are provided for the determination of the target type.
new Function<Var[], Var>(){...}
explicitly contains the type-information. Because of this args is expected of Var[]-type and no error message is shown.
There are two possibilities to fix the error:
1) In the getAttribut-method cast args explicitly to Var[], i.e. replace
(int)args[0].value()
with
(int)((Var[])args)[0].value()
or 2) Don't use a generic interface i.e. change the interface to
interface Function {
public Var apply(Var[] args);
}
Then type information is preserved. Of course the rest of the code has to be adapted accordingly.
How can I create a single common factory for hundreds of service-interfaces?
I have a common generic super-interface, which all my service-interfaces extend: BaseDao<T>
There are hundreds of (generated) interfaces sub-classing my BaseDao, e.g. CustomerDao extends BaseDao<Customer>. Of course, I do not want to implement a single factory for every sub-class. Especially, because there is already a DaoFactory, which I need to "glue" into my Weld-environment.
Hence, I implemented this:
#ApplicationScoped
public class InjectingDaoFactory {
#SuppressWarnings("rawtypes") // We *MUST* *NOT* declare a wild-card -- Weld does not accept it => omit the type argument completely.
#Produces
public BaseDao getDao(final InjectionPoint injectionPoint) {
final Type type = injectionPoint.getType();
// ... some checks and helpful exceptions ...
final Class<?> c = (Class<?>) type;
// ... more checks and helpful exceptions ...
#SuppressWarnings("unchecked")
final Class<BaseDao<?>> clazz = (Class<BaseDao<?>>) c;
final BaseDao<?> dao = DaoFactory.getDao(clazz);
return dao;
}
}
In the code requiring such a DAO, I now tried this:
#Inject
private CustomerDao customerDao;
But I get the error org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type CustomerDao with qualifiers #Default -- Weld does not understand that my InjectingDaoFactory is capable of providing the correct sub-class to meet the dependency on CustomerDao.
Please note that I (of course) did not have the chance to debug the code of my factory. Maybe I need to use InjectionPoint.getMember() instead of InjectionPoint.getType() -- this is not my problem, now. My problem is that the responsibility of my factory for the sub-interfaces extending BaseDao is not understood by Weld at all.
So, what do I need to do to make Weld understand that one single factory can provide all the implementations of the many sub-interfaces of my BaseDao common DAO-interface?
According to this documentation, I created the following extension, which seems to work fine:
public class InjectingDaoExtension implements Extension {
public InjectingDaoExtension() {
}
private final Set<Class<? extends BaseDao>> injectedDaoInterfaces = new HashSet<>();
public <T> void processInjectionTarget(#Observes ProcessInjectionTarget<T> pit, BeanManager beanManager) {
final InjectionTarget<T> it = pit.getInjectionTarget();
for (InjectionPoint injectionPoint : it.getInjectionPoints()) {
Field field = null;
try {
Member member = injectionPoint.getMember();
field = member.getDeclaringClass().getDeclaredField(member.getName());
} catch (Exception e) {
// ignore
}
if (field != null) {
Class<?> type = field.getType();
if (BaseDao.class.isAssignableFrom(type)) {
if (! type.isInterface()) {
pit.addDefinitionError(new IllegalStateException(String.format("%s is not an interface! Cannot inject: %s", type, field)));
}
#SuppressWarnings("unchecked")
Class<? extends BaseDao> c = (Class<? extends BaseDao>) type;
injectedDaoInterfaces.add(c);
} else {
field = null;
}
}
}
}
public void afterBeanDiscovery(#Observes AfterBeanDiscovery abd, BeanManager beanManager) {
for (Class<? extends BaseDao> daoInterface : injectedDaoInterfaces) {
abd.addBean(createBean(daoInterface, beanManager));
}
}
protected <D extends BaseDao> Bean<D> createBean(final Class<D> daoInterface, final BeanManager beanManager) {
return new Bean<D>() {
private InjectionTarget<D> injectionTarget;
public synchronized InjectionTarget<D> getInjectionTargetOrNull() {
return injectionTarget;
}
public synchronized InjectionTarget<D> getInjectionTarget() {
if (injectionTarget == null) {
D handler = DaoFactory.getDao(daoInterface);
#SuppressWarnings("unchecked")
Class<D> handlerClass = (Class<D>) handler.getClass();
final AnnotatedType<D> at = beanManager.createAnnotatedType(handlerClass);
injectionTarget = beanManager.createInjectionTarget(at);
}
return injectionTarget;
}
#Override
public Class<?> getBeanClass() {
return daoInterface;
}
#Override
public Set<InjectionPoint> getInjectionPoints() {
// The underlying DaoFactory is not yet initialised, when this method is first called!
// Hence we do not use getInjectionTarget(), but getInjectionTargetOrNull(). Maybe this
// causes problems with injections inside the DAOs, but so far, they don't use injection
// and it does not matter. Additionally, they are RequestScoped and therefore the injection
// later *may* work fine. Cannot and do not need to test this now. Marco :-)
InjectionTarget<D> it = getInjectionTargetOrNull();
return it == null ? Collections.emptySet() : it.getInjectionPoints();
}
#Override
public String getName() {
return getBeanClass().getSimpleName();
}
#Override
public Set<Annotation> getQualifiers() {
Set<Annotation> qualifiers = new HashSet<Annotation>();
qualifiers.add(new AnnotationLiteral<Default>() {});
qualifiers.add(new AnnotationLiteral<Any>() {});
return qualifiers;
}
#Override
public Class<? extends Annotation> getScope() {
return RequestScoped.class;
}
#Override
public Set<Class<? extends Annotation>> getStereotypes() {
return Collections.emptySet();
}
#Override
public Set<Type> getTypes() {
Set<Type> types = new HashSet<>();
types.add(daoInterface); // TODO add more types?!
return types;
}
#Override
public D create(CreationalContext<D> creationalContext) {
D handler = DaoFactory.getDao(daoInterface);
InjectionTarget<D> it = getInjectionTarget();
it.inject(handler, creationalContext);
it.postConstruct(handler);
return handler;
}
#Override
public void destroy(D instance, CreationalContext<D> creationalContext) {
InjectionTarget<D> it = getInjectionTarget();
it.preDestroy(instance);
it.dispose(instance);
creationalContext.release();
}
#Override
public boolean isAlternative() {
return false;
}
#Override
public boolean isNullable() {
return false;
}
};
}
}
The idea is that it first collects all sub-interfaces of BaseDao that need to be injected. Then, it provides the factory for each of them.
Important: As already stated in the comments, it is necessary to put this extension in a separate JAR, which does not provide any services. As soon as I placed a class implementing Extension in the same JAR as a service implementation (e.g. published via #RequestScoped), the service was not found, anymore.
I have the following code to be unit tested:
public void foo() {
Entity entity = //...
persistence.save(entity);
entity.setDate(new Date());
persistence.save(entity);
}
I would like to verify that on the first invocation of persistence.save entity.getDate() returns null.
Therefore I'm unable to use Mockito.verify(/*...*/) because at that time the method foo already completed and entity.setDate(Date) was called.
So I think I need to do verifications of invocations already at the time the invocations happen. How do I do this using Mockito?
I created the following Answer implementation:
public class CapturingAnswer<T, R> implements Answer<T> {
private final Function<InvocationOnMock, R> capturingFunction;
private final List<R> capturedValues = new ArrayList<R>();
public CapturingAnswer(final Function<InvocationOnMock, R> capturingFunction) {
super();
this.capturingFunction = capturingFunction;
}
#Override
public T answer(final InvocationOnMock invocation) throws Throwable {
capturedValues.add(capturingFunction.apply(invocation));
return null;
}
public List<R> getCapturedValues() {
return Collections.unmodifiableList(capturedValues);
}
}
This answer captures properties of the invocations being made. The capturedValues can then be used for simple assertions. The implementation uses Java 8 API. If that is not available one would need to use an interface that is able to convert the InvocationOnMock to the captured value. The usage in the testcase is like this:
#Test
public void testSomething() {
CapturingAnswer<Void,Date> captureDates = new CapturingAnswer<>(this::getEntityDate)
Mockito.doAnswer(captureDates).when(persistence).save(Mockito.any(Entity.class));
service.foo();
Assert.assertNull(captureDates.getCapturedValues().get(0));
}
private Date getEntityDate(InvocationOnMock invocation) {
Entity entity = (Entity)invocation.getArguments()[0];
return entity.getDate();
}
The capturing that is done by the presented Answer implementation can't be achieved with Mockitos ArgumentCaptor because this is only used after the invocation of the method under test.
In my original comment, this was the answer I had in mind.
The class to be mocked:
class MockedClass{
void save(SomeBean sb){
//doStuff
}
}
The class we'll need to verify the Date object is null.
class SomeBean{
Date date;
Date getDate(){return date;}
void setDate(Date date){this.date=date;}
}
The class under test:
class TestClass{
MockedClass mc;
TestClass(MockedClass mc){this.mc = mc;}
void doWork(){
SomeBean sb = new SomeBean();
mc.save(sb);
sb.setDate(new Date());
mc.save(sb);
}
}
And the test case:
#Test
public void testAnswer(){
MockedClass mc = Mockito.mock(MockedClass.class);
Mockito.doAnswer(new Answer<Void>(){
boolean checkDate = true;
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
SomeBean sb = (SomeBean) invocation.getArguments()[0];
if(checkDate && sb.getDate() != null){
throw new NullPointerException(); //Or a more meaningful exception
}
checkDate = false;
return null;
}}).when(mc).save(Mockito.any(SomeBean.class));;
TestClass tc = new TestClass(mc);
tc.doWork();
}
The first time through this Answer (The term I should have used in my original comment), this will throw an exception and fail the test case if date is not null. The second time through, checkDate will be false, so the check will not be performed.
Having a dynamic proxy for an interface with default methods, how do I invoke a default method? By using something like defaultmethod.invoke(this, ...) you just get your proxy invocation handler called (Which is somehow correct, cause you have no implementing class for this interface).
I have a workaround using ASM to create a class implementing the interface and delegating such calls to an instance of this class. But this is not a good solution, especially if the default method calls other interface methods (you get a delegator ping-pong). The JLS is surprisingly silent about this question...
Here a small code example:
public class Java8Proxy implements InvocationHandler {
public interface WithDefaultMethod {
void someMethod();
default void someDefaultMethod() {
System.out.println("default method invoked!");
}
}
#Test
public void invokeTest() {
WithDefaultMethod proxy = (WithDefaultMethod) Proxy.newProxyInstance(
WithDefaultMethod.class.getClassLoader(),
new Class<?>[] { WithDefaultMethod.class }, this);
proxy.someDefaultMethod();
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// assuming not knowing the interface before runtime (I wouldn't use a
// proxy, would I?)
// what to do here to get the line printed out?
// This is just a loop
// method.invoke(this, args);
return null;
}
}
You can use the MethodHandles type in your InvocationHandler. This code is copied from Zero Turnaround.
Constructor<MethodHandles.Lookup> constructor;
Class<?> declaringClass;
Object result;
if (method.isDefault()) {
declaringClass = method.getDeclaringClass();
constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
constructor.setAccessible(true);
result = constructor.
newInstance(declaringClass, MethodHandles.Lookup.PRIVATE).
unreflectSpecial(method, declaringClass).
bindTo(proxy).
invokeWithArguments(args);
return(result);
}
The accepted answer uses setAccessible(true) to break into MethodHandles.Lookup, something that is restricted in Java 9 and beyond. This mail describes a JDK change that works for Java 9 or later.
It is possible to get this to work on Java 8 (and later) if you can get the writer of the interface to call your utility with an instance of MethodHandles.Lookup created in the interface (so it gets the permission to access the default methods of the interface):
interface HelloGenerator {
public static HelloGenerator createProxy() {
// create MethodHandles.Lookup here to get access to the default methods
return Utils.createProxy(MethodHandles.lookup(), HelloGenerator.class);
}
abstract String name();
default void sayHello() {
System.out.println("Hello " + name());
}
}
public class Utils {
static <P> P createProxy(MethodHandles.Lookup lookup, Class<P> type) {
InvocationHandler handler = (proxy, method, args) -> {
if (method.isDefault()) {
// can use unreflectSpecial here, but only because MethodHandles.Lookup
// instance was created in the interface and passed through
return lookup
.unreflectSpecial(method, method.getDeclaringClass())
.bindTo(proxy)
.invokeWithArguments(args);
}
return ...; // your desired proxy behaviour
};
Object proxy = Proxy.newProxyInstance(
type.getClassLoader(), new Class<?>[] {type}, handler);
return type.cast(proxy);
}
}
This approach won't handle all Java 8 use cases, but it did handle mine.
Since jdk-16 this is supported in a native way, via invokeDefault.
To your example, this would be done as:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class InvocationHandlerTest {
public static void main(String[] args) {
WithDefaultMethod proxy = (WithDefaultMethod) Proxy.newProxyInstance(
WithDefaultMethod.class.getClassLoader(),
new Class<?>[] { WithDefaultMethod.class }, new Java8Proxy());
proxy.someDefaultMethod();
}
interface WithDefaultMethod {
void someMethod();
default void someDefaultMethod() {
System.out.println("default method invoked!");
}
}
static class Java8Proxy implements InvocationHandler {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoked");
InvocationHandler.invokeDefault(proxy, method, args);
return null;
}
}
}
But you do not need an explicit implementation of the interface that you need, this can be done slightly different:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class InvocationHandlerTest {
public static void main(String[] args) {
WithDefaultMethod proxy = (WithDefaultMethod) Proxy.newProxyInstance(
WithDefaultMethod.class.getClassLoader(),
new Class<?>[] { WithDefaultMethod.class },
(o, m, params) -> {
if (m.isDefault()) {
// if it's a default method, invoke it
return InvocationHandler.invokeDefault(o, m, params);
}
return null;
});
proxy.someDefaultMethod();
}
interface WithDefaultMethod {
void someMethod();
default void someDefaultMethod() {
System.out.println("default method invoked!");
}
}
}
I wrote up a blog entry detailing the different approaches that must be used for Java 8 and 9+: http://netomi.github.io/2020/04/17/default-methods.html
It includes code from the spring framework to handle the different cases in a clean and efficient way.
This is annoyingly stupid counter-intuitive behaviour, which I assert is a bug in method#invoke(Object,Object[]), because you can't keep things simple in an InvocationHandler, like:
if (method.isDefault())
method.invoke(proxy, args);
else
method.invoke(target, args); // to call a wrapped object
So have to do a special lookup for a MethodHandle, and bind to proxy, to call, it.
I refined the McDowell provided code as follows (simplified):
private static final Constructor<MethodHandles.Lookup> lookupConstructor;
static {
try {
lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
lookupConstructor.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
private static MethodHandle findDefaultMethodHandle(Class<?> facadeInterface, Method m) {
try {
Class<?> declaringClass = m.getDeclaringClass();
// Used mode -1 = TRUST, because Modifier.PRIVATE failed for me in Java 8.
MethodHandles.Lookup lookup = lookupConstructor.newInstance(declaringClass, -1);
try {
return lookup.findSpecial(facadeInterface, m.getName(), MethodType.methodType(m.getReturnType(), m.getParameterTypes()), declaringClass);
} catch (IllegalAccessException e) {
try {
return lookup.unreflectSpecial(m, declaringClass);
} catch (IllegalAccessException x) {
x.addSuppressed(e);
throw x;
}
}
} catch (RuntimeException e) {
throw (RuntimeException) e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static class InvocationHandlerImpl implements InvocationHandler {
private final Class<?> facadeInterface;
private Object invokeDefault(Object proxy, Method method, Object[] args) throws Throwable {
MethodHandle mh = findDefaultMethodHandle(facadeInterface, m);
return mh.bindTo(proxy).invokeWithArguments(args);
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.isDefault()) {
return invokeDefault(proxy, method, args);
}
// rest of code method calls
}
}
facadeInterface is the interface being proxied, which declares the default method, it will probably be possible to use super-interface default methods too.
Non-toy code should do this lookup before invoke is called, or at least cache the MethodHandle.