javadependency-injectioncdiquarkuscustom-scope

Custom InjectableContext cannot differ between two injection points within the same bean in Quarkus


I try to implement a @TenantScope which can be used for stateful beans rather than using a nested map. For this I am implementing a io.quarkus.arc.InjectableContext.

I have it working so far, except if I have two injection points of the same type. My implementation for the two get(...) methods looks like this::

@Override
public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext) {
    String tenantId = getTenantIdFromContex();
    Map<String, ContextInstanceHandle<?>> tenantMap = contextualMap.computeIfAbsent(contextual, c -> new HashMap<>());
    @SuppressWarnings("unchecked")
    ContextInstanceHandle<T> contextInstanceHandle =
            (ContextInstanceHandle<T>) tenantMap.computeIfAbsent(tenantId, t -> {
                if (creationalContext == null) {
                    return null;
                }
                T createdInstance = contextual.create(creationalContext);
                return new ContextInstanceHandleImpl<T>(
                        (InjectableBean<T>) contextual, createdInstance, creationalContext);
            });
    return contextInstanceHandle.get();
}

@SuppressWarnings("unchecked")
@Override
public <T> T get(Contextual<T> contextual) {
    String tenantId = getTenantIdFromContex();
    if(!contextualMap.containsKey(contextual) || !contextualMap.get(contextual).containsKey(tenantId)){
        return null;
    }
    return ((ContextInstanceHandle<T>) contextualMap.get(contextual).get(tenantId)).get();
}

Assume this bean

@TenantScoped
class MyTenantScopedQueue(){...}

My custom context behaves as expected when I have a single injection point of a @TenantScoped type in a bean. Depending on the tenantId of my context, I get the specific instance. As soon as I have two injection points I run into a problem:

class MyBean() {

@Inject
MyTenantScopedQueue queue;
@Inject
MyTenantScopedQueue queueForOtherStuff;

...
}

It seems that Contextual<T> does not hold enough information to differ between this two injection points resulting in having same instance injected twice. How can I implement my InjectableContext so I do get different instances for each injection point?


Solution

  • Is the @TenantScoped a normal scope? If so then the injected MyTenantScopedQueue (which is a client proxy) must obtain the active context object first, then call Context.get() and finally delegate to the underlying bean instance. And your context implementation must ensure that there is exactly one instance for a given bean associated with the current thread.

    Now both injection points are satisfied by the same Bean. Which means that if you invoke a method upon queue and queueForOtherStuff from the same thread then both invocations will delegate to the same bean instance.

    If the scope is not normal, i.e. not annotated with @jakarta.enterprise.context.NormalScope but with @jakarta.inject.Scope, then the behavior is undefined (up to you ;-).