Search code examples
jax-rsjavabeansglassfish-4java-ee-7

Jax-rs initialize subresource There was no object available for injection


I've got a subresource class which must be initialized with a parameter from the path and which also contains a reference to an EJB which must be injected.

Resource class:

@Path("widgets")
public class MasterResource{
    @Inject
    WidgetBean widgets;

    @Context
    ResourceContext rc;

    @Path("{year}")
    public WidgetArchives wArchives(@PathParam("year") String year){
        return rc.initResource(new WidgetArchiveResource(year));
    }
}

Subresource class

public class WidgetArchiveResource{
    @Inject
    WidgetBean widgets;

    public WidgetArchiveResource(String year){
        ....code
    }

    @GET
    public String doGet(){
        ....code using WidgetBean
    }
}

When I invoke get with the year, I see the following error:

org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=WidgetBean,parent=WidgetArchiveResource,qualifiers={},position=-1,optional=false,self=false,unqualified=null,542790913)

I'm new to Java EE. What am I doing wrong?


Solution

  • The method rc.initResource makes sense only if you need to inject some dependencies into the resource (e.g. annotated by @Inject). This is a JAX-RS specific way to inject dependencies into a bean. It is deprecated in a full JavaEE environment where the preferred way to inject dependencies is via CDI. rc.initResource does not inject all CDI beans (e.g. @EJB could work, but @Inject not).

    Hence, in JavaEE, the preferred way to do what you want to achieve would be to inject WidgetArchiveResource into parent MasterResource. CDI beans must have constructor without arguments, but you may pass year using setter. If you put your master resouce in request scope, it will be recreated for every request, making it safe for concurrent requests:

    @Path("widgets")
    @RequestScoped
    public class MasterResource{
        @Inject
        Instance<WidgetArchiveResource> waResources;
    
        @Path("{year}")
        public WidgetArchives wArchives(@PathParam("year") String year){
            WidgetArchiveResource waResource = waResources.get();
            waResource.setYear(year);
            return waResource;
        }
    }
    

    Also, you should either have beans.xml in your WEB-INF folder with bean-discovery-mode="all", or explicitly add dependent scope on your WidgetArchiveResource to make it CDI eligible:

    @Dependent
    public class WidgetArchiveResource {
        public WidgetArchiveResource() {} // optional no arg constructor
    }