cdi icon indicating copy to clipboard operation
cdi copied to clipboard

Define how `InjectionPoint` works when a bean is obtained via CDI.current()

Open Azquelt opened this issue 11 months ago • 4 comments

The InjectionPoint Javadoc says this:

Provides access to metadata about an injection point. May represent an injected field or a parameter of a bean constructor, initializer method, producer method, disposer method or observer method.

If the injection point is a dynamically selected reference obtained then the metadata obtain reflects the injection point of the Instance, with the required type and any additional required qualifiers defined by Instance.select().

This doesn't cover the case where a bean is looked up using CDI.current().select(...).get() because there is no injection point for the Instance.

For example if I have a qualifier:

@Qualifier
@Retention(RUNTIME)
@Target({ TYPE, FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
public interface @MyQualifier {
    @Nonbinding String value()
    public class MyQualifierLiteral ...
}

and a managed bean class:

@Dependent
@MyQualifier("")
public class MyBean {

    @Inject
    InjectionPoint ip;

    public String getQualifierValue() {
        return ip.getQualifiers().stream()
                        .filter(a -> a.annotationType() == MyQualifier.class)
                        .map(MyQualifier.class::cast)
                        .map(MyQualifier::value)
                        .findFirst()
                        .orElseThrow(() -> new IllegalStateException("No qualifier found"));
    }
}

I can inject this bean like this and get its qualifiers:

@Inject @MyQualifier("foo") private MyBean fooBean;
@Inject @MyQualifier("bar") private Instance<MyBean> barBeanInstance;
@Inject @Any private Instance<Object> objectInstance;

void test() {
    assert fooBean.getQualifierValue().equals("foo");
    assert barBeanInstance.get().getQualifierValue().equals("bar");
    assert objectInstance.select(MyBean.class, new MyQualifier.MyQualifierLiteral("baz")).get().getQualifierValue().equals("baz");
}

However, I don't think it's defined what happens when I do this:

void test() {
    MyBean bean = CDI.current().select(MyBean.class, new MyQualifier.MyQualifierLiteral("qux")).get();
    assert bean.getQualifierValue().equals("qux");
}

Would it make sense to define how the InjectionPoint should work here? I don't think there's any sensible value for getAnnotated(), getBean() or getMember(), but getQualifiers() and getType() would still be helpful in this scenario.

This came to my attention because the Jakarta Batch spec gave an example of looking up a bean like this but getting the injection point isn't supported on open web beans for a bean obtained via CDI.select(): eclipse-ee4j/batch-tck#69

Azquelt avatar Feb 28 '24 16:02 Azquelt

Correct observation. CDI.current(), or BeanContainer.createInstance() for what it's worth, basically gives you an Instance<Object> which was not obtained by injection. Looking up an InjectionPoint from that, or a @Dependent bean that injects InjectionPoint, ends up in an unspecified territory.

I agree that getType() and getQualifiers() can easily be supported, the remaining methods can just return null (or false). That's what we do in ArC (Weld probably does the same, though I didn't look).

Ladicek avatar Feb 28 '24 17:02 Ladicek

I took the liberty of creating a CDI 5.0 milestone and assigning this issue to it. This is something that should be specified IMHO. Of course, my preference would be to specify it the way we do it :-), but that's TBD.

Ladicek avatar Feb 28 '24 17:02 Ladicek

Yea, this is sensible, Weld does this too. I did a quick scan and found this test - https://github.com/weld/core/blob/master/tests-arquillian/src/test/java/org/jboss/weld/tests/injectionPoint/InjectionPointTest.java#L111-L127

manovotn avatar Feb 28 '24 17:02 manovotn

My 2cts would be that it can be to add a BaseInjectionPoint class - please find a better name ;), maybe QualifiedType or just make it extends Annotated and getAnnotated defaulting to this. This would avoid all the broken cases where a not null value would not be allowed to get a null value which can break programmatic cases for no real gain IMHO so letting Annotated be injected in a @Dependent producer method parameter looks saner to me.

rmannibucau avatar Feb 28 '24 19:02 rmannibucau