rest icon indicating copy to clipboard operation
rest copied to clipboard

CDI constructor injection & the use of multi-arg constructors Resource and Provider classes

Open damnhandy opened this issue 6 years ago • 16 comments

One thing that would be very helpful for implementors is providing clarity on how JAX-RS Resource classes and Providers are instantiated when used with CDI. Presently, there's some significant behavioral differences when switching between JAX-RS implementations with CDI. For example, using Jersey 2.27 with JBoss Weld 3.0.4.Final , constructor injection works as you'd expect. With you attempt to do the same thing with JBoss RESTEasy 3.5.1.Final and Weld 3.0.4.Final, you get an error stating that a 0-arg constructor is required. The JBoss team is interpreting this language in section 3.1

By default a new resource class instance is created for each request to that resource

As a zero-arg constructor is required. See this issue here:

https://issues.jboss.org/browse/RESTEASY-1538

That issue is closed and marked as resolve, citing language in the spec.

I think it would be helpful somewhere in section 3 that when using CDI with JAX-RS, constructor injection MUST be supported when a Resource class have a multi-arg constructor annotated with @Inject.

Similarly, it should also be permissible to do something like this:

@Provider
public class JacksonObjectMapperProvider implements ContextResolver<ObjectMapper> {
    
    private ObjectMapper objectMapper;

    @Inject
    public JacksonObjectMapperProvider(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return objectMapper;
    }
}

Yet again, the above code works fine in Jersey + Weld while RESTEasy + Weld complains about not having a zero-arg constructor. As CDI starts to be folded into JAX-RS, some additional verbiage to make this abundantly clear that constructor injection must be supported in these use cases would be wonderful.

damnhandy avatar Jul 01 '18 15:07 damnhandy

Hi,

The requirement to support the multi-arg constructor for JAX-RS sounds good indeec.

On Sunday, July 1, 2018, Ryan J. McDonough [email protected] wrote:

One thing that would be very helpful for implementors is providing clarity on how JAX-RS Resource classes and Providers are instantiated when used with CDI. Presently, there's some significant behavioral differences when switching between JAX-RS implementations with CDI. For example, using Jersey 2.27 with JBoss Weld 3.0.4.Final , constructor injection works as you'd expect. With you attempt to do the same thing with JBoss RESTEasy 3.5.1.Final and Weld 3.0.4.Final, you get an error stating that a 0-arg constructor is required. The JBoss team is interpreting this language in section 3.1

By default a new resource class instance is created for each request to that resource

As a zero-arg constructor is required. See this issue here:

https://issues.jboss.org/browse/RESTEASY-1538

That issue is closed and marked as resolve, citing language in the spec.

I think it would be helpful somewhere in section 3 that when using CDI with JAX-RS, constructor injection MUST be supported when a Resource class have a multi-arg constructor annotated with @Inject.

Similarly, it should also be permissible to do something like this:

@Providerpublic class JacksonObjectMapperProvider implements ContextResolver<ObjectMapper> {

private ObjectMapper objectMapper;

@Inject
public JacksonObjectMapperProvider(ObjectMapper objectMapper) {
    this.objectMapper = objectMapper;
}

@Override
public ObjectMapper getContext(Class<?> type) {
    return objectMapper;
}

}

Yet again, the above code works fine in Jersey + Weld while RESTEasy + Weld complains about not having a zero-arg constructor. As CDI starts to be folded into JAX-RS, some additional verbiage to make this abundantly clear that constructor injection must be supported in these use cases would be wonderful.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/eclipse-ee4j/jaxrs-api/issues/633, or mute the thread https://github.com/notifications/unsubscribe-auth/AC5XTlpM7dmyENwMoLAtI_XSJUON-CTLks5uCOu-gaJpZM4U-XX3 .

arjantijms avatar Jul 01 '18 18:07 arjantijms

Hi Ryan,

Section 11.2.8 of the JAX-RS spec states that constructor injection is OPTIONAL with CDI (and other beans). It recommends the use of field injection and @PostConstruct for portability.

— Santiago

On Jul 1, 2018, at 11:33 AM, Ryan J. McDonough [email protected] wrote:

One thing that would be very helpful for implementors is providing clarity on how JAX-RS Resource classes and Providers are instantiated when used with CDI. Presently, there's some significant behavioral differences when switching between JAX-RS implementations with CDI. For example, using Jersey 2.27 with JBoss Weld 3.0.4.Final , constructor injection works as you'd expect. With you attempt to do the same thing with JBoss RESTEasy 3.5.1.Final and Weld 3.0.4.Final, you get an error stating that a 0-arg constructor is required. The JBoss team is interpreting this language in section 3.1

By default a new resource class instance is created for each request to that resource As a zero-arg constructor is required. See this issue here:

https://issues.jboss.org/browse/RESTEASY-1538 https://issues.jboss.org/browse/RESTEASY-1538 That issue is closed and marked as resolve, citing language in the spec.

I think it would be helpful somewhere in section 3 that when using CDI with JAX-RS, constructor injection MUST be supported when a Resource class have a multi-arg constructor annotated with @Inject.

Similarly, it should also be permissible to do something like this:

@Provider public class JacksonObjectMapperProvider implements ContextResolver<ObjectMapper> {

private ObjectMapper objectMapper;

@Inject
public JacksonObjectMapperProvider(ObjectMapper objectMapper) {
    this.objectMapper = objectMapper;
}

@Override
public ObjectMapper getContext(Class<?> type) {
    return objectMapper;
}

} Yet again, the above code works fine in Jersey + Weld while RESTEasy + Weld complains about not having a zero-arg constructor. As CDI starts to be folded into JAX-RS, some additional verbiage to make this abundantly clear that constructor injection must be supported in these use cases would be wonderful.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/eclipse-ee4j/jaxrs-api/issues/633, or mute the thread https://github.com/notifications/unsubscribe-auth/AFU9N6AO-PpH7xeUxDoDHA9GsNPKwBH1ks5uCOu-gaJpZM4U-XX3.

spericas avatar Jul 05 '18 15:07 spericas

@spericas

Section 11.2.8 of the JAX-RS spec states that constructor injection is OPTIONAL with CDI (and other beans). It recommends the use of field injection and @PostConstruct for portability.

Maybe it's only a matter of rephrasing that saying that "constructor injection is NON-OPTIONAL (MUST be supported)", and that "field injection and @PostConstruct is a style choice and has no influence on portability"?

arjantijms avatar Jul 05 '18 15:07 arjantijms

@spericas I'm wondering why constructor injection was specified as being OPTIONAL back then. Is there any special reason why it wasn't defined as mandatory.

chkal avatar Jul 07 '18 06:07 chkal

Christian,

I would need to dig up that discussion to be certain, but I suspect it is related to the usual problem of combining DI systems. JAX-RS constructors much support param injection of @Context, etc. so, from an implementation perspective, it can get tricky to instantiate JAX-RS/CDI resource classes.

We could strengthen this in the spec, but is it really worth adding more implementation complexity? I much rather spend energy moving to a CDI-as-only-DI world.

— Santiago

On Jul 7, 2018, at 2:59 AM, Christian Kaltepoth [email protected] wrote:

@spericas https://github.com/spericas I'm wondering why constructor injection was specified as being OPTIONAL back then. Is there any special reason why it wasn't defined as mandatory.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/eclipse-ee4j/jaxrs-api/issues/633#issuecomment-403194205, or mute the thread https://github.com/notifications/unsubscribe-auth/AFU9N4ni4ZruZmb7A03G8M4siiaIdu6Vks5uEFw-gaJpZM4U-XX3.

spericas avatar Jul 09 '18 13:07 spericas

I would need to dig up that discussion to be certain, but I suspect it is related to the usual problem of combining DI systems.

Very practical speaking we have used something like this in Payara where both HK2 and CDI tried to inject something. By making it a constructor injection which only CDI does, the conflict was "resolved" (although it's indeed not an ideal solution and there should be a veto actually).

much rather spend energy moving to a CDI-as-only-DI world.

Perhaps you're right, and we shouldn't spend energy on this.

Btw, do you remember why CDI was not chosen right away as the only DI when JAX-RS 1.0 or 1.1 was released? I know that JAX-RS 1.0 went final before CDI 1.0, but it was quite clear that CDI was coming.

arjantijms avatar Jul 09 '18 14:07 arjantijms

On Jul 9, 2018, at 10:55 AM, Arjan Tijms [email protected] wrote:

I would need to dig up that discussion to be certain, but I suspect it is related to the usual problem of combining DI systems.

Very practical speaking we have used something like this in Payara where both HK2 and CDI tried to inject something. By making it a constructor injection which only CDI does, the conflict was "resolved" (although it's indeed not an ideal solution and there should be a veto actually).

much rather spend energy moving to a CDI-as-only-DI world.

Perhaps you're right, and we shouldn't spend energy on this.

Btw, do you remember why CDI was not chosen right away as the only DI when JAX-RS 1.0 or 1.1 was released? I know that JAX-RS 1.0 went final before CDI 1.0, but it was quite clear that CDI was coming.

I wasn’t part of that EG. Yes, CDI was coming but also recall that JAX-RS always supported a standalone mode and, at the time, CDI implementations were considered somewhat large and complex (more than pure DI for sure, which was the one part JAX-RS needed) so I think the decision made at the time is perfectly understandable given the constraints.

— Santiago

spericas avatar Jul 09 '18 16:07 spericas

To second Santiago, as far as I can remember it was not so clear than today that CDI will be embraced such globally. JAX-RS needed a solution that will be there when JAX-RS 1.0 is there, and that is there as long JAX-RS is there. CDI could have failed.

mkarg avatar Jul 09 '18 16:07 mkarg

I wasn’t part of that EG. Yes, CDI was coming but also recall that JAX-RS always supported a standalone mode and, at the time, CDI implementations were considered somewhat large and complex

I can appreciate these reasons, although at the time JAX-RS 1.0 was released CDI implementations hadn't been build yet really (OWB, CanDI and Weld were all in progress or even just started), so maybe that couldn't have been the case. Or at the very least, maybe there was the fear that they would eventually get large and complex.

(more than pure DI for sure, which was the one part JAX-RS needed) so I think the decision made at the time is perfectly understandable given the constraints.

There was JSR 330 though at that same time period, which was only DI. In fact that is what HK2 was eventually made compatible with and which is what Jersey had been using.

JAX-RS needed a solution that will be there when JAX-RS 1.0 is there, and that is there as long JAX-RS is there. CDI could have failed.

That sounds a bit more likely. At the time the mailing list wasn't open, but does anyone still have an actual mail were this decision was made? I remember that at some point Bill Burke mentioned some thing about JAX-RS going to use its own DI engine, and then Gavin King was about to talk to the JAX-RS EG about that, but not sure if that ever happened.

arjantijms avatar Jul 09 '18 17:07 arjantijms

On Jul 9, 2018, at 1:23 PM, Arjan Tijms [email protected] wrote:

I wasn’t part of that EG. Yes, CDI was coming but also recall that JAX-RS always supported a standalone mode and, at the time, CDI implementations were considered somewhat large and complex

I can appreciate these reasons, although at the time JAX-RS 1.0 was released CDI implementations hadn't been build yet really (OWB, CanDI and Weld were all in progress or even just started), so maybe that couldn't have been the case. Or at the very least, maybe there was the fear that they would eventually get large and complex.

The CDI spec already included Events and many other features that were not needed (and was likely bigger than JAX-RS at the time).

(more than pure DI for sure, which was the one part JAX-RS needed) so I think the decision made at the time is perfectly understandable given the constraints.

There was JSR 330 though at that same time period, which was only DI. In fact that is what HK2 was eventually made compatible with and which is what Jersey had been using.

That is just a few annotations, would have been very confusing if JAX-RS defined different semantics for them.

JAX-RS needed a solution that will be there when JAX-RS 1.0 is there, and that is there as long JAX-RS is there. CDI could have failed.

That sounds a bit more likely. At the time the mailing list wasn't open, but does anyone still have an actual mail were this decision was made? I remember that at some point Bill Burke mentioned some thing about JAX-RS going to use its own DI engine, and then Gavin King was about to talk to the JAX-RS EG about that, but not sure if that ever happened.

Interesting, didn’t know that.

— Santiago

spericas avatar Jul 09 '18 18:07 spericas

We could strengthen this in the spec, but is it really worth adding more implementation complexity? I much rather spend energy moving to a CDI-as-only-DI world.

Maybe I'm missing something obvious here, but IMO this issue is actually about improving integration with CDI. So fixing it is an important step into the "CDI-as-only-DI world" direction. Currently implementations are not required to support CDI constructor injection which really feels weird for everyone familiar with CDI.

chkal avatar Jul 15 '18 06:07 chkal

On Jul 15, 2018, at 2:46 AM, Christian Kaltepoth [email protected] wrote:

We could strengthen this in the spec, but is it really worth adding more implementation complexity? I much rather spend energy moving to a CDI-as-only-DI world.

Maybe I'm missing something obvious here, but IMO this issue is actually about improving integration with CDI. So fixing it is an important step into the "CDI-as-only-DI world" direction. Currently implementations are not required to support CDI constructor injection which really feels weird for everyone familiar with CDI.

Of course, but I think the original request was about the 2.2 spec. Needless to say this needs to be addressed (with all the other issues) as part of 3.0.

— Santiago

spericas avatar Jul 15 '18 12:07 spericas

Of course, but I think the original request was about the 2.2 spec. Needless to say this needs to be addressed (with all the other issues) as part of 3.0.

If 2.2 already largely moved to CDI, is it still necessary for 3.0 then?

arjantijms avatar Jul 15 '18 12:07 arjantijms

If 2.2 already largely moved to CDI, is it still necessary for 3.0 then?

If the real issue is the implementation complexity of combining CDI's @Inject with JAX-RS's @Context in case of mixed constructor injection, the problem gets automatically solved by eliminating @Context as part of 3.0. At least this is my understanding.

chkal avatar Jul 15 '18 13:07 chkal

I just ran across this issue after doing a search for the same topic and realized I never followed up on it :) @spericas I was actually considering this for a 3.0 feature. Adding it to the 2.x spec seems rather disruptive. The challenge is that moving between JAX-RS implementations while using the same CDI implementation yield very different results. So yes @chkal, this is indeed about improving integration with CDI as it's not at all consistent across JAX-RS implementations at the moment. If CDI will replace things like @Context, the stance on constructor injection should be addressed head-on to avoid what is happening today.

Making constructor injection optional feels really weird from an end user perspective. Especially when I can have a non JAX-RS class use constructor injection but I am forced into field injection on JAX-RS resources or providers depending on the chosen implementation.

damnhandy avatar Aug 14 '19 01:08 damnhandy

I agree that it is very unintuitive that a JAX-RS controller would not support @Inject on its constructor. It forces me to use mutable fields on the controller and is inconsistent with non JAX-RS classes where Weld constructor wiring is no problem. I second the notion expressed elsewhere in this issue that this needs addressing. For example, constructor wiring is supported consistently in Spring

chrisgleissner avatar Feb 15 '20 22:02 chrisgleissner