I'm trying to minimize repeated code for a number of JAX-RS resource handlers, all of which require a few of the same path and query parameters. The basic url template for each resource looks like this:
/{id}/resourceName
and each resource has multiple subresources:
/{id}/resourceName/subresourceName
So, resource/subresource paths (incl. query parameters) might look like
/12345/foo/bar?xyz=0
/12345/foo/baz?xyz=0
/12345/quux/abc?xyz=0
/12345/quux/def?xyz=0
The common parts across resources foo and quux are @PathParam("id") and @QueryParam("xyz"). I could implement the resource classes like this:
// FooService.java
@Path("/{id}/foo")
public class FooService
{
    @PathParam("id") String id;
    @QueryParam("xyz") String xyz;
    
    @GET @Path("bar")
    public Response getBar() { /* snip */ }
    
    @GET @Path("baz")
    public Response getBaz() { /* snip */ }
}
// QuuxService.java
@Path("/{id}/quux")
public class QuxxService
{
    @PathParam("id") String id;
    @QueryParam("xyz") String xyz;
    
    @GET @Path("abc")
    public Response getAbc() { /* snip */ }
    
    @GET @Path("def")
    public Response getDef() { /* snip */ }
}
I've managed to avoid repeating the parameter injection into every single get* method.1  This is a good start, but I'd like to be able to avoid the repetition across resource classes as well. An approach that works with CDI (which I also need) is to use an abstract base class which FooService and QuuxService could extend:
// BaseService.java
public abstract class BaseService
{
    // JAX-RS injected fields
    @PathParam("id") protected String id;
    @QueryParam("xyz") protected String xyz;
    
    // CDI injected fields
    @Inject protected SomeUtility util;
}
// FooService.java
@Path("/{id}/foo")
public class FooService extends BaseService
{
    @GET @Path("bar")
    public Response getBar() { /* snip */ }
    
    @GET @Path("baz")
    public Response getBaz() { /* snip */ }
}
// QuuxService.java
@Path("/{id}/quux")
public class QuxxService extends BaseService
{   
    @GET @Path("abc")
    public Response getAbc() { /* snip */ }
    
    @GET @Path("def")
    public Response getDef() { /* snip */ }
}
Inside of the get* methods, the CDI injection (miraculously) works correctly: the util field is not null. Unfortunately, the JAX-RS injection does not work; id and xyz are null in the get* methods of FooService and QuuxService.
Given that the CDI works as I'd like it to, I'm wondering if the failure to inject @PathParams (etc.) into subclasses is a bug or just part of the JAX-RS spec.
Another approach I have already tried is using BaseService as a single point of entry that delegates to FooService and QuuxService as needed. This is basically as described in RESTful Java with JAX-RS using subresource locators.
// BaseService.java
@Path("{id}")
public class BaseService
{
    @PathParam("id") protected String id;
    @QueryParam("xyz") protected String xyz;
    @Inject protected SomeUtility util;
    
    public BaseService () {} // default ctor for JAX-RS
    
    // ctor for manual "injection"
    public BaseService(String id, String xyz, SomeUtility util)
    {
        this.id = id;
        this.xyz = xyz;
        this.util = util;
    }
    
    @Path("foo")
    public FooService foo()
    {
        return new FooService(id, xyz, util); // manual DI is ugly
    }
    
    @Path("quux")
    public QuuxService quux()
    {
        return new QuuxService(id, xyz, util); // yep, still ugly
    }
}
// FooService.java
public class FooService extends BaseService
{
    public FooService(String id, String xyz, SomeUtility util)
    {
        super(id, xyz, util); // the manual DI ugliness continues
    }
    
    @GET @Path("bar")
    public Response getBar() { /* snip */ }
    
    @GET @Path("baz")
    public Response getBaz() { /* snip */ }
}
// QuuxService.java
public class QuuzService extends BaseService
{
    public FooService(String id, String xyz, SomeUtility util)
    {
        super(id, xyz, util); // the manual DI ugliness continues
    }
    
    @GET @Path("abc")
    public Response getAbc() { /* snip */ }
    
    @GET @Path("def")
    public Response getDef() { /* snip */ }
}
The downside to this approach is that neither CDI injection nor JAX-RS injection works in the subresource classes. The reason for this is fairly obvious2, but what that means is that I have to manually re-inject the fields into the subclasses' constructor, which is messy, ugly, and doesn't easily let me customize further injection. Example: say I wanted to @Inject an instance into FooService but not QuuxService. Because I'm explicitly instantiating the subclasses of BaseService, CDI injection won't work, so the ugliness is continued.
With a bit of direction from @Tarlog, I think I've found the answer to one of my questions,
Why aren't inherited fields injected by JAX-RS?
In JSR-311 §3.6:
If a subclass or implementation method has any JAX-RS annotations then all of the annotations on the super class or interface method are ignored.
I'm sure that there's a real reason for this decision, but unfortunately that fact is working against me in this particular use case. I'm still interested in any possible workarounds.
1 The caveat with using field-level injection is that I'm now tied to per-request resource class instantiation, but I can live with that.
2 Because I'm the one calling new FooService() rather than the container/the JAX-RS implementation.
Here is a workaround I'm using:
Define a constructor for the BaseService with 'id' and 'xyz' as params:
// BaseService.java
public abstract class BaseService
{
    // JAX-RS injected fields
    protected final String id;
    protected final String xyz;
    public BaseService (String id, String xyz) {
        this.id = id;
        this.xyz = xyz;
    }
}
Repeat the constructor on all subclasses with the injects:
// FooService.java
@Path("/{id}/foo")
public class FooService extends BaseService
{
    public FooService (@PathParam("id") String id, @QueryParam("xyz") String xyz) {
        super(id, xyz);
    }
    @GET @Path("bar")
    public Response getBar() { /* snip */ }
    @GET @Path("baz")
    public Response getBaz() { /* snip */ }
}
Looking at Jax's JIRA it seems someone asked for annotation inheritance as milestone for JAX-RS.
The feature you're looking for just doesn't exist in JAX-RS yet, however, would this work? It's ugly, but prevents recurrent injection.
public abstract class BaseService
{
    // JAX-RS injected fields
    @PathParam("id") protected String id;
    @QueryParam("xyz") protected String xyz;
    // CDI injected fields
    @Inject protected SomeUtility util;
    @GET @Path("bar")
    public abstract Response getBar();
    @GET @Path("baz")
    public abstract Response getBaz();
    @GET @Path("abc")
    public abstract Response getAbc();
    @GET @Path("def")
    public abstract Response getDef();
}
// FooService.java
@Path("/{id}/foo")
public class FooService extends BaseService
{
    public Response getBar() { /* snip */ }
    public Response getBaz() { /* snip */ }
}
// QuuxService.java
@Path("/{id}/quux")
public class QuxxService extends BaseService
{   
    public Response getAbc() { /* snip */ }
    public Response getDef() { /* snip */ }
}
Or in another workaround :
public abstract class BaseService
{
    @PathParam("id") protected String id;
    @QueryParam("xyz") protected String xyz;
    // CDI injected fields
    @Inject protected SomeUtility util;
    @GET @Path("{stg}")
    public abstract Response getStg(@Pathparam("{stg}") String stg);
}
// FooService.java
@Path("/{id}/foo")
public class FooService extends BaseService
{
    public Response getStg(String stg) {
        if(stg.equals("bar")) {
              return getBar();
        } else {
            return getBaz();
        }
    }
    public Response getBar() { /* snip */ }
    public Response getBaz() { /* snip */ }
}
But seeing how touchy you are, frankly, I doubt your frustration will go away with this ugly code :)
In RESTEasy one can construct a class, annotate with @*Param as usual, and finish by annotating the class @Form. This @Form class may then be a parameter injection into any other service's method call. http://docs.jboss.org/resteasy/docs/2.3.5.Final/userguide/html/_Form.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With