Skip to content

Commit

Permalink
Make ArC intercept default methods
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartwdouglas committed May 21, 2020
1 parent a848498 commit 46554a0
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import static io.quarkus.arc.processor.IndexClassLookupUtils.getClassByName;

import io.quarkus.gizmo.DescriptorUtils;
import io.quarkus.gizmo.Gizmo;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -15,6 +13,7 @@
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
Expand All @@ -27,8 +26,11 @@
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import io.quarkus.gizmo.DescriptorUtils;
import io.quarkus.gizmo.Gizmo;

/**
*
*
* @author Martin Kouba
* @author Michal Szynkiewicz, [email protected]
*/
Expand Down Expand Up @@ -186,6 +188,14 @@ public MethodVisitor visitMethod(int access, String name, String descriptor, Str
classLevelBindings, bytecodeTransformerConsumer, transformUnproxyableClasses));
}
}
for (DotName i : classInfo.interfaceNames()) {
ClassInfo interfaceInfo = getClassByName(beanDeployment.getIndex(), i);
if (interfaceInfo != null) {
//interfaces can't have final methods
addInterceptedMethodCandidates(beanDeployment, interfaceInfo, candidates,
classLevelBindings, bytecodeTransformerConsumer, transformUnproxyableClasses);
}
}
return finalMethodsFoundAndNotChanged;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;

/**
Expand Down Expand Up @@ -46,7 +50,7 @@ private Reflections() {
}

/**
*
*
* @param clazz
* @param fieldName
* @return the field declared in the class hierarchy
Expand All @@ -67,7 +71,7 @@ private static Field findFieldInternal(Class<?> clazz, String fieldName) {
}

/**
*
*
* @param clazz
* @param methodName
* @param parameterTypes
Expand All @@ -78,14 +82,32 @@ public static Method findMethod(Class<?> clazz, String methodName, Class<?>... p
}

private static Method findMethodInternal(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
try {
return clazz.getDeclaredMethod(methodName, parameterTypes);
} catch (NoSuchMethodException e) {
if (clazz.getSuperclass() != null) {
return findMethodInternal(clazz.getSuperclass(), methodName, parameterTypes);
Class<?> theClass = clazz;
Deque<Class<?>> interfaces = new ArrayDeque<>();
while (theClass != null) {
try {
return theClass.getDeclaredMethod(methodName, parameterTypes);
} catch (NoSuchMethodException e) {
interfaces.addAll(Arrays.asList(theClass.getInterfaces()));
theClass = theClass.getSuperclass();
}
}
//look for default methods on interfaces
Set<Class<?>> seen = new HashSet<>(interfaces);
while (!interfaces.isEmpty()) {
Class<?> iface = interfaces.pop();
try {
return iface.getDeclaredMethod(methodName, parameterTypes);
} catch (NoSuchMethodException ex) {
//ignore
}
for (Class<?> extra : iface.getInterfaces()) {
if (seen.add(extra)) {
interfaces.add(extra);
}
}
throw new IllegalArgumentException(e);
}
throw new IllegalArgumentException("Cannot find method " + methodName + Arrays.asList(parameterTypes) + " on " + clazz);
}

public static Constructor<?> findConstructor(Class<?> clazz, Class<?>... parameterTypes) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.arc.test.interceptors.defaultmethod;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;

@Target({ TYPE, METHOD })
@Retention(RUNTIME)
@Documented
@InterceptorBinding
public @interface ABinding {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.quarkus.arc.test.interceptors.defaultmethod;

import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
@ABinding
public class DefaultMethodBean implements DefaultMethodInterface {

public String hello() {
return "hello";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.quarkus.arc.test.interceptors.defaultmethod;

import io.quarkus.arc.Arc;
import io.quarkus.arc.ArcContainer;
import io.quarkus.arc.InstanceHandle;
import io.quarkus.arc.test.ArcTestContainer;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

public class DefaultMethodInterceptorTest {

@RegisterExtension
public ArcTestContainer container = new ArcTestContainer(ABinding.class, DefaultMethodBean.class,
DefaultMethodInterface.class, MessageInterceptor.class);

@Test
public void testInterception() {
ArcContainer arc = Arc.container();

InstanceHandle<DefaultMethodBean> handle = arc.instance(DefaultMethodBean.class);
DefaultMethodBean simpleBean = handle.get();
Assertions.assertEquals("intercepted:hello", simpleBean.hello());
Assertions.assertEquals("intercepted:default method", simpleBean.defaultMethod());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.quarkus.arc.test.interceptors.defaultmethod;

public interface DefaultMethodInterface {

default String defaultMethod() {
return "default method";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.quarkus.arc.test.interceptors.defaultmethod;

import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

@ABinding
@Interceptor
public class MessageInterceptor {

@AroundInvoke
public Object invoke(InvocationContext context) throws Exception {
return "intercepted:" + context.proceed();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.quarkus.it.panache;

import javax.persistence.Entity;

import io.quarkus.hibernate.orm.panache.PanacheEntity;

@Entity
public class Beer extends PanacheEntity {

public String name;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.quarkus.it.panache;

import java.util.List;

import javax.enterprise.context.ApplicationScoped;
import javax.transaction.Transactional;

import io.quarkus.hibernate.orm.panache.PanacheRepository;

@ApplicationScoped
@Transactional
public class BeerRepository implements PanacheRepository<Beer> {
public List<Beer> findOrdered() {
return find("ORDER BY name").list();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.quarkus.it.panache;

import javax.inject.Inject;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
public class TransactionalRepositoryTest {

@Inject
BeerRepository beerRepository;

@Test
public void testTransactionalRepository() {
Beer b = new Beer();
b.name = "IPA";
beerRepository.persist(b);

Assertions.assertEquals(1, beerRepository.count());
}

}

0 comments on commit 46554a0

Please sign in to comment.