Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Java-generics template type in RESTful Response object via GenericEntity<List<T>>

I have a generic JAX-RS resource class and I have defined a generic findAll method

public abstract class GenericDataResource<T extends GenericModel> {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response findAll() {
        Query query = em.createNamedQuery(modelClass.getSimpleName()+".findAll");
        List<T> list = query.getResultList();
        return Response.ok(new GenericEntity<List<T>>(list) {}).build();
    }
}

and User class:

public class User extends GenericModel {
    ...
}

And here is example subclass definition:

@Path("users")
public class UserResource extends GenericDataResource<User> {

    public UserResource() {
        super(User.class);
    }
}

I get below Exception:

com.sun.jersey.api.MessageException: A message body writer for Java class 
java.util.Vector, and Java type java.util.List<T>, 
and MIME media type application/json was not found exception.

If I replace T with a defined class such as User like so:

GenericEntity<List<User>>(list)

then it works fine.

Any idea as to how I can make it work with generic T?

like image 939
Emin Avatar asked Mar 14 '13 11:03

Emin


2 Answers

Once the source code is compiled, the (anonymous) class created by the line:

new GenericEntity<List<T>>(list) {}

uses a type variable to refer to its parent. Since type variables have no value at runtime, you can't use generics like this. You are forced to pass a so-called type token from the calling site. This is an example which requires the token to be passed from the caller of findAll(), but you could require one in the constructor and save it in an instance variable as well:

public abstract class GenericDataResource<T extends GenericModel> {
  public Response findAll(GenericEntity<List<T>> token) {
    Query query = em.createNamedQuery(modelClass.getSimpleName() + ".findAll");
    List<T> list = query.getResultList();
    return Response.ok(token).build();
  }
}

The caller will send a token like

new GenericEntity<List<User>>() {}

If you only use non-parameterized subclasses, findAll() may take advantage of reflection to build the token (untested, hope you get the idea):

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response findAll() {
  Query query = em.createNamedQuery(modelClass.getSimpleName()+".findAll");
  List<T> list = query.getResultList();
  return Response.ok(new GenericEntity(list, getType())).build();
}

You have to implement getType() to return the desired type. It will be a subclass of ParameterizedType capable of denoting the type List<DAO<User>>

like image 181
Raffaele Avatar answered Oct 27 '22 01:10

Raffaele


In addition to the answers, to read the Response object back on the client :

List<MyObject> list = response.readEntity(new GenericType<List<MyObject>>(){}));
like image 34
jediz Avatar answered Oct 27 '22 02:10

jediz