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>>(){}));
Response object gets returned to client in response to request sent to a server. The Response class has Response.ResponseBuilder inner class that collects each properties set to its field of type Response.ResponseBuilder. Response.ResponseBuilder applies the Builder design pattern to construct Response Object.
The build() method is responsible to connect the chain of Response.ResponseBuilder objects formed in the course of build.
E.g.
Response.status(200);
status method return Response.ResponseBuilder after assigning STATUS
Response.status(200).entity( AnyObj );
entity object assigns the entity( the payload returned back) of type
Response.ResponseBuilder and gets assigned to the Response's instance varible of. After that, status also assigns the status and returns Response.ResponseBuilder instance. The builder connects them at time of build() method call.
Response.status(200).entity( obj ).build();
Finally, the build method constructs complete(with properties set forth) Response.
Now comes the question of GenericEntity object. It Represents a response entity of a generic type T.
For Instance,
GenericEntity> obj = new GenericEntity>(lst) {};
Response.status(200).entity( obj ).build();
obj is type of List as given above. entity method accepts Object type which is generic. In case, the need of specific type arises, you got to pass it as argument of GenericEntity type by which at runtime it's casted to object of Specific type.
Practical Use
Wanted for my Jersey Framework to respond JSON of Array type which was List object in my Java model back to client as part of Response object.
Therefore,
new GenericEntity> --- casts the object/payload to List type
new GenericEntity --- runtime type becomes String
Resource on Webservice Side
public Response findAllFruits(@QueryParam("frtID") String frtID ) {
List<String> lst = new ArrayList<String>();
lst.add("Banana");
lst.add("Apple");
GenericEntity<List<String>> obj = new GenericEntity<List<String>>(lst) {};
return Response.status(200).entity( obj ).build();
}
Output
Response sent back to client.
[
"Banana",
"Apple"
]
How to Read Response Entity
List<CustomType> myObj= response.readEntity(new GenericType<List<CustomType>>() {});
To solve it a made a method to return a GenericEntity
and then a invoke it from my generic class, some thing like below:
@XmlRootElement
public abstract class AbstractRest<T extends IEntity> {
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
public abstract class SmartWebServiceNEW<R extends AbstractRest<T>> {
@GET
@Produces({MediaType.APPLICATION_XML})
public Response findAll() {
List<T> lista = getDelegate().findAll();
if (lista == null || lista.isEmpty()) {
return Response.status(Response.Status.NO_CONTENT).build();
}
List<R> retorno = new ArrayList<R>();
for (T it : lista) {
retorno.add(toRest(it));
}
GenericEntity entity = listToGenericEntity(retorno);
return Response.ok(entity).build();
}
protected abstract GenericEntity listToGenericEntity(List<R> restList);
}
@Path("/something")
@RequestScoped
public class MyEntityResource extends SmartWebServiceNEW<MyEntityExtendingAbstractRest> {
@Override
protected GenericEntity listToGenericEntity(List<MyEntityExtendingAbstractRest> restList) {
return new GenericEntity<List<MyEntityExtendingAbstractRest>>(restList) {
};
}
}