Skip to content

Commit

Permalink
ArC - producers - change the client proxy class package
Browse files Browse the repository at this point in the history
- use the package of the return type/field type
- and use the produced type to test the app class
- previously, the package of the declaring class was used
- also a proxy class does not reference the bean class directly but performs a
lookup via the bean identifier when instantiated
- resolves quarkusio#22815
  • Loading branch information
mkouba committed Feb 18, 2022
1 parent 82878c0 commit f978548
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package io.quarkus.arc.test.unproxyable;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.IOException;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Instance;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Singleton;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.test.unproxyable.some.Resource;
import io.quarkus.test.QuarkusUnitTest;

// This test aims to test the https://github.com/quarkusio/quarkus/issues/22815 in Quarkus integration
// There is a duplicate test for ArC standalone: io.quarkus.arc.test.clientproxy.constructor.ProducerReturnTypePackagePrivateNoArgsConstructorTest
public class ProducerReturnTypePackagePrivateNoArgsConstructorTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot(root -> root
.addClasses(ProducerReturnTypePackagePrivateNoArgsConstructorTest.class, ResourceProducer.class,
Resource.class));

@Inject
Instance<Resource> instance;

@Test
public void testProducer() throws IOException {
assertTrue(instance.isResolvable());
assertEquals(5, instance.get().ping());
}

@Singleton
static class ResourceProducer {

@ApplicationScoped
@Produces
Resource resource() {
return Resource.from(5);
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.quarkus.arc.test.unproxyable.some;

public abstract class Resource {

Resource() {
}

public static Resource from(int ping) {
return new Resource() {

@Override
public int ping() {
return ping;
}
};
}

public abstract int ping();

}
Original file line number Diff line number Diff line change
Expand Up @@ -899,7 +899,7 @@ private List<BeanInfo> findBeans(Collection<DotName> beanDefiningAnnotations, Li

// non-inherited stuff:
for (MethodInfo method : beanClass.methods()) {
if (Methods.isSynthetic(method)) {
if (method.isSynthetic()) {
continue;
}
if (annotationStore.getAnnotations(method).isEmpty()) {
Expand All @@ -926,7 +926,7 @@ private List<BeanInfo> findBeans(Collection<DotName> beanDefiningAnnotations, Li
while (aClass != null) {
for (MethodInfo method : aClass.methods()) {
Methods.MethodKey methodDescriptor = new Methods.MethodKey(method);
if (Methods.isSynthetic(method) || Methods.isOverriden(methodDescriptor, methods)) {
if (method.isSynthetic() || Methods.isOverriden(methodDescriptor, methods)) {
continue;
}
methods.add(methodDescriptor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1796,7 +1796,7 @@ protected void implementIsSuppressed(BeanInfo bean, ClassCreator beanCreator) {

private String getProxyTypeName(BeanInfo bean, String baseName) {
StringBuilder proxyTypeName = new StringBuilder();
proxyTypeName.append(bean.getTargetPackageName());
proxyTypeName.append(bean.getClientProxyPackageName());
if (proxyTypeName.length() > 0) {
proxyTypeName.append(".");
}
Expand Down Expand Up @@ -1838,7 +1838,7 @@ private void initializeProxy(BeanInfo bean, String baseName, ClassCreator beanCr
// Create a new proxy instance, atomicity does not really matter here
BytecodeCreator proxyNull = proxy.ifNull(proxyInstance).trueBranch();
proxyNull.assign(proxyInstance, proxyNull.newInstance(
MethodDescriptor.ofConstructor(proxyTypeName, beanCreator.getClassName()), proxyNull.getThis()));
MethodDescriptor.ofConstructor(proxyTypeName, String.class), proxyNull.load(bean.getIdentifier())));
proxyNull.writeInstanceField(FieldDescriptor.of(beanCreator.getClassName(), FIELD_NAME_PROXY, proxyTypeName),
proxyNull.getThis(),
proxyInstance);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,22 @@ public String getTargetPackageName() {
return packageName;
}

public String getClientProxyPackageName() {
if (isProducerField() || isProducerMethod()) {
AnnotationTarget target = getTarget().get();
DotName typeName = target.kind() == Kind.FIELD ? target.asField().type().name()
: target.asMethod().returnType().name();
String packageName = DotNames.packageName(typeName);
if (packageName.startsWith("java.")) {
// It is not possible to place a class in a JDK package
packageName = AbstractGenerator.DEFAULT_PACKAGE;
}
return packageName;
} else {
return getTargetPackageName();
}
}

void validate(List<Throwable> errors, List<BeanDeploymentValidator> validators,
Consumer<BytecodeTransformer> bytecodeTransformerConsumer, Set<DotName> classesReceivingNoArgsCtor) {
Beans.validateBean(this, errors, validators, bytecodeTransformerConsumer, classesReceivingNoArgsCtor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import io.quarkus.arc.processor.ResourceOutput.Resource.SpecialType;
import io.quarkus.gizmo.BytecodeCreator;
import io.quarkus.gizmo.ClassCreator;
import io.quarkus.gizmo.DescriptorUtils;
import io.quarkus.gizmo.FieldCreator;
import io.quarkus.gizmo.FieldDescriptor;
import io.quarkus.gizmo.MethodCreator;
Expand Down Expand Up @@ -81,13 +80,14 @@ Collection<Resource> generate(BeanInfo bean, String beanClassName,
ProviderType providerType = new ProviderType(bean.getProviderType());
ClassInfo providerClass = getClassByName(bean.getDeployment().getBeanArchiveIndex(), providerType.name());
String baseName = getBaseName(bean, beanClassName);
String targetPackage = bean.getTargetPackageName();
String targetPackage = bean.getClientProxyPackageName();
String generatedName = generatedNameFromTarget(targetPackage, baseName, CLIENT_PROXY_SUFFIX);
if (existingClasses.contains(generatedName)) {
return Collections.emptyList();
}

ResourceClassOutput classOutput = new ResourceClassOutput(applicationClassPredicate.test(bean.getBeanClass()),
boolean applicationClass = applicationClassPredicate.test(getApplicationClassTestName(bean));
ResourceClassOutput classOutput = new ResourceClassOutput(applicationClass,
name -> name.equals(generatedName) ? SpecialType.CLIENT_PROXY : null, generateSources);

// Foo_ClientProxy extends Foo implements ClientProxy
Expand Down Expand Up @@ -122,7 +122,7 @@ Collection<Resource> generate(BeanInfo bean, String beanClassName,
}
Map<ClassInfo, Map<TypeVariable, Type>> resolvedTypeVariables = Types.resolvedTypeVariables(providerClass,
bean.getDeployment());
FieldCreator beanField = clientProxy.getFieldCreator(BEAN_FIELD, DescriptorUtils.extToInt(beanClassName))
FieldCreator beanField = clientProxy.getFieldCreator(BEAN_FIELD, InjectableBean.class)
.setModifiers(ACC_PRIVATE | ACC_FINAL);
if (mockable) {
clientProxy.getFieldCreator(MOCK_FIELD, providerType.descriptorName()).setModifiers(ACC_PRIVATE | ACC_VOLATILE);
Expand All @@ -134,7 +134,7 @@ Collection<Resource> generate(BeanInfo bean, String beanClassName,
.setModifiers(ACC_PRIVATE | ACC_FINAL);
}

createConstructor(clientProxy, beanClassName, superClass, beanField.getFieldDescriptor(),
createConstructor(clientProxy, bean.getIdentifier(), superClass, beanField.getFieldDescriptor(),
contextField != null ? contextField.getFieldDescriptor() : null);
implementDelegate(clientProxy, providerType, beanField.getFieldDescriptor(), bean);
implementGetContextualInstance(clientProxy, providerType);
Expand Down Expand Up @@ -255,16 +255,19 @@ private void implementMockMethods(ClassCreator clientProxy, ProviderType provide
set.returnValue(null);
}

void createConstructor(ClassCreator clientProxy, String beanClassName, String superClasName, FieldDescriptor beanField,
void createConstructor(ClassCreator clientProxy, String beanIdentifier, String superClasName, FieldDescriptor beanField,
FieldDescriptor contextField) {
MethodCreator creator = clientProxy.getMethodCreator(Methods.INIT, void.class, beanClassName);
MethodCreator creator = clientProxy.getMethodCreator(Methods.INIT, void.class, String.class);
creator.invokeSpecialMethod(MethodDescriptor.ofConstructor(superClasName), creator.getThis());
ResultHandle beanHandle = creator.getMethodParam(0);
ResultHandle containerHandle = creator.invokeStaticMethod(MethodDescriptors.ARC_CONTAINER);
ResultHandle beanIdentifierHandle = creator.getMethodParam(0);
ResultHandle beanHandle = creator.invokeInterfaceMethod(MethodDescriptors.ARC_CONTAINER_BEAN, containerHandle,
beanIdentifierHandle);
creator.writeInstanceField(beanField, creator.getThis(), beanHandle);
if (contextField != null) {
creator.writeInstanceField(contextField, creator.getThis(), creator.invokeInterfaceMethod(
MethodDescriptors.ARC_CONTAINER_GET_ACTIVE_CONTEXT,
creator.invokeStaticMethod(MethodDescriptors.ARC_CONTAINER), creator
containerHandle, creator
.invokeInterfaceMethod(MethodDescriptor.ofMethod(InjectableBean.class, "getScope", Class.class),
beanHandle)));
}
Expand Down Expand Up @@ -349,4 +352,17 @@ Collection<MethodInfo> getDelegatingMethods(BeanInfo bean, Consumer<BytecodeTran
return methods.values();
}

private DotName getApplicationClassTestName(BeanInfo bean) {
DotName testedName;
// For producers we need to test the produced type
if (bean.isProducerField()) {
testedName = bean.getTarget().get().asField().type().name();
} else if (bean.isProducerMethod()) {
testedName = bean.getTarget().get().asMethod().returnType().name();
} else {
testedName = bean.getBeanClass();
}
return testedName;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,9 @@ final class Methods {
public static final String INIT = "<init>";
// static initializer
public static final String CLINIT = "<clinit>";
// copied from java.lang.reflect.Modifier.SYNTHETIC
static final int SYNTHETIC = 0x00001000;
// copied from java.lang.reflect.Modifier.BRIDGE
static final int BRIDGE = 0x00000040;

public static final String TO_STRING = "toString";

private static final List<String> IGNORED_METHODS = initIgnoredMethods();
Expand All @@ -60,10 +59,6 @@ private static List<String> initIgnoredMethods() {
private Methods() {
}

static boolean isSynthetic(MethodInfo method) {
return (method.flags() & SYNTHETIC) != 0;
}

static boolean isBridge(MethodInfo method) {
return (method.flags() & BRIDGE) != 0;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.quarkus.arc.test.clientproxy.constructor;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import io.quarkus.arc.Arc;
import io.quarkus.arc.ClientProxy;
import io.quarkus.arc.test.ArcTestContainer;
import io.quarkus.arc.test.clientproxy.constructor.some.Resource;
import java.io.IOException;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Singleton;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

// This test aims to test the https://github.com/quarkusio/quarkus/issues/22815 in ArC standalone
// There is a duplicate test for Quarkus integration: io.quarkus.arc.test.unproxyable.ProducerReturnTypePackagePrivateNoArgsConstructorTest
public class ProducerReturnTypePackagePrivateNoArgsConstructorTest {

@RegisterExtension
public ArcTestContainer container = new ArcTestContainer(ResourceProducer.class);

@Test
public void testProducer() throws IOException {
Resource res = Arc.container().instance(Resource.class).get();
assertNotNull(res);
assertTrue(res instanceof ClientProxy);
assertEquals(5, res.ping());
}

@Singleton
static class ResourceProducer {

@ApplicationScoped
@Produces
Resource resource() {
return Resource.from(5);
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.quarkus.arc.test.clientproxy.constructor.some;

public abstract class Resource {

Resource() {
}

public static Resource from(int ping) {
return new Resource() {

@Override
public int ping() {
return ping;
}
};
}

public abstract int ping();

}

0 comments on commit f978548

Please sign in to comment.