Skip to content

Commit

Permalink
Polish the API changes
Browse files Browse the repository at this point in the history
- add ArcContainer.getContexts()
- remove ArcContainer.normalScopedInstance()
- rename InjectableContext.getOrCreate() to getIfActive()
- add ClientProxies util class
  • Loading branch information
mkouba committed Nov 3, 2020
1 parent b972eb4 commit 15eed2d
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 112 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
@RequestScoped
public class CurrentVertxRequest {

public RoutingContext current;
private RoutingContext current;

@Produces
@RequestScoped
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -28,6 +29,13 @@ public interface ArcContainer {
*/
InjectableContext getActiveContext(Class<? extends Annotation> scopeType);

/**
*
* @param scopeType
* @return the matching context objects, never null
*/
Collection<InjectableContext> getContexts(Class<? extends Annotation> scopeType);

/**
*
* @return the set of all supported scopes
Expand Down Expand Up @@ -98,14 +106,6 @@ public interface ArcContainer {
*/
<T> InstanceHandle<T> instance(InjectableBean<T> bean);

/**
* Returns an instance of a normal scoped bean
*
* @param bean
* @return a new bean instance
*/
<T> T normalScopedInstance(InjectableBean<T> 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
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {

Expand All @@ -25,24 +25,27 @@ 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 <T> The type of bean
* @return
* @param <T> 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> T getOrCreate(Contextual<T> contextual) {
default <T> T getIfActive(Contextual<T> contextual,
Function<Contextual<T>, CreationalContext<T>> creationalContextFunction) {
if (!isActive()) {
return null;
}
T result = get(contextual);
if (result != null) {
return result;
}
return get(contextual, new CreationalContextImpl<>(contextual));
return get(contextual, creationalContextFunction.apply(contextual));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -79,8 +79,7 @@ public class ArcContainerImpl implements ArcContainer {
private final List<InjectableObserverMethod<?>> observers;
private final Map<Class<? extends Annotation>, Set<Annotation>> transitiveInterceptorBindings;

// List of "ambiguous" contexts that could share a scope
private final List<InjectableContext> contexts;
private final Map<Class<? extends Annotation>, Collection<InjectableContext>> contexts;
private final ManagedContext requestContext;
private final InjectableContext applicationContext;
private final InjectableContext singletonContext;
Expand All @@ -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();
Expand All @@ -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<Class<? extends Annotation>, Set<Annotation>> entry : components.getTransitiveInterceptorBindings()
.entrySet()) {
Expand All @@ -154,6 +155,17 @@ public ArcContainerImpl() {
instance = InstanceImpl.of(Object.class, Collections.emptySet());
}

private void putContext(InjectableContext context) {
Collection<InjectableContext> values = contexts.get(context.getScope());
if (values == null) {
contexts.put(context.getScope(), Collections.singleton(context));
} else {
List<InjectableContext> multi = new LinkedList<>(values);
multi.add(context);
contexts.put(context.getScope(), Collections.unmodifiableList(multi));
}
}

private void addBuiltInBeans() {
// BeanManager, Event<?>, Instance<?>
beans.add(new BeanManagerBean());
Expand Down Expand Up @@ -183,8 +195,8 @@ public InjectableContext getActiveContext(Class<? extends Annotation> 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);
Expand All @@ -195,13 +207,15 @@ public InjectableContext getActiveContext(Class<? extends Annotation> scopeType)
return selected;
}

@Override
public Collection<InjectableContext> getContexts(Class<? extends Annotation> scopeType) {
requireRunning();
return contexts.getOrDefault(scopeType, Collections.emptyList());
}

@Override
public Set<Class<? extends Annotation>> getScopes() {
Set<Class<? extends Annotation>> 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
Expand Down Expand Up @@ -263,39 +277,6 @@ public <T> InstanceHandle<T> instance(InjectableBean<T> bean) {
return (InstanceHandle<T>) beanInstanceHandle(bean, null);
}

@Override
public <T> T normalScopedInstance(InjectableBean<T> bean) {
requireRunning();
Class<? extends Annotation> 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 <T> InjectableInstance<T> select(Class<T> type, Annotation... qualifiers) {
return instance.select(type, qualifiers);
Expand Down Expand Up @@ -497,16 +478,24 @@ boolean isScope(Class<? extends Annotation> 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<? extends Annotation> scopeType : contexts.keySet()) {
if (scopeType.equals(annotationType)) {
return true;
}
}
return false;
}

boolean isNormalScope(Class<? extends Annotation> annotationType) {
if (annotationType.isAnnotationPresent(NormalScope.class)) {
return true;
}
for (InjectableContext context : contexts) {
if (context.getScope().equals(annotationType) && context.isNormal()) {
return true;
Collection<InjectableContext> injectableContexts = contexts.get(annotationType);
if (injectableContexts != null) {
for (InjectableContext context : injectableContexts) {
if (context.isNormal()) {
return true;
}
}
}
return false;
Expand Down
Original file line number Diff line number Diff line change
@@ -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> T getApplicationScopedDelegate(InjectableContext applicationContext, InjectableBean<T> bean) {
T result = applicationContext.get(bean);
if (result == null) {
result = applicationContext.get(bean, newCreationalContext(bean));
}
return result;
}

public static <T> T getDelegate(InjectableBean<T> bean) {
Iterable<InjectableContext> 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 <T> CreationalContextImpl<T> newCreationalContext(Contextual<T> contextual) {
return new CreationalContextImpl<>(contextual);
}

}
Loading

0 comments on commit 15eed2d

Please sign in to comment.