CDI Injection not possible in a JAX-RS subresource instance created by ResourceContext initResource

Viewed 3748

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

  1. Is my assumption about why @Inject fails correct?
  2. Is there any way to make @Inject work in the sub resource?
  3. Is there a better solution to passing the ReportService instance from Resource to SubResource that is more "CDI/Java EE" like?
4 Answers

This works for me

Resource

@Path("reports")
public ReportsResource reportsResource() {
    return CDI.current().select(ReportsResource.class).get();
}

Subresource

@Inject ReportBean reportBean;

@GET
@Path("/{rptno}")
@Produces(MediaType.APPLICATION_XML)
public Report report(@PathParam("rptno") int rptNumber) throws Exception {
    return reportBean.getReport(rptNumber);
}
Related