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 669a3659889d8..71cfa5cf8cc6d 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 @@ -67,6 +67,7 @@ import io.quarkus.arc.RemovedBean; import io.quarkus.arc.ResourceReferenceProvider; import io.quarkus.arc.impl.ArcCDIProvider.ArcCDI; +import io.quarkus.arc.impl.EventImpl.Notifier; public class ArcContainerImpl implements ArcContainer { @@ -111,13 +112,13 @@ public ArcContainerImpl(CurrentContextFactory currentContextFactory) { this.currentContextFactory = currentContextFactory == null ? new ThreadLocalCurrentContextFactory() : currentContextFactory; - Contexts.Builder contextsBuilder = new Contexts.Builder( - new RequestContext(this.currentContextFactory.create(RequestScoped.class)), new ApplicationContext(), - new SingletonContext()); - + List components = new ArrayList<>(); for (ComponentsProvider componentsProvider : ServiceLoader.load(ComponentsProvider.class)) { - Components components = componentsProvider.getComponents(); - for (InjectableBean bean : components.getBeans()) { + components.add(componentsProvider.getComponents()); + } + + for (Components c : components) { + for (InjectableBean bean : c.getBeans()) { if (bean instanceof InjectableInterceptor) { interceptors.add((InjectableInterceptor) bean); } else if (bean instanceof InjectableDecorator) { @@ -126,27 +127,13 @@ public ArcContainerImpl(CurrentContextFactory currentContextFactory) { beans.add(bean); } } - removedBeans.add(components.getRemovedBeans()); - observers.addAll(components.getObservers()); - // Add custom contexts - for (InjectableContext context : components.getContexts()) { - if (ApplicationScoped.class.equals(context.getScope())) { - throw new IllegalStateException( - "Failed to register a context - built-in application context is always active: " + context); - } - if (Singleton.class.equals(context.getScope())) { - throw new IllegalStateException( - "Failed to register a context - built-in singleton context is always active: " + context); - } - contextsBuilder.putContext(context); - } - transitiveInterceptorBindings.putAll(components.getTransitiveInterceptorBindings()); - qualifierNonbindingMembers.putAll(components.getQualifierNonbindingMembers()); - qualifiers.addAll(components.getQualifiers()); + removedBeans.add(c.getRemovedBeans()); + observers.addAll(c.getObservers()); + transitiveInterceptorBindings.putAll(c.getTransitiveInterceptorBindings()); + qualifierNonbindingMembers.putAll(c.getQualifierNonbindingMembers()); + qualifiers.addAll(c.getQualifiers()); } - this.contexts = contextsBuilder.build(); - // register built-in beans addBuiltInBeans(beans); @@ -180,14 +167,31 @@ public List get() { }); this.transitiveInterceptorBindings = Map.copyOf(transitiveInterceptorBindings); this.registeredQualifiers = new Qualifiers(qualifiers, qualifierNonbindingMembers); - } - private static void addBuiltInBeans(List> beans) { - // BeanManager, Event, Instance, InjectionPoint - beans.add(new BeanManagerBean()); - beans.add(new EventBean()); - beans.add(InstanceBean.INSTANCE); - beans.add(new InjectionPointBean()); + Contexts.Builder contextsBuilder = new Contexts.Builder( + new RequestContext(this.currentContextFactory.create(RequestScoped.class), + notifierOrNull(Set.of(Initialized.Literal.REQUEST, Any.Literal.INSTANCE)), + notifierOrNull(Set.of(BeforeDestroyed.Literal.REQUEST, Any.Literal.INSTANCE)), + notifierOrNull(Set.of(Destroyed.Literal.REQUEST, Any.Literal.INSTANCE))), + new ApplicationContext(), + new SingletonContext()); + + // Add custom contexts + for (Components c : components) { + for (InjectableContext context : c.getContexts()) { + if (ApplicationScoped.class.equals(context.getScope())) { + throw new IllegalStateException( + "Failed to register a context - built-in application context is always active: " + context); + } + if (Singleton.class.equals(context.getScope())) { + throw new IllegalStateException( + "Failed to register a context - built-in singleton context is always active: " + context); + } + contextsBuilder.putContext(context); + } + } + + this.contexts = contextsBuilder.build(); } public void init() { @@ -423,6 +427,20 @@ InstanceHandle getResource(Type type, Set annotations) { return null; } + private Notifier notifierOrNull(Set qualifiers) { + Notifier notifier = EventImpl.createNotifier(Object.class, Object.class, + qualifiers, this, false); + return notifier.isEmpty() ? null : notifier; + } + + private static void addBuiltInBeans(List> beans) { + // BeanManager, Event, Instance, InjectionPoint + beans.add(new BeanManagerBean()); + beans.add(new EventBean()); + beans.add(InstanceBean.INSTANCE); + beans.add(new InjectionPointBean()); + } + private InstanceHandle instanceHandle(Type type, Annotation... qualifiers) { return beanInstanceHandle(getBean(type, qualifiers), null); } diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/EventImpl.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/EventImpl.java index c8ff31fbff796..204114e169531 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/EventImpl.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/EventImpl.java @@ -223,7 +223,7 @@ static class Notifier { private final Class runtimeType; private final List> observerMethods; - private final EventMetadata eventMetadata; + final EventMetadata eventMetadata; private final boolean hasTxObservers; private final boolean activateRequestContext; 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 4786437600efa..2f9eb74464027 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 @@ -1,8 +1,9 @@ package io.quarkus.arc.impl; import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.Arrays; -import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; @@ -10,14 +11,10 @@ import java.util.function.Function; import java.util.stream.Collectors; -import javax.enterprise.context.BeforeDestroyed; import javax.enterprise.context.ContextNotActiveException; -import javax.enterprise.context.Destroyed; -import javax.enterprise.context.Initialized; import javax.enterprise.context.RequestScoped; import javax.enterprise.context.spi.Contextual; import javax.enterprise.context.spi.CreationalContext; -import javax.enterprise.inject.Any; import org.jboss.logging.Logger; @@ -38,15 +35,16 @@ class RequestContext implements ManagedContext { private final CurrentContext currentContext; - private final LazyValue> initializedNotifier; - private final LazyValue> beforeDestroyedNotifier; - private final LazyValue> destroyedNotifier; + private final Notifier initializedNotifier; + private final Notifier beforeDestroyedNotifier; + private final Notifier destroyedNotifier; - public RequestContext(CurrentContext currentContext) { + public RequestContext(CurrentContext currentContext, Notifier initializedNotifier, + Notifier beforeDestroyedNotifier, Notifier destroyedNotifier) { this.currentContext = currentContext; - this.initializedNotifier = new LazyValue<>(RequestContext::createInitializedNotifier); - this.beforeDestroyedNotifier = new LazyValue<>(RequestContext::createBeforeDestroyedNotifier); - this.destroyedNotifier = new LazyValue<>(RequestContext::createDestroyedNotifier); + this.initializedNotifier = initializedNotifier; + this.beforeDestroyedNotifier = beforeDestroyedNotifier; + this.destroyedNotifier = destroyedNotifier; } @Override @@ -195,24 +193,17 @@ public void destroy(ContextState state) { } if (state instanceof RequestContextState) { RequestContextState reqState = ((RequestContextState) state); - reqState.isValid = false; - synchronized (state) { - Map, ContextInstanceHandle> map = ((RequestContextState) state).map; + if (reqState.invalidate()) { // Fire an event with qualifier @BeforeDestroyed(RequestScoped.class) if there are any observers for it - try { - fireIfNotEmpty(beforeDestroyedNotifier); - } catch (Exception e) { - LOG.warn("An error occurred during delivery of the @BeforeDestroyed(RequestScoped.class) event", e); + fireIfNotEmpty(beforeDestroyedNotifier); + Map, ContextInstanceHandle> map = ((RequestContextState) state).map; + if (!map.isEmpty()) { + //Performance: avoid an iterator on the map elements + map.forEach(this::destroyContextElement); + map.clear(); } - //Performance: avoid an iterator on the map elements - map.forEach(this::destroyContextElement); // Fire an event with qualifier @Destroyed(RequestScoped.class) if there are any observers for it - try { - fireIfNotEmpty(destroyedNotifier); - } catch (Exception e) { - LOG.warn("An error occurred during delivery of the @Destroyed(RequestScoped.class) event", e); - } - map.clear(); + fireIfNotEmpty(destroyedNotifier); } } else { throw new IllegalArgumentException("Invalid state implementation: " + state.getClass().getName()); @@ -227,10 +218,14 @@ private void destroyContextElement(Contextual contextual, ContextInstanceHand } } - private void fireIfNotEmpty(LazyValue> value) { - Notifier notifier = value.get(); - if (!notifier.isEmpty()) { - notifier.notify(toString()); + private void fireIfNotEmpty(Notifier notifier) { + if (notifier != null && !notifier.isEmpty()) { + try { + notifier.notify(toString()); + } catch (Exception e) { + LOG.warn("An error occurred during delivery of the container lifecycle event for qualifiers " + + notifier.eventMetadata.getQualifiers(), e); + } } } @@ -239,33 +234,24 @@ private ContextNotActiveException notActive() { return new ContextNotActiveException(msg); } - private static Notifier createInitializedNotifier() { - return EventImpl.createNotifier(Object.class, Object.class, - new HashSet<>(Arrays.asList(Initialized.Literal.REQUEST, Any.Literal.INSTANCE)), - ArcContainerImpl.instance(), false); - } + static class RequestContextState implements ContextState { - private static Notifier createBeforeDestroyedNotifier() { - return EventImpl.createNotifier(Object.class, Object.class, - new HashSet<>(Arrays.asList(BeforeDestroyed.Literal.REQUEST, Any.Literal.INSTANCE)), - ArcContainerImpl.instance(), false); - } + private static final VarHandle IS_VALID; - private static Notifier createDestroyedNotifier() { - return EventImpl.createNotifier(Object.class, Object.class, - new HashSet<>(Arrays.asList(Destroyed.Literal.REQUEST, Any.Literal.INSTANCE)), - ArcContainerImpl.instance(), false); - } - - static class RequestContextState implements ContextState { + static { + try { + IS_VALID = MethodHandles.lookup().findVarHandle(RequestContextState.class, "isValid", int.class); + } catch (ReflectiveOperationException e) { + throw new Error(e); + } + } private final Map, ContextInstanceHandle> map; - - private volatile boolean isValid; + private volatile int isValid; RequestContextState(ConcurrentMap, ContextInstanceHandle> value) { this.map = Objects.requireNonNull(value); - this.isValid = true; + this.isValid = 1; } @Override @@ -274,9 +260,17 @@ public Map, Object> getContextualInstances() { .collect(Collectors.toUnmodifiableMap(ContextInstanceHandle::getBean, ContextInstanceHandle::get)); } + /** + * @return {@code true} if the state was successfully invalidated, {@code false} otherwise + */ + boolean invalidate() { + // Atomically sets the value just like AtomicBoolean.compareAndSet(boolean, boolean) + return IS_VALID.compareAndSet(this, 1, 0); + } + @Override public boolean isValid() { - return isValid; + return isValid == 1; } }