0

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?

Herr Derb
  • 4,977
  • 5
  • 34
  • 62
  • 2
    I have the impression that this is expected. What differentiates the 2 injection points? It seems they differ only in the name of the variable; if so, CDI (and probably ArC) indeed does NOT differentiate between them. I believe this is the text book case for using qualifiers. – Nikos Paraskevopoulos May 11 '23 at 04:51
  • I was already there but then I was bothered by the overhead to define producers for each qualifier (`@Named`). But I guess thats the way to go. – Herr Derb May 11 '23 at 05:51
  • If you want a new instance for each injection point, you don't need a custom scope and context -- you can just make the bean `@Dependent`. What your context implementation seems to do is a "per-tenant singleton" (which in my opinion is a perfectly reasonable thing to have, just not something that will produce a different instance for each injection point). – Ladicek May 11 '23 at 06:45
  • The missing proxy on a dependent bean is an issue fpr my use case. I would need the best from both of the worlds :) I think I just need to befriend, that I need to use qualifiers. @NikosParaskevopoulos If you want, you can c&p you comment as answer – Herr Derb May 11 '23 at 06:55
  • In the end, the problem was me, I don't even need to use qualifiers. I went a level to deep. It works as expected with the implementation of the context. – Herr Derb May 11 '23 at 07:30

1 Answers1

0

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 ;-).

Martin Kouba
  • 1,121
  • 5
  • 8