Skip to content

Commit

Permalink
ArC: optimize client proxy delegate access for request context
Browse files Browse the repository at this point in the history
- if only one request context is registered
- similar to how we optimize the client proxy delegate access for
application context
  • Loading branch information
mkouba committed Oct 24, 2023
1 parent b334097 commit 4ef44f9
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.stream.Collectors;

import jakarta.annotation.Priority;
import jakarta.enterprise.context.RequestScoped;

import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
Expand Down Expand Up @@ -201,8 +202,15 @@ public List<Resource> generateResources(ReflectionRegistration reflectionRegistr
beanGenerator.precomputeGeneratedName(bean);
}

CustomAlterableContextsGenerator alterableContextsGenerator = new CustomAlterableContextsGenerator(generateSources);
List<CustomAlterableContextInfo> alterableContexts = customAlterableContexts.getRegistered();

boolean optimizeClientProxyRequestDelegate = !beanDeployment.getCustomContexts()
.containsKey(BuiltinScope.REQUEST.getInfo())
&& alterableContexts.stream().noneMatch(c -> c.scopeAnnotation.equals(RequestScoped.class));

ClientProxyGenerator clientProxyGenerator = new ClientProxyGenerator(applicationClassPredicate, generateSources,
allowMocking, refReg, existingClasses);
allowMocking, refReg, existingClasses, optimizeClientProxyRequestDelegate);

InterceptorGenerator interceptorGenerator = new InterceptorGenerator(annotationLiterals, applicationClassPredicate,
privateMembers, generateSources, refReg, existingClasses, beanToGeneratedName,
Expand Down Expand Up @@ -233,15 +241,12 @@ public List<Resource> generateResources(ReflectionRegistration reflectionRegistr
}

ContextInstancesGenerator contextInstancesGenerator = new ContextInstancesGenerator(generateSources,
reflectionRegistration, beanDeployment, scopeToGeneratedName);
refReg, beanDeployment, scopeToGeneratedName);
if (optimizeContexts) {
contextInstancesGenerator.precomputeGeneratedName(BuiltinScope.APPLICATION.getName());
contextInstancesGenerator.precomputeGeneratedName(BuiltinScope.REQUEST.getName());
}

CustomAlterableContextsGenerator alterableContextsGenerator = new CustomAlterableContextsGenerator(generateSources);
List<CustomAlterableContextInfo> alterableContexts = customAlterableContexts.getRegistered();

List<Resource> resources = new ArrayList<>();

if (executor != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,17 @@ public class ClientProxyGenerator extends AbstractGenerator {
private final Predicate<DotName> applicationClassPredicate;
private final boolean mockable;
private final Set<String> existingClasses;
// We optimize the access to the delegate if a single request context is present
private final boolean optimizeClientProxyRequestDelegate;

public ClientProxyGenerator(Predicate<DotName> applicationClassPredicate, boolean generateSources, boolean mockable,
ReflectionRegistration reflectionRegistration, Set<String> existingClasses) {
ReflectionRegistration reflectionRegistration, Set<String> existingClasses,
boolean optimizeClientProxyRequestDelegate) {
super(generateSources, reflectionRegistration);
this.applicationClassPredicate = applicationClassPredicate;
this.mockable = mockable;
this.existingClasses = existingClasses;
this.optimizeClientProxyRequestDelegate = optimizeClientProxyRequestDelegate;
}

/**
Expand Down Expand Up @@ -136,8 +140,9 @@ Collection<Resource> generate(BeanInfo bean, String beanClassName,
clientProxy.getFieldCreator(MOCK_FIELD, providerType.descriptorName()).setModifiers(ACC_PRIVATE | ACC_VOLATILE);
}
FieldCreator contextField = null;
if (BuiltinScope.APPLICATION.is(bean.getScope())) {
// It is safe to store the application context instance on the proxy
if (BuiltinScope.APPLICATION.is(bean.getScope())
|| (BuiltinScope.REQUEST.is(bean.getScope()) && optimizeClientProxyRequestDelegate)) {
// It is safe to store the context instance on the proxy
contextField = clientProxy.getFieldCreator(CONTEXT_FIELD, InjectableContext.class)
.setModifiers(ACC_PRIVATE | ACC_FINAL);
}
Expand Down Expand Up @@ -275,11 +280,14 @@ void createConstructor(ClassCreator clientProxy, String superClasName, FieldDesc
beanIdentifierHandle);
creator.writeInstanceField(beanField, creator.getThis(), beanHandle);
if (contextField != null) {
creator.writeInstanceField(contextField, creator.getThis(), creator.invokeInterfaceMethod(
MethodDescriptors.ARC_CONTAINER_GET_ACTIVE_CONTEXT,
// At this point we can be sure there's only one context implementation available
ResultHandle contextList = creator.invokeInterfaceMethod(
MethodDescriptors.ARC_CONTAINER_GET_CONTEXTS,
containerHandle, creator
.invokeInterfaceMethod(MethodDescriptor.ofMethod(InjectableBean.class, "getScope", Class.class),
beanHandle)));
beanHandle));
creator.writeInstanceField(contextField, creator.getThis(),
creator.invokeInterfaceMethod(MethodDescriptors.LIST_GET, contextList, creator.load(0)));
}
creator.returnValue(null);
}
Expand All @@ -305,6 +313,12 @@ void implementDelegate(ClassCreator clientProxy, ProviderType providerType, Fiel
FieldDescriptor.of(clientProxy.getClassName(), CONTEXT_FIELD, InjectableContext.class),
creator.getThis()),
beanHandle));
} else if (optimizeClientProxyRequestDelegate && BuiltinScope.REQUEST.is(bean.getScope())) {
creator.returnValue(creator.invokeStaticMethod(MethodDescriptors.CLIENT_PROXIES_GET_REQ_SCOPED_DELEGATE,
creator.readInstanceField(
FieldDescriptor.of(clientProxy.getClassName(), CONTEXT_FIELD, InjectableContext.class),
creator.getThis()),
beanHandle));
} else {
creator.returnValue(creator.invokeStaticMethod(MethodDescriptors.CLIENT_PROXIES_GET_DELEGATE,
beanHandle));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.arc.processor;

import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -19,11 +20,12 @@ public class CustomAlterableContexts {
this.applicationClassPredicate = applicationClassPredicate;
}

public CustomAlterableContextInfo add(Class<? extends AlterableContext> contextClass, Boolean isNormal) {
public CustomAlterableContextInfo add(Class<? extends AlterableContext> contextClass, Boolean isNormal,
Class<? extends Annotation> scopeAnnotation) {
String generatedName = contextClass.getName() + "_InjectableContext";
boolean isApplicationClass = applicationClassPredicate.test(DotName.createSimple(contextClass));
CustomAlterableContextInfo result = new CustomAlterableContextInfo(contextClass, isNormal, generatedName,
isApplicationClass);
isApplicationClass, scopeAnnotation);
registered.add(result);
return result;
}
Expand Down Expand Up @@ -52,13 +54,15 @@ public static class CustomAlterableContextInfo {
public final Boolean isNormal;
public final String generatedName;
public final boolean isApplicationClass;
public final Class<? extends Annotation> scopeAnnotation;

CustomAlterableContextInfo(Class<? extends AlterableContext> contextClass, Boolean isNormal,
String generatedName, boolean isApplicationClass) {
String generatedName, boolean isApplicationClass, Class<? extends Annotation> scopeAnnotation) {
this.contextClass = contextClass;
this.isNormal = isNormal;
this.generatedName = generatedName;
this.isApplicationClass = isApplicationClass;
this.scopeAnnotation = scopeAnnotation;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,9 @@ public final class MethodDescriptors {
public static final MethodDescriptor ARC_CONTAINER_GET_ACTIVE_CONTEXT = MethodDescriptor.ofMethod(ArcContainer.class,
"getActiveContext", InjectableContext.class, Class.class);

public static final MethodDescriptor ARC_CONTAINER_GET_CONTEXTS = MethodDescriptor.ofMethod(ArcContainer.class,
"getContexts", List.class, Class.class);

public static final MethodDescriptor CONTEXT_GET = MethodDescriptor.ofMethod(Context.class, "get", Object.class,
Contextual.class,
CreationalContext.class);
Expand Down Expand Up @@ -273,6 +276,9 @@ public final class MethodDescriptors {
public static final MethodDescriptor CLIENT_PROXIES_GET_APP_SCOPED_DELEGATE = MethodDescriptor.ofMethod(ClientProxies.class,
"getApplicationScopedDelegate", Object.class, InjectableContext.class, InjectableBean.class);

public static final MethodDescriptor CLIENT_PROXIES_GET_REQ_SCOPED_DELEGATE = MethodDescriptor.ofMethod(ClientProxies.class,
"getRequestScopedDelegate", Object.class, InjectableContext.class, InjectableBean.class);

public static final MethodDescriptor CLIENT_PROXIES_GET_DELEGATE = MethodDescriptor.ofMethod(ClientProxies.class,
"getDelegate", Object.class, InjectableBean.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ public void register(RegistrationContext registrationContext) {
if (InjectableContext.class.isAssignableFrom(contextClass)) {
config.contextClass((Class<? extends InjectableContext>) contextClass);
} else {
CustomAlterableContextInfo info = customAlterableContexts.add(contextClass, context.isNormal);
CustomAlterableContextInfo info = customAlterableContexts.add(contextClass, context.isNormal,
scopeAnnotation);
config.creator(bytecode -> {
return bytecode.newInstance(MethodDescriptor.ofConstructor(info.generatedName));
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ public static <T> T getApplicationScopedDelegate(InjectableContext applicationCo
return result;
}

// This method is only used if a single request context is present
public static <T> T getRequestScopedDelegate(InjectableContext requestContext, InjectableBean<T> bean) {
T result = requestContext.getIfActive(bean, ClientProxies::newCreationalContext);
if (result == null) {
throw notActive(bean);
}
return result;
}

public static <T> T getDelegate(InjectableBean<T> bean) {
List<InjectableContext> contexts = Arc.container().getContexts(bean.getScope());
T result = null;
Expand All @@ -46,17 +55,21 @@ public static <T> T getDelegate(InjectableBean<T> bean) {
}
}
if (result == null) {
String msg = String.format(
"%s context was not active when trying to obtain a bean instance for a client proxy of %s",
bean.getScope().getSimpleName(), bean);
if (bean.getScope().equals(RequestScoped.class)) {
msg += "\n\t- you can activate the request context for a specific method using the @ActivateRequestContext interceptor binding";
}
throw new ContextNotActiveException(msg);
throw notActive(bean);
}
return result;
}

private static ContextNotActiveException notActive(InjectableBean<?> bean) {
String msg = String.format(
"%s context was not active when trying to obtain a bean instance for a client proxy of %s",
bean.getScope().getSimpleName(), bean);
if (bean.getScope().equals(RequestScoped.class)) {
msg += "\n\t- you can activate the request context for a specific method using the @ActivateRequestContext interceptor binding";
}
return new ContextNotActiveException(msg);
}

private static <T> CreationalContextImpl<T> newCreationalContext(Contextual<T> contextual) {
return new CreationalContextImpl<>(contextual);
}
Expand Down

0 comments on commit 4ef44f9

Please sign in to comment.