I have a JAX-RS resource class that provides path routing to sub resource classes using @Context ResourceContext to create sub resource instances for each resource type. In this example I am instantiating a reporting sub resource.
Resource
@Context
ResourceContext rc;
@Path("reports")
public ReportsResource reportsResource() {
return rc.initResource(new ReportsResource());
}
The sub resource needs an instance of a ReportService class (defined with the @Stateless annotation), the natural solution would be to @Inject it ...
Report SubResource
@Inject
ReportsService rs;
@GET
@Path("{rptno}")
@Produces(MediaType.APPLICATION_XML)
public Report report(@PathParam("rptno") int rptNumber) throws Exception {
return rs.getReport(rptNumber);
}
My experience using Java EE7 with both Glassfish and WAS Liberty Profile is that an instance of ReportService rs is not injected, leaving rs as null and causing a NPE.
My assumption is that because the resource class is doing a "new ReportsResource()", CDI has no visibility to the ReportsResource instance and so ReportsResource is not container managed. This seems to be the same situation as this question Inject EJB into JAX-RS 2.0 subresource when subresource is got via ResourceContext
My solution is somewhat different, I chose to @Inject ReportService in the Resource class, then pass the instance on the ReportsResource constructor.
Modified Resource
@Inject
ReportsSerivce rs;
@Context
ResourceContext rc;
@Path("reports")
public ReportsResource reportsResource() {
return rc.initResource(new ReportsResource(rs));
}
Modified Report Subresource
public class ReportsResource {
private ReportsSerivce rs;
public ReportsResource(ReportsSerivce rs) {
this.rs = rs;
}
@Context
HttpHeaders headers;
@GET
@Path("{rptno}")
@Produces(MediaType.APPLICATION_XML)
public Report report(@PathParam("rptno") int rptNumber) throws Exception {
return rs.getReport(rptNumber);
}
So to my questions
- Is my assumption about why @Inject fails correct?
- Is there any way to make @Inject work in the sub resource?
- Is there a better solution to passing the ReportService instance from Resource to SubResource that is more "CDI/Java EE" like?
This works for me
Resource
Subresource
According to the documentation of Jersey (JAX-RS reference implementation) if you want your sub-resource's lifecycle to be managed by container, you have to return the class type and not an instance of it. You can then manage the lifecycle of sub-resource as you wish like any other container managed resource.
For example:
more info could be found on docs: https://jersey.java.net/documentation/latest/jaxrs-resources.html#d0e2496
If you want to inject CDI beans into JAX-RS resources, I don't recommend using rc.initResource. All it does is injection of fields into existing object, but it uses JAX-RS specific mechanism, similar to how injection worked for EJBs in JavaEE5, when CDI was not available.
It is better to use CDI and remove
ResourceContext
from your code.Example:
Resource
The master resource should be
@RequestScoped
so that it gets recreated for each request. Or you may inject using Intance to get new instance for every method call:Note that injecting the sub resource directly into the root resource may cause a problem. I'm sharing what I've learnt.
Following works.
And we might got a
NullPointerException
when we simple callBecause the injection of the
ChildrenResource
instance itself happens before it injected into theParentResource
.In this case,
Optional
might helpBut using
ResourceContext
can be said more proper.