Skip to content

Commit

Permalink
Merge pull request #691 from mkouba/issue-690-mp-rest-bean-registrar
Browse files Browse the repository at this point in the history
RestClientProcessor - replace generated bean classes with BeanRegistrar
  • Loading branch information
mkouba committed Feb 1, 2019
2 parents 0a09e82 + 151464c commit a0edcf0
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,8 @@
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.ws.rs.Path;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.ClientResponseFilter;

Expand All @@ -37,17 +32,17 @@
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.protean.gizmo.ClassCreator;
import org.jboss.protean.gizmo.ClassOutput;
import org.jboss.protean.gizmo.MethodCreator;
import org.jboss.protean.arc.processor.BeanConfigurator;
import org.jboss.protean.arc.processor.BeanRegistrar;
import org.jboss.protean.arc.processor.ScopeInfo;
import org.jboss.protean.gizmo.MethodDescriptor;
import org.jboss.protean.gizmo.ResultHandle;
import org.jboss.resteasy.client.jaxrs.internal.proxy.ResteasyClientProxy;
import org.jboss.resteasy.spi.ResteasyConfiguration;
import org.jboss.shamrock.deployment.annotations.BuildProducer;
import org.jboss.shamrock.deployment.annotations.BuildStep;
import org.jboss.shamrock.arc.deployment.AdditionalBeanBuildItem;
import org.jboss.shamrock.arc.deployment.GeneratedBeanBuildItem;
import org.jboss.shamrock.arc.deployment.BeanRegistrarBuildItem;
import org.jboss.shamrock.deployment.builditem.CombinedIndexBuildItem;
import org.jboss.shamrock.deployment.builditem.FeatureBuildItem;
import org.jboss.shamrock.deployment.builditem.GeneratedClassBuildItem;
Expand All @@ -59,19 +54,10 @@
import org.jboss.shamrock.restclient.runtime.RestClientProxy;

class RestClientProcessor {

private static final DotName REST_CLIENT = DotName.createSimple(RestClient.class.getName());

private static final DotName[] CLIENT_ANNOTATIONS = {
DotName.createSimple("javax.ws.rs.GET"),
DotName.createSimple("javax.ws.rs.HEAD"),
DotName.createSimple("javax.ws.rs.DELETE"),
DotName.createSimple("javax.ws.rs.OPTIONS"),
DotName.createSimple("javax.ws.rs.PATCH"),
DotName.createSimple("javax.ws.rs.POST"),
DotName.createSimple("javax.ws.rs.PUT"),
DotName.createSimple("javax.ws.rs.PUT"),
DotName.createSimple(RegisterRestClient.class.getName()),
DotName.createSimple(Path.class.getName())
};
private static final DotName REGISTER_REST_CLIENT = DotName.createSimple(RegisterRestClient.class.getName());

@Inject
BuildProducer<GeneratedClassBuildItem> generatedClass;
Expand All @@ -88,11 +74,17 @@ class RestClientProcessor {
@Inject
BuildProducer<SubstrateResourceBuildItem> resources;

@Inject
BuildProducer<BeanRegistrarBuildItem> beanRegistrars;

@Inject
BuildProducer<FeatureBuildItem> feature;

@Inject
CombinedIndexBuildItem combinedIndexBuildItem;

@BuildStep
public void build(BuildProducer<GeneratedBeanBuildItem> generatedBeans, BuildProducer<FeatureBuildItem> feature) throws Exception {
public void build() throws Exception {
feature.produce(new FeatureBuildItem(FeatureBuildItem.MP_REST_CLIENT));
reflectiveClass.produce(new ReflectiveClassBuildItem(false, false,
DefaultResponseExceptionMapper.class.getName(),
Expand All @@ -109,57 +101,58 @@ public void build(BuildProducer<GeneratedBeanBuildItem> generatedBeans, BuildPro
reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"));
reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, "org.jboss.resteasy.plugins.providers.jsonb.JsonBindingProvider", "org.jboss.resteasy.plugins.providers.jsonb.AbstractJsonBindingProvider"));
proxyDefinition.produce(new SubstrateProxyDefinitionBuildItem(ResteasyConfiguration.class.getName()));

// According to the spec only rest client interfaces annotated with RegisterRestClient are registered as beans
Map<DotName, ClassInfo> interfaces = new HashMap<>();
for (DotName type : CLIENT_ANNOTATIONS) {
for (AnnotationInstance annotation : combinedIndexBuildItem.getIndex().getAnnotations(type)) {
AnnotationTarget target = annotation.target();
ClassInfo theInfo;
if (target.kind() == AnnotationTarget.Kind.CLASS) {
theInfo = target.asClass();
} else if (target.kind() == AnnotationTarget.Kind.METHOD) {
theInfo = target.asMethod().declaringClass();
} else {
continue;
}
if (!Modifier.isInterface(theInfo.flags())) {
continue;
}
interfaces.put(theInfo.name(), theInfo);
for (AnnotationInstance annotation : combinedIndexBuildItem.getIndex().getAnnotations(REGISTER_REST_CLIENT)) {
AnnotationTarget target = annotation.target();
ClassInfo theInfo;
if (target.kind() == AnnotationTarget.Kind.CLASS) {
theInfo = target.asClass();
} else if (target.kind() == AnnotationTarget.Kind.METHOD) {
theInfo = target.asMethod().declaringClass();
} else {
continue;
}
if (!Modifier.isInterface(theInfo.flags())) {
continue;
}
interfaces.put(theInfo.name(), theInfo);
}

if (interfaces.isEmpty()) {
return;
}

for (Map.Entry<DotName, ClassInfo> entry : interfaces.entrySet()) {
String iName = entry.getKey().toString();
proxyDefinition.produce(new SubstrateProxyDefinitionBuildItem(iName, ResteasyClientProxy.class.getName()));
proxyDefinition.produce(new SubstrateProxyDefinitionBuildItem(iName, RestClientProxy.class.getName()));
reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, iName));

//now generate CDI beans
//TODO: do we need to check if CDI is enabled? Are we just assuming it always is?
String className = iName + "$$RestClientProxy";
AtomicReference<byte[]> bytes = new AtomicReference<>();
try (ClassCreator creator = new ClassCreator(new ClassOutput() {
@Override
public void write(String name, byte[] data) {
bytes.set(data);
generatedClass.produce(new GeneratedClassBuildItem(true, name, data));
}

BeanRegistrar beanRegistrar = new BeanRegistrar() {

@Override
public void register(RegistrationContext registrationContext) {
for (Map.Entry<DotName, ClassInfo> entry : interfaces.entrySet()) {
BeanConfigurator<Object> configurator = registrationContext.configure(entry.getKey());
// The spec is not clear whether we should add superinterfaces too - let's keep aligned with SmallRye for now
configurator.addType(entry.getKey());
// We use @Singleton here as we do not need another proxy
configurator.scope(ScopeInfo.SINGLETON);
configurator.addQualifier(REST_CLIENT);
configurator.creator(m -> {
// return new RestClientBase(proxyType).create();
ResultHandle interfaceHandle = m.loadClass(entry.getKey().toString());
ResultHandle baseHandle = m.newInstance(MethodDescriptor.ofConstructor(RestClientBase.class, Class.class), interfaceHandle);
ResultHandle ret = m.invokeVirtualMethod(MethodDescriptor.ofMethod(RestClientBase.class, "create", Object.class), baseHandle);
m.returnValue(ret);
});
configurator.done();
}
}, className, null, RestClientBase.class.getName())) {

creator.addAnnotation(Dependent.class);
MethodCreator producer = creator.getMethodCreator("producerMethod", iName);
producer.addAnnotation(Produces.class);
producer.addAnnotation(RestClient.class);
producer.addAnnotation(ApplicationScoped.class);

ResultHandle ret = producer.invokeVirtualMethod(MethodDescriptor.ofMethod(RestClientBase.class, "create", Object.class), producer.getThis());
producer.returnValue(ret);

MethodCreator ctor = creator.getMethodCreator(MethodDescriptor.ofConstructor(className));
ctor.invokeSpecialMethod(MethodDescriptor.ofConstructor(RestClientBase.class, Class.class), ctor.getThis(), ctor.loadClass(iName));
ctor.returnValue(null);
}
generatedBeans.produce(new GeneratedBeanBuildItem(className, bytes.get()));
}
};
beanRegistrars.produce(new BeanRegistrarBuildItem(beanRegistrar));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;

import javax.enterprise.context.spi.CreationalContext;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Type;
Expand Down Expand Up @@ -70,18 +72,14 @@ public final class BeanConfigurator<T> {

/**
*
* @param implClass
* @param implClassName
* @param beanDeployment
* @param beanConsumer
*/
BeanConfigurator(Class<?> implClass, BeanDeployment beanDeployment, Consumer<BeanInfo> beanConsumer) {
BeanConfigurator(DotName implClassName, BeanDeployment beanDeployment, Consumer<BeanInfo> beanConsumer) {
this.implClass = beanDeployment.getIndex().getClassByName(Objects.requireNonNull(implClassName));
this.beanDeployment = beanDeployment;
this.beanConsumer = beanConsumer;
this.implClass = beanDeployment.getIndex().getClassByName(DotName.createSimple(implClass.getName()));
if (this.implClass == null) {
// TODO we have a problem
throw new IllegalArgumentException();
}
this.types = new HashSet<>();
this.qualifiers = new HashSet<>();
this.scope = ScopeInfo.DEPENDENT;
Expand Down Expand Up @@ -132,6 +130,16 @@ public BeanConfigurator<T> types(Type... types) {
Collections.addAll(this.types, types);
return this;
}

public BeanConfigurator<T> addType(DotName className) {
this.types.add(Type.create(className, Kind.CLASS));
return this;
}

public BeanConfigurator<T> addQualifier(DotName annotationName) {
this.qualifiers.add(AnnotationInstance.create(annotationName, null, new AnnotationValue[] {}));
return this;
}

public BeanConfigurator<T> qualifiers(AnnotationInstance... qualifiers) {
Collections.addAll(this.qualifiers, qualifiers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ public class BeanDeployment {
RegistrationContext registrationContext = new RegistrationContext() {

@Override
public <T> BeanConfigurator<T> configure(Class<?> beanClass) {
return new BeanConfigurator<T>(beanClass, BeanDeployment.this, beans::add);
public <T> BeanConfigurator<T> configure(DotName beanClassName) {
return new BeanConfigurator<T>(beanClassName, BeanDeployment.this, beans::add);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,13 @@ Collection<Resource> generateSyntheticBean(BeanInfo bean, ReflectionRegistration
} else {
baseName = DotNames.simpleName(bean.getImplClazz().name());
}
baseName += SYNTHETIC_SUFFIX;

Type providerType = bean.getProviderType();
ClassInfo providerClass = bean.getDeployment().getIndex().getClassByName(providerType.name());
String providerTypeName = providerClass.name().toString();
String targetPackage = getPackageName(bean);
String generatedName = targetPackage.replace('.', '/') + "/" + baseName + SYNTHETIC_SUFFIX + BEAN_SUFFIX;
String generatedName = targetPackage.replace('.', '/') + "/" + baseName + BEAN_SUFFIX;

boolean isApplicationClass = applicationClassPredicate.test(bean.getImplClazz().name());
ResourceClassOutput classOutput = new ResourceClassOutput(isApplicationClass, name -> name.equals(generatedName) ? SpecialType.BEAN : null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.jboss.protean.arc.processor;

import org.jboss.jandex.DotName;
import org.jboss.protean.arc.InjectableBean;

/**
Expand All @@ -31,15 +32,18 @@ public interface BeanRegistrar extends BuildExtension {
*/
void register(RegistrationContext registrationContext);


interface RegistrationContext extends BuildContext {

/**
*
* @param beanClass
* @return a new synthetic bean builder
*/
<T> BeanConfigurator<T> configure(Class<?> beanClass);
<T> BeanConfigurator<T> configure(DotName beanClassName);

default <T> BeanConfigurator<T> configure(Class<?> beanClass) {
return configure(DotName.createSimple(beanClass.getName()));
}

// TODO add synthetic observer?

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ Collection<MethodInfo> getDelegatingMethods(BeanInfo bean) {
resolved = Types.buildResolvedMap(producerField.type().asParameterizedType().arguments(), fieldClass.typeParameters(), Collections.emptyMap());
}
Methods.addDelegatingMethods(bean.getDeployment().getIndex(), fieldClass, resolved, methods);
} else if (bean.isSynthetic()) {
Methods.addDelegatingMethods(bean.getDeployment().getIndex(), bean.getImplClazz(), Collections.emptyMap(), methods);
}
return methods.values();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,43 +16,16 @@

package org.jboss.protean.arc;

import java.lang.annotation.Annotation;
import java.util.Set;

import javax.enterprise.inject.spi.InterceptionType;
import javax.interceptor.InvocationContext;
import javax.enterprise.inject.spi.Interceptor;

/**
* Represents an injectable interceptor bean. It is an alternative to {@link javax.enterprise.inject.spi.Interceptor}.
* Represents an interceptor bean.
*
* @author Martin Kouba
*
* @param <T>
*/
public interface InjectableInterceptor<T> extends InjectableBean<T> {

/**
*
* @return the interceptor bindings
*/
Set<Annotation> getInterceptorBindings();

/**
*
* @param type
* @return {@code true} if this interceptor intercepts the given kind of interception, {@code false} othewise
*/
boolean intercepts(InterceptionType type);

/**
*
* @param type
* @param instance
* @param ctx
* @return the invocation return value
* @throws Exception
*/
Object intercept(InterceptionType type, T instance, InvocationContext ctx) throws Exception;
public interface InjectableInterceptor<T> extends InjectableBean<T>, Interceptor<T> {

/**
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ public class ClientResource {
@GET
@Path("/manual")
public String manual() throws Exception {

RestInterface iface = RestClientBuilder.newBuilder()
.baseUrl(new URL("http", "localhost", 8080, "/"))
.build(RestInterface.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import javax.ws.rs.GET;
import javax.ws.rs.Path;

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

@RegisterRestClient
@Path("/test")
public interface RestInterface {

Expand Down

0 comments on commit a0edcf0

Please sign in to comment.