Skip to content

Commit

Permalink
Merge pull request #28330 from mkouba/arc-req-context-get-rid-of-lazy…
Browse files Browse the repository at this point in the history
…values

ArC RequestContext - get rid of LazyValue for container lifecycle events
  • Loading branch information
mkouba authored Oct 4, 2022
2 parents 4d5e42c + 88e9c41 commit 0bcebb3
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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> 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) {
Expand All @@ -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);

Expand Down Expand Up @@ -180,14 +167,31 @@ public List<RemovedBean> get() {
});
this.transitiveInterceptorBindings = Map.copyOf(transitiveInterceptorBindings);
this.registeredQualifiers = new Qualifiers(qualifiers, qualifierNonbindingMembers);
}

private static void addBuiltInBeans(List<InjectableBean<?>> 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() {
Expand Down Expand Up @@ -423,6 +427,20 @@ InstanceHandle<Object> getResource(Type type, Set<Annotation> annotations) {
return null;
}

private Notifier<Object> notifierOrNull(Set<Annotation> qualifiers) {
Notifier<Object> notifier = EventImpl.createNotifier(Object.class, Object.class,
qualifiers, this, false);
return notifier.isEmpty() ? null : notifier;
}

private static void addBuiltInBeans(List<InjectableBean<?>> beans) {
// BeanManager, Event<?>, Instance<?>, InjectionPoint
beans.add(new BeanManagerBean());
beans.add(new EventBean());
beans.add(InstanceBean.INSTANCE);
beans.add(new InjectionPointBean());
}

private <T> InstanceHandle<T> instanceHandle(Type type, Annotation... qualifiers) {
return beanInstanceHandle(getBean(type, qualifiers), null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ static class Notifier<T> {

private final Class<?> runtimeType;
private final List<ObserverMethod<? super T>> observerMethods;
private final EventMetadata eventMetadata;
final EventMetadata eventMetadata;
private final boolean hasTxObservers;
private final boolean activateRequestContext;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
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;
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;
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;

Expand All @@ -38,15 +35,16 @@ class RequestContext implements ManagedContext {

private final CurrentContext<RequestContextState> currentContext;

private final LazyValue<Notifier<Object>> initializedNotifier;
private final LazyValue<Notifier<Object>> beforeDestroyedNotifier;
private final LazyValue<Notifier<Object>> destroyedNotifier;
private final Notifier<Object> initializedNotifier;
private final Notifier<Object> beforeDestroyedNotifier;
private final Notifier<Object> destroyedNotifier;

public RequestContext(CurrentContext<RequestContextState> currentContext) {
public RequestContext(CurrentContext<RequestContextState> currentContext, Notifier<Object> initializedNotifier,
Notifier<Object> beforeDestroyedNotifier, Notifier<Object> 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
Expand Down Expand Up @@ -195,24 +193,17 @@ public void destroy(ContextState state) {
}
if (state instanceof RequestContextState) {
RequestContextState reqState = ((RequestContextState) state);
reqState.isValid = false;
synchronized (state) {
Map<Contextual<?>, 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<Contextual<?>, 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());
Expand All @@ -227,10 +218,14 @@ private void destroyContextElement(Contextual<?> contextual, ContextInstanceHand
}
}

private void fireIfNotEmpty(LazyValue<Notifier<Object>> value) {
Notifier<Object> notifier = value.get();
if (!notifier.isEmpty()) {
notifier.notify(toString());
private void fireIfNotEmpty(Notifier<Object> 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);
}
}
}

Expand All @@ -239,33 +234,24 @@ private ContextNotActiveException notActive() {
return new ContextNotActiveException(msg);
}

private static Notifier<Object> 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<Object> 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<Object> 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<Contextual<?>, ContextInstanceHandle<?>> map;

private volatile boolean isValid;
private volatile int isValid;

RequestContextState(ConcurrentMap<Contextual<?>, ContextInstanceHandle<?>> value) {
this.map = Objects.requireNonNull(value);
this.isValid = true;
this.isValid = 1;
}

@Override
Expand All @@ -274,9 +260,17 @@ public Map<InjectableBean<?>, 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;
}

}
Expand Down

0 comments on commit 0bcebb3

Please sign in to comment.