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?
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>>
In addition to the answers, to read the Response object back on the client :
List<MyObject> list = response.readEntity(new GenericType<List<MyObject>>(){}));
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