diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/CurrentVertxRequest.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/CurrentVertxRequest.java index 2588f1a13485ad..b31b9b6fe47f39 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/CurrentVertxRequest.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/CurrentVertxRequest.java @@ -8,7 +8,7 @@ @RequestScoped public class CurrentVertxRequest { - public RoutingContext current; + private RoutingContext current; @Produces @RequestScoped diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java index 8f78d6d4492e64..7e8718e6295b48 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java @@ -5,7 +5,6 @@ import static org.objectweb.asm.Opcodes.ACC_PRIVATE; import static org.objectweb.asm.Opcodes.ACC_VOLATILE; -import io.quarkus.arc.ArcContainer; import io.quarkus.arc.ClientProxy; import io.quarkus.arc.InjectableBean; import io.quarkus.arc.InjectableContext; @@ -31,7 +30,6 @@ import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; -import javax.enterprise.context.spi.Contextual; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; import org.jboss.jandex.FieldInfo; @@ -276,22 +274,17 @@ void implementDelegate(ClassCreator clientProxy, ProviderType providerType, Fiel } ResultHandle beanHandle = creator.readInstanceField(beanField, creator.getThis()); - ResultHandle contextHandle; if (BuiltinScope.APPLICATION.is(bean.getScope())) { - // Application context stored in a field and is always active - contextHandle = creator.readInstanceField( - FieldDescriptor.of(clientProxy.getClassName(), CONTEXT_FIELD, InjectableContext.class), creator.getThis()); - - creator.returnValue(creator.invokeInterfaceMethod( - MethodDescriptor.ofMethod(InjectableContext.class, "getOrCreate", Object.class, Contextual.class), - contextHandle, beanHandle)); + // Application context is stored in a field and is always active + creator.returnValue(creator.invokeStaticMethod(MethodDescriptors.CLIENT_PROXIES_GET_APP_SCOPED_DELEGATE, + creator.readInstanceField( + FieldDescriptor.of(clientProxy.getClassName(), CONTEXT_FIELD, InjectableContext.class), + creator.getThis()), + beanHandle)); } else { - // Arc.container() - ResultHandle container = creator.invokeStaticMethod(MethodDescriptors.ARC_CONTAINER); - creator.returnValue(creator.invokeInterfaceMethod( - MethodDescriptor.ofMethod(ArcContainer.class, "normalScopedInstance", Object.class, InjectableBean.class), - container, beanHandle)); + creator.returnValue(creator.invokeStaticMethod(MethodDescriptors.CLIENT_PROXIES_GET_DELEGATE, + beanHandle)); } } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/MethodDescriptors.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/MethodDescriptors.java index ad2e4f708a8bcb..6e2e68273bfb5b 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/MethodDescriptors.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/MethodDescriptors.java @@ -8,6 +8,7 @@ import io.quarkus.arc.InjectableContext; import io.quarkus.arc.InjectableInterceptor; import io.quarkus.arc.InjectableReferenceProvider; +import io.quarkus.arc.impl.ClientProxies; import io.quarkus.arc.impl.CreationalContextImpl; import io.quarkus.arc.impl.FixedValueSupplier; import io.quarkus.arc.impl.InjectableReferenceProviders; @@ -242,6 +243,12 @@ public final class MethodDescriptors { public static final MethodDescriptor REMOVED_BEAN_IMPL = MethodDescriptor.ofConstructor(RemovedBeanImpl.class, Kind.class, String.class, Set.class, Set.class); + 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_DELEGATE = MethodDescriptor.ofMethod(ClientProxies.class, + "getDelegate", Object.class, InjectableBean.class); + private MethodDescriptors() { } diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/ArcContainer.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/ArcContainer.java index e01373784caae8..9bd2d0733ae5dd 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/ArcContainer.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/ArcContainer.java @@ -2,6 +2,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.util.Collection; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.function.Supplier; @@ -28,6 +29,13 @@ public interface ArcContainer { */ InjectableContext getActiveContext(Class scopeType); + /** + * + * @param scopeType + * @return the matching context objects, never null + */ + Collection getContexts(Class scopeType); + /** * * @return the set of all supported scopes @@ -98,14 +106,6 @@ public interface ArcContainer { */ InstanceHandle instance(InjectableBean bean); - /** - * Returns an instance of a normal scoped bean - * - * @param bean - * @return a new bean instance - */ - T normalScopedInstance(InjectableBean bean); - /** * Instances of dependent scoped beans obtained with the returned injectable instance must be explicitly destroyed, either * via the {@link Instance#destroy(Object)} method invoked upon the same injectable instance or with diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/InjectableContext.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/InjectableContext.java index e675156d734950..24cfee0323e93f 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/InjectableContext.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/InjectableContext.java @@ -1,16 +1,16 @@ package io.quarkus.arc; -import io.quarkus.arc.impl.CreationalContextImpl; import java.util.Map; +import java.util.function.Function; import javax.enterprise.context.NormalScope; import javax.enterprise.context.spi.AlterableContext; import javax.enterprise.context.spi.Contextual; +import javax.enterprise.context.spi.CreationalContext; /** - * A context implementing this interface allows to capture and view its state via {@link ContextState}. - * It also allows user to destroy all contextual instances within this context. - * - * @author Martin Kouba + * A context implementing this interface makes it possible to capture and view its state via the {@link ContextState}. + * + * It also allows users to destroy all contextual instances within this context. */ public interface InjectableContext extends AlterableContext { @@ -25,16 +25,19 @@ public interface InjectableContext extends AlterableContext { ContextState getState(); /** - * Attempts to get or create a new isntance of the given contextual. If the scope is not active this returns null. - * - * This allows for the isActive check and the actual creation to happen in a single method, which gives a performance - * benefit by performing fewer thread local operations. + * If the context is active then return an existing instance of certain contextual type or create a new instance, otherwise + * return a null value. + * + * This allows for the {@link #isActive()} check and the actual creation to happen in a single method, which gives a + * performance benefit by performing fewer thread local operations. * - * @param contextual The bean - * @param The type of bean - * @return + * @param the type of contextual type + * @param contextual the contextual type + * @param creationalContextFunction the creational context function + * @return the contextual instance, or a null value */ - default T getOrCreate(Contextual contextual) { + default T getIfActive(Contextual contextual, + Function, CreationalContext> creationalContextFunction) { if (!isActive()) { return null; } @@ -42,7 +45,7 @@ default T getOrCreate(Contextual contextual) { if (result != null) { return result; } - return get(contextual, new CreationalContextImpl<>(contextual)); + return get(contextual, creationalContextFunction.apply(contextual)); } /** diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java index d4002d557023a2..9f573e77326b13 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java @@ -19,6 +19,7 @@ import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -39,7 +40,6 @@ import java.util.stream.Collectors; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.BeforeDestroyed; -import javax.enterprise.context.ContextNotActiveException; import javax.enterprise.context.Dependent; import javax.enterprise.context.Destroyed; import javax.enterprise.context.Initialized; @@ -79,8 +79,7 @@ public class ArcContainerImpl implements ArcContainer { private final List> observers; private final Map, Set> transitiveInterceptorBindings; - // List of "ambiguous" contexts that could share a scope - private final List contexts; + private final Map, Collection> contexts; private final ManagedContext requestContext; private final InjectableContext applicationContext; private final InjectableContext singletonContext; @@ -107,8 +106,10 @@ public ArcContainerImpl() { applicationContext = new ApplicationContext(); singletonContext = new SingletonContext(); requestContext = new RequestContext(); - contexts = new ArrayList<>(); - contexts.add(requestContext); + contexts = new HashMap<>(); + putContext(requestContext); + putContext(applicationContext); + putContext(singletonContext); for (ComponentsProvider componentsProvider : ServiceLoader.load(ComponentsProvider.class)) { Components components = componentsProvider.getComponents(); @@ -131,7 +132,7 @@ public ArcContainerImpl() { throw new IllegalStateException( "Failed to register a context - built-in singleton context is always active: " + context); } - contexts.add(context); + putContext(context); } for (Entry, Set> entry : components.getTransitiveInterceptorBindings() .entrySet()) { @@ -154,6 +155,17 @@ public ArcContainerImpl() { instance = InstanceImpl.of(Object.class, Collections.emptySet()); } + private void putContext(InjectableContext context) { + Collection values = contexts.get(context.getScope()); + if (values == null) { + contexts.put(context.getScope(), Collections.singleton(context)); + } else { + List multi = new LinkedList<>(values); + multi.add(context); + contexts.put(context.getScope(), Collections.unmodifiableList(multi)); + } + } + private void addBuiltInBeans() { // BeanManager, Event, Instance beans.add(new BeanManagerBean()); @@ -183,8 +195,8 @@ public InjectableContext getActiveContext(Class scopeType) return singletonContext; } InjectableContext selected = null; - for (InjectableContext context : contexts) { - if (scopeType.equals(context.getScope()) && context.isActive()) { + for (InjectableContext context : contexts.get(scopeType)) { + if (context.isActive()) { if (selected != null) { throw new IllegalArgumentException( "More than one context object for the given scope: " + selected + " " + context); @@ -195,13 +207,15 @@ public InjectableContext getActiveContext(Class scopeType) return selected; } + @Override + public Collection getContexts(Class scopeType) { + requireRunning(); + return contexts.getOrDefault(scopeType, Collections.emptyList()); + } + @Override public Set> getScopes() { - Set> scopes = contexts.stream().map(InjectableContext::getScope) - .collect(Collectors.toSet()); - scopes.add(ApplicationScoped.class); - scopes.add(Singleton.class); - return scopes; + return contexts.keySet().stream().collect(Collectors.toSet()); } @Override @@ -263,39 +277,6 @@ public InstanceHandle instance(InjectableBean bean) { return (InstanceHandle) beanInstanceHandle(bean, null); } - @Override - public T normalScopedInstance(InjectableBean bean) { - requireRunning(); - Class scopeType = bean.getScope(); - // Application/Singleton context is always active - if (ApplicationScoped.class.equals(scopeType)) { - return applicationContext.getOrCreate(bean); - } else if (Singleton.class.equals(scopeType)) { - return applicationContext.getOrCreate(bean); - } - T result = null; - InjectableContext selectedContext = null; - for (InjectableContext context : contexts) { - if (scopeType.equals(context.getScope())) { - if (result != null) { - if (context.isActive()) { - throw new IllegalArgumentException( - "More than one context object for the given scope: " + selectedContext + " " + context); - } - } else { - result = context.getOrCreate(bean); - if (result != null) { - selectedContext = context; - } - } - } - } - if (result == null) { - throw new ContextNotActiveException(); - } - return result; - } - @Override public InjectableInstance select(Class type, Annotation... qualifiers) { return instance.select(type, qualifiers); @@ -497,16 +478,24 @@ boolean isScope(Class annotationType) { if (annotationType.isAnnotationPresent(Scope.class) || annotationType.isAnnotationPresent(NormalScope.class)) { return true; } - return contexts.stream().map(InjectableContext::getScope).filter(annotationType::equals).findAny().isPresent(); + for (Class scopeType : contexts.keySet()) { + if (scopeType.equals(annotationType)) { + return true; + } + } + return false; } boolean isNormalScope(Class annotationType) { if (annotationType.isAnnotationPresent(NormalScope.class)) { return true; } - for (InjectableContext context : contexts) { - if (context.getScope().equals(annotationType) && context.isNormal()) { - return true; + Collection injectableContexts = contexts.get(annotationType); + if (injectableContexts != null) { + for (InjectableContext context : injectableContexts) { + if (context.isNormal()) { + return true; + } } } return false; diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ClientProxies.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ClientProxies.java new file mode 100644 index 00000000000000..da0b25fe09c499 --- /dev/null +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ClientProxies.java @@ -0,0 +1,49 @@ +package io.quarkus.arc.impl; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InjectableBean; +import io.quarkus.arc.InjectableContext; +import javax.enterprise.context.ContextNotActiveException; +import javax.enterprise.context.spi.Contextual; + +public final class ClientProxies { + + private ClientProxies() { + } + + public static T getApplicationScopedDelegate(InjectableContext applicationContext, InjectableBean bean) { + T result = applicationContext.get(bean); + if (result == null) { + result = applicationContext.get(bean, newCreationalContext(bean)); + } + return result; + } + + public static T getDelegate(InjectableBean bean) { + Iterable contexts = Arc.container().getContexts(bean.getScope()); + T result = null; + InjectableContext selectedContext = null; + for (InjectableContext context : contexts) { + if (result != null) { + if (context.isActive()) { + throw new IllegalArgumentException( + "More than one context object for the given scope: " + selectedContext + " " + context); + } + } else { + result = context.getIfActive(bean, ClientProxies::newCreationalContext); + if (result != null) { + selectedContext = context; + } + } + } + if (result == null) { + throw new ContextNotActiveException(); + } + return result; + } + + private static CreationalContextImpl newCreationalContext(Contextual contextual) { + return new CreationalContextImpl<>(contextual); + } + +} diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/CreationalContextImpl.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/CreationalContextImpl.java index dbab8d8c4ab8f5..8dc42286fbd5be 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/CreationalContextImpl.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/CreationalContextImpl.java @@ -1,22 +1,25 @@ package io.quarkus.arc.impl; -import io.quarkus.arc.InjectableBean; -import io.quarkus.arc.InjectableReferenceProvider; -import io.quarkus.arc.InstanceHandle; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.function.Function; + import javax.enterprise.context.spi.Contextual; import javax.enterprise.context.spi.CreationalContext; +import io.quarkus.arc.InjectableBean; +import io.quarkus.arc.InjectableReferenceProvider; +import io.quarkus.arc.InstanceHandle; + /** * * @author Martin Kouba * * @param */ -public class CreationalContextImpl implements CreationalContext { +public class CreationalContextImpl implements CreationalContext, Function, CreationalContext> { private final Contextual contextual; private final CreationalContextImpl parent; @@ -86,6 +89,11 @@ public CreationalContextImpl child(Contextual contextual) { return new CreationalContextImpl<>(contextual, this); } + @Override + public CreationalContext apply(Contextual contextual) { + return this; + } + public static CreationalContextImpl unwrap(CreationalContext ctx) { if (ctx instanceof CreationalContextImpl) { return (CreationalContextImpl) ctx; diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/RequestContext.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/RequestContext.java index 9985c37e945e63..1f01281e799a82 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/RequestContext.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/RequestContext.java @@ -9,8 +9,10 @@ import java.util.Arrays; import java.util.HashSet; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.function.Function; import java.util.stream.Collectors; import javax.enterprise.context.BeforeDestroyed; import javax.enterprise.context.ContextNotActiveException; @@ -49,11 +51,11 @@ public Class getScope() { return RequestScoped.class; } + @SuppressWarnings("unchecked") @Override - public T getOrCreate(Contextual contextual) { - if (contextual == null) { - throw new IllegalArgumentException("Contextual parameter must not be null"); - } + public T getIfActive(Contextual contextual, Function, CreationalContext> creationalContextFun) { + Objects.requireNonNull(contextual, "Contextual must not be null"); + Objects.requireNonNull(creationalContextFun, "CreationalContext supplier must not be null"); Map, ContextInstanceHandle> ctx = currentContext.get(); if (ctx == null) { // Thread local not set - context is not active! @@ -61,7 +63,7 @@ public T getOrCreate(Contextual contextual) { } ContextInstanceHandle instance = (ContextInstanceHandle) ctx.get(contextual); if (instance == null) { - CreationalContext creationalContext = new CreationalContextImpl<>(contextual); + CreationalContext creationalContext = creationalContextFun.apply(contextual); // Bean instance does not exist - create one if we have CreationalContext instance = new ContextInstanceHandleImpl((InjectableBean) contextual, contextual.create(creationalContext), creationalContext); @@ -70,30 +72,28 @@ public T getOrCreate(Contextual contextual) { return instance.get(); } - @SuppressWarnings("unchecked") @Override public T get(Contextual contextual, CreationalContext creationalContext) { - if (contextual == null) { - throw new IllegalArgumentException("Contextual parameter must not be null"); - } - Map, ContextInstanceHandle> ctx = currentContext.get(); - if (ctx == null) { + T result = getIfActive(contextual, + CreationalContextImpl.unwrap(Objects.requireNonNull(creationalContext, "CreationalContext must not be null"))); + if (result == null) { // Thread local not set - context is not active! throw new ContextNotActiveException(); } - ContextInstanceHandle instance = (ContextInstanceHandle) ctx.get(contextual); - if (instance == null && creationalContext != null) { - // Bean instance does not exist - create one if we have CreationalContext - instance = new ContextInstanceHandleImpl((InjectableBean) contextual, - contextual.create(creationalContext), creationalContext); - ctx.put(contextual, instance); - } - return instance != null ? instance.get() : null; + return result; } + @SuppressWarnings("unchecked") @Override public T get(Contextual contextual) { - return get(contextual, null); + Objects.requireNonNull(contextual, "Contextual must not be null"); + Map, ContextInstanceHandle> ctx = currentContext.get(); + if (ctx == null) { + // Thread local not set - context is not active! + throw new ContextNotActiveException(); + } + ContextInstanceHandle instance = (ContextInstanceHandle) ctx.get(contextual); + return instance == null ? null : instance.get(); } @Override