0

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?

KG6ZVP
  • 3,610
  • 4
  • 26
  • 45
  • Try to inject `WidgetArchiveResource` and not manually create this class with `new`. Provide a default constructor and set the year through a method. You might want to use `bean-discovery-mode="all"` in beans.xml – simdevmon Mar 29 '16 at 11:35
  • This is a kind of learning exercise for me. I've seen initresource used in this way and I'm trying to understand it. – KG6ZVP Mar 29 '16 at 15:31

1 Answers1

2

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
}
OndroMih
  • 7,280
  • 1
  • 26
  • 44
  • Is there any way to do this without having to instantiate a WidgetArchiveResource every time something on the "widgets" path is called? – KG6ZVP Mar 29 '16 at 20:04
  • You may assign it a bigger scope. The biggest scope is `@ApplicationScoped`, which means that it will be created once and then reused until application terminates. You may remove @Dependent and add `@ApplicationScoped`. You may also mark `MasterResource` like this, instead of `@RequestScoped`. But mind that you should not store any state between REST requests, that's why I recommend `@RequestScoped` – OndroMih Mar 29 '16 at 21:29
  • So there's no way to use RequestScoped on the master without instantiating all subresources even if they're not used in the request? – KG6ZVP Mar 29 '16 at 21:40
  • Oh, I misunderstood. It is possible, you may use Instance to inject the object dynamically when needed. I will update my answer. – OndroMih Mar 29 '16 at 21:53