Staying DRY with JAX-RS

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.

Is there a fix or workaround for this problem?

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.


tl;dr What's the right way to avoid repeatedly injecting fields across JAX-RS resource handler classes?

And why aren't inherited fields injected by JAX-RS, while CDI has no issues with this?


Edit 1

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.

-------------Problems Reply------------

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

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 String id;
protected String xyz;

public BaseService (String id, String xyz) {
this.id = id;
this.xyz = zyx;
}
}

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 */ }
}

You can add a custom provider, particularly via AbstractHttpContextInjectable:

// FooService.java
@Path("/{id}/foo")
public class FooService
{
@Context CommonStuff common;

@GET @Path("bar")
public Response getBar() { /* snip */ }

@GET @Path("baz")
public Response getBaz() { /* snip */ }
}

@Provider
public class CommonStuffProvider
extends AbstractHttpContextInjectable<CommonStuff>
implements InjectableProvider<Context, Type>
{

...

@Override
public CommonStuff getValue(HttpContext context)
{
CommonStuff c = new CommonStuff();
c.id = ...initialize from context;
c.xyz = ...initialize from context;

return c;
}
}

Granted, you'll have to extract the path parameters and/or the query parameters the hard way from HttpContext, but you'll do it once in one place.

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 :)

What is the motivation of avoiding parameters injections?
If the motivation is avoiding of repeating hard-coded strings, so you can easily rename them, you can reuse "constants":

// FooService.java
@Path("/" + FooService.ID +"/foo")
public class FooService
{
public static final String ID = "id";
public static final String XYZ= "xyz";
public static final String BAR= "bar";

@PathParam(ID) String id;
@QueryParam(XYZ) String xyz;

@GET @Path(BAR)
public Response getBar() { /* snip */ }

@GET @Path(BAR)
public Response getBaz() { /* snip */ }
}

// QuuxService.java
@Path("/" + FooService.ID +"/quux")
public class QuxxService
{
@PathParam(FooService.ID) String id;
@QueryParam(FooService.XYZ) String xyz;

@GET @Path("abc")
public Response getAbc() { /* snip */ }

@GET @Path("def")
public Response getDef() { /* snip */ }
}

(Sorry for posting the second answer, but it was too long to put it in a comment of the previous answer)

Instead of using @PathParam, @QueryParam or any other param, you can use @Context UriInfo to access any types of parameters. So your code could be:

// FooService.java
@Path("/{id}/foo")
public class FooService
{
@Context UriInfo uriInfo;

public static String getIdParameter(UriInfo uriInfo) {
return uriInfo.getPathParameters().getFirst("id");
}

@GET @Path("bar")
public Response getBar() { /* snip */ }

@GET @Path("baz")
public Response getBaz() { /* snip */ }
}

// QuuxService.java
@Path("/{id}/quux")
public class QuxxService
{
@Context UriInfo uriInfo;

@GET @Path("abc")
public Response getAbc() { /* snip */ }

@GET @Path("def")
public Response getDef() { /* snip */ }
}

Pay attention that getIdParameter is static, so you can put it in some utility class and reuse accorss multiple classes.
UriInfo is guaranteed to be threadsafe, so you can keep resource class as singleton.

Category:java Views:1 Time:2011-05-03

Related post

  • Staying DRY with JAXB 2011-05-02

    I'm developing a number of Java classes that must serialize to XML in the following format: <foo value="123"/> <!-- or this --> <bar value="abc"/> <!-- or this --> <baz value="true"/> In the beginning, Foo.java looked so

  • Given LINQ to Entities does not support "Custom methods" how do you stay DRY? 2010-01-11

    I have run into this problem: Custom Methods & Extension Methods cannot be translated into a store expression Basically I have some complicated LINQ queries, so wanted to break them down into subqueries which are implemented as methods that retur

  • How to stay DRY when logic needs a C# and Javascript implementation? 2009-02-25

    I'm currently using the ASP.NT MVC RC1 to implement a basic timesheet application. I'd like to follow the DRY principles but finding it difficult in one particular case: One of my views, a partial view actually, has a number of textboxes that represe

  • How can I test common Rails controller behavior while staying DRY? 2010-08-18

    I've been writing RSpec tests for some Rails controllers and I've discovered a strong impulse to ensure that the Authlogic authentication is working properly. I also feel like I should be verifying that each action uses the same application-wide layo

  • Using Thinking Sphinx and staying DRY (Don't Repeat Yourself) 2010-12-21

    In the large project I'm working on we have many models we want to index and search. I'm finding myself repeating things over and over... and know this could be bad later when we make changes! Is there a good way to keep code DRY while using Thinking

  • Call a controller's method in other controllers (staying DRY) 2011-09-30

    I'm slightly new to Rails (i.e. stupid and need some teachin'). I have a controller (call it ControllerFoo) that performs a particular task (theMethod) which could be useful in other controllers (say, from within ControllerBar). So, of course, the me

  • In ASP.NET MVC3 how do you stay DRY with very similar but slightly different viewmodels? 2012-01-24

    In building an app, we created a generic object model to store some values, the viewmodel looks a bit like this at the moment: public class FooViewModel { public int ID { get; set; } public byte FooType { get; set; } [Required] [Display(Name = "Bar N

  • How to stay DRY? Do Not Repeat Yourself! 2009-12-03

    I find that one of the most frustrating aspects to software development is finding a solution to a problem, forgetting it, then being faced with the same issue in the future only to forgot how you previously solved it. Or to write a useful bit of cod

  • Staying DRY while testing a controller, authorized via CanCan 2010-10-28

    I'm retroactively writing some tests, using RSpec, for a Rails project. I'm using the CanCan gem to provide authorization. I decided to write a spec that will test the ability.rb model. I then went on to test my remaining models. I've moved on to con

  • How to stay DRY with validations in Domain objects and services vs validations in UI layer 2011-05-26

    I have searched for answers and even asked several questions on this subject, but haven't really found the right answer yet. How do I expose validation methods in my POCO Domain objects and services to the UI layer? Currently I am using web forms. Fo

  • Rails 3 – Desperate to stay dry with scopes and associations 2011-07-19

    I'm totally stumped and would love any help from you developers more clever than myself. Here are the relevant facts: Each topic has_many comments. The Comment model has a scope called very_popular, which we'll pretend involves comparing a several of

  • How to stay DRY whilst using LINQ to Entities and helper methods? 2011-08-15

    Lets say that I have a particular way of deciding whether some strings "match", like this: public bool stringsMatch(string searchFor, string searchIn) { if (string.IsNullOrEmpty(searchFor)) { return true; } return searchIn != null && (searchI

  • How to DRY on CRUD parts of my Rails app? 2009-01-25

    I am writing an app which - similarly to many apps out there - is 90% regular CRUD things and 10% "juice", where we need nasty business logic and more flexibility and customization. Regarding this 90%, I was trying to stick to the DRY principle as mu

  • Guidelines for applying DRY in Haskell function definitions 2009-05-06

    I have a question about whether or not a specific way of applying of the DRY principle is considered a good practice in Haskell.I'm going to present an example, and then ask whether the approach I'm taking is considered good Haskell style. In a nutsh

  • rails - DRY respond_to with repeated actions 2010-02-03

    In one of my rails controller, I must respond to several types of formats, so I use the typical respond_to chain: respond_to do |format| format.html { ... } format.mobile { ... } format.jpg { ... } format.xml { ... } format.js { ... } end Usually tha

  • An existing Traceability Matrix with Team Foundation Server 2010 - how to practice DRY? 2011-01-20

    We have an existing Traceability Matrix in Excel that has columns like: Project Business Rule Group Requirement ID Business Rule Type etc, etc I would like to stay DRY (don't repeat yourself) in the sense that when we create a new requirement in this

  • DRY options menus on Android 2011-06-11

    I'm learning about creating Options Menus for Android apps. In the guide it has the following tip for staying DRY with menus: Tip: If your application contains multiple activities and some of them provide the same Options Menu, consider creating an a

  • DRY user-input validation (clientside, serverside) using JSON-schema 2011-08-01

    As part of a extensive test-case, I'm building an ajax-based CMS-like application which provides CRUD-functionality on various documenttypes, e.g: articles, tags, etc. on the server and client-side I'm considering to use JSON-schema ( http://json-sch

  • DRY Principles. Is this too DRY? 2014-08-15

    Been following the DRY principles and trying not to repeat my code where possible and thus reusing controller actions and views. I have used the index.html.erb view for my Listings on the main page which you can see here http://frnsh.herokuapp.com/ T

Copyright (C) dskims.com, All Rights Reserved.

processed in 0.143 (s). 11 q(s)