Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the right way to organize Jersey resources using inheritance and generics?

I'm developing an app with Jersey where I have many resources. Although main functionality of these resources varies, they share lots of common methods (like list, read, update and etc). The app runs on Google App Engine and uses Guice for dependency injection.

My first approach was to have a generic AbstactResource which contains all common logic, and it's respectively extended by all other resources which add their required custom methods.

public class AbstractResource<T> {

@GET
public ListPage<T> list(@QueryParam("limit") Integer limit,
    @QueryParam("start") Integer start) {
    // ... implementation
}

@GET
@Path("/{id}")
public T get(@PathParam("id") Long id) {
    // ... implementation
}

And sample resource looks like:

public class TenantResource extends AbstractResource<Tenant> {
    // custom resource related methods here
}

Everything works fine in this case. The problems appear when I add one more level of abstraction. Let's say if I want to store history and changelogs only for some of my resources. I've created one more abstract class extending AbstractResource called AudiatableResource which adds the required functionality.

public abstract class AuditableResource<T extends AuditableModel> 
    extends AbstractResource {
        // here I override update and create methods to save changelogs
}

As you see the type parameter in this case has changed (now it extends AuditableModel).

New concrete resources will look like:

public class PropertyResource extends AuditableResource<Tenant> {
    // custom resource related methods here
}

In this case everything still works, but this time I'm getting lots of warning messages on start-up:

WARNING: Return type T of method public T com.pkg.AbstractResource.get(java.lang.Long) is not resolvable to a concrete type
WARNING: Return type T of method public T com.pkg.AbstractResource.getNew() is not resolvable to a concrete type
WARNING: Return type com.pkg.data.ListPage<T> of method public com.pkg.ListPage<T> com.pkg.AbstractResource.list(java.lang.Integer,java.lang.Integer) is not resolvable to a concrete type

I really wonder if this approach is correct using Jersey and if I can just ignore this messages. It would be interesting to know how resources are organized in cases when there are large number of them.

like image 569
turan Avatar asked May 04 '12 21:05

turan


1 Answers

One way to go is to separate the definition of the resources from the implementation.

  • Have very simple resource classes, defining the different services you want to offer. This way, the API you expose through rest is easily located and audited. The different methods are probably delegates to an implementation class
  • Implement the business logic of your resources in the implementations, where you might want to use inheritance to factor common behavior.

The reason you get those messages at runtime is that jersey uses runtime information about types in the resource. Generic type information being erased at compile time, it cannot get the actual return type of the generic class methods. If you provide a REST "facade" to your implementation, you can make this explicit.

public class Facade {
  private final PropertyResource propertyResource;
  public Facade() {
    propertyResource = new PropertyResource();
  }
  @GET
  @Path("somepath")
  public Tenant something() {
    return propertyResource.something();
  }
}
like image 63
tonio Avatar answered Oct 04 '22 12:10

tonio