Skip to content

Commit

Permalink
Merge pull request #29155 from manovotn/issue29128
Browse files Browse the repository at this point in the history
Include interfaces as bean types of RR resources
  • Loading branch information
geoand authored Nov 10, 2022
2 parents 38acaed + 47697e2 commit 2e6008e
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -733,7 +734,8 @@ private FilterClassIntrospector createFilterClassIntrospector() {
public void transformEndpoints(
ResourceScanningResultBuildItem resourceScanningResultBuildItem,
ResourceInterceptorsBuildItem resourceInterceptorsBuildItem,
BuildProducer<io.quarkus.arc.deployment.AnnotationsTransformerBuildItem> annotationsTransformer) {
BuildProducer<io.quarkus.arc.deployment.AnnotationsTransformerBuildItem> annotationsTransformer,
BeanArchiveIndexBuildItem beanArchiveIndexBuildItem) {

// all found resources and sub-resources
Set<DotName> allResources = new HashSet<>();
Expand Down Expand Up @@ -774,7 +776,7 @@ public void transform(TransformationContext context) {
// check if the class is one of resources/sub-resources
if (allResources.contains(clazz.name())
&& clazz.declaredAnnotation(ResteasyReactiveDotNames.TYPED) == null) {
context.transform().add(createTypedAnnotationInstance(clazz)).done();
context.transform().add(createTypedAnnotationInstance(clazz, beanArchiveIndexBuildItem)).done();
return;
}
// check if the class is one of providers, either explicitly declaring the annotation
Expand All @@ -783,17 +785,52 @@ public void transform(TransformationContext context) {
|| filtersAndInterceptors.contains(clazz.name().toString()))
&& clazz.declaredAnnotation(ResteasyReactiveDotNames.TYPED) == null) {
// Add @Typed(MyResource.class)
context.transform().add(createTypedAnnotationInstance(clazz)).done();
context.transform().add(createTypedAnnotationInstance(clazz, beanArchiveIndexBuildItem)).done();
}
}
}));
}

private AnnotationInstance createTypedAnnotationInstance(ClassInfo clazz) {
private AnnotationInstance createTypedAnnotationInstance(ClassInfo clazz,
BeanArchiveIndexBuildItem beanArchiveIndexBuildItem) {
Set<DotName> interfaceNames = new HashSet<>();
ClassInfo currentClazz = clazz;
while (!ResteasyReactiveDotNames.OBJECT.equals(currentClazz.name())) {
currentClazz.interfaceNames().forEach(iface -> interfaceNames.add(iface));
// inspect super class
currentClazz = beanArchiveIndexBuildItem.getIndex().getClassByName(currentClazz.superName());
}
Set<DotName> allInterfaces = new HashSet<>();
recursiveInterfaceSearch(interfaceNames, allInterfaces, beanArchiveIndexBuildItem);
AnnotationValue[] annotationValues = new AnnotationValue[allInterfaces.size() + 1];
// always add the bean impl class
annotationValues[0] = AnnotationValue.createClassValue("value",
Type.create(clazz.name(), Type.Kind.CLASS));
Iterator<DotName> iterator = allInterfaces.iterator();
for (int i = 1; i < annotationValues.length; i++) {
annotationValues[i] = AnnotationValue.createClassValue("value",
Type.create(iterator.next(), Type.Kind.CLASS));
}
return AnnotationInstance.create(ResteasyReactiveDotNames.TYPED, clazz,
new AnnotationValue[] { AnnotationValue.createArrayValue("value",
new AnnotationValue[] { AnnotationValue.createClassValue("value",
Type.create(clazz.name(), Type.Kind.CLASS)) }) });
annotationValues) });
}

private void recursiveInterfaceSearch(Set<DotName> interfacesToProcess, Set<DotName> allDiscoveredInterfaces,
BeanArchiveIndexBuildItem beanArchiveIndexBuildItem) {
allDiscoveredInterfaces.addAll(interfacesToProcess);
Set<DotName> additionalInterfacesToProcess = new HashSet<>();
for (DotName name : interfacesToProcess) {
ClassInfo clazz = beanArchiveIndexBuildItem.getIndex().getClassByName(name);
if (clazz != null) {
// get all interface that this interface extends
additionalInterfacesToProcess.addAll(clazz.interfaceNames());
}
}
if (!additionalInterfacesToProcess.isEmpty()) {
// recursively process newly found interfaces
recursiveInterfaceSearch(additionalInterfacesToProcess, allDiscoveredInterfaces, beanArchiveIndexBuildItem);
}
}

private Collection<DotName> additionalContextTypes(List<ContextTypeBuildItem> contextTypeBuildItems) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package io.quarkus.rest.client.reactive.beanTypes;

public interface Alpha extends Delta {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package io.quarkus.rest.client.reactive.beanTypes;

public interface Beta extends Delta {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package io.quarkus.rest.client.reactive.beanTypes;

public interface Charlie extends Beta {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.quarkus.rest.client.reactive.beanTypes;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

@Path("/")
@RegisterRestClient(configKey = "hello2")
public interface Client extends Alpha {
@GET
@Produces(MediaType.TEXT_PLAIN)
String test();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.quarkus.rest.client.reactive.beanTypes;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Alternative;

import org.eclipse.microprofile.rest.client.inject.RestClient;

import io.quarkus.arc.Priority;

@Alternative()
@Priority(1)
@ApplicationScoped
@RestClient
public class ClientMock implements Client, Charlie {

@Override
public String test() {
return "hello from " + ClientMock.class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package io.quarkus.rest.client.reactive.beanTypes;

public interface Delta {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.rest.client.reactive.beanTypes;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

import org.eclipse.microprofile.rest.client.inject.RestClient;

@ApplicationScoped
public class MyBean {

@Inject
@RestClient
Client client;

String test() {
return client.test();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package io.quarkus.rest.client.reactive.beanTypes;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.Set;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.inject.Inject;

import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.Arc;
import io.quarkus.test.QuarkusUnitTest;

/**
* Tests that resources processed by RR have bean types equivalent to that of the impl class plus all interfaces in
* their hierarchy
*/
public class ResourceBeanTypeTest {

@RegisterExtension
static final QuarkusUnitTest TEST = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(Client.class, ClientMock.class, MyBean.class, Alpha.class, Beta.class,
Charlie.class, Delta.class));

@Inject
MyBean myBean;

@Test
void shouldHaveAllInterfaceTypes() {
// firstly, sanity check
assertThat(myBean.test()).isEqualTo("hello from " + ClientMock.class);

// now see what types does the Client bean have - the impl class and all interfaces should be in place
BeanManager beanManager = Arc.container().beanManager();
Set<Bean<?>> beans = beanManager.getBeans(Client.class, RestClient.LITERAL);
Bean<?> resolvedBean = beanManager.resolve(beans);
assertThat(resolvedBean.getScope()).isEqualTo(ApplicationScoped.class);
assertThat(resolvedBean.getTypes()).contains(Client.class, ClientMock.class, Alpha.class, Beta.class, Charlie.class,
Delta.class);
}
}

0 comments on commit 2e6008e

Please sign in to comment.