I use Jersey and decided to go with GSON instead of Moxy for JSON handling (didn't like the fact that Moxy requires setters).
Everything works fine until now, except one very annoying issue in my JerseyTest
subclasses: custom GsonProvider
is not being recognized unless explicitly registered for each call. It is, however, being recognized if I deploy the application to Tomcat.
My ResourceConfig
:
@ApplicationPath("")
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(GsonProvider.class);
register(SomeResource.class);
}
}
Implementation of GsonProvider
(though I don't think it is related to the issue I experience):
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class GsonProvider<T> implements MessageBodyReader<T>, MessageBodyWriter<T> {
private final Gson mGson;
public GsonProvider() {
mGson = new GsonBuilder().create();
}
@Override
public boolean isReadable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return true;
}
@Override
public T readFrom(Class<T> type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
InputStream entityStream) throws IOException, WebApplicationException {
InputStreamReader reader = new InputStreamReader(entityStream, "UTF-8");
try {
return mGson.fromJson(reader, type);
} finally {
reader.close();
}
}
@Override
public boolean isWriteable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return true;
}
@Override
public long getSize(T t, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return -1;
}
@Override
public void writeTo(T t, Class<?> type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream) throws IOException, WebApplicationException {
PrintWriter printWriter = new PrintWriter(entityStream);
try {
String json = mGson.toJson(t);
printWriter.write(json);
printWriter.flush();
} finally {
printWriter.close();
}
}
}
This test results in MessageBodyProviderNotFoundException
:
public class SomeResourceTest extends JerseyTest {
@Override
public Application configure() {
return new MyResourceConfig();
}
@Test
public void someApi_200Returned() throws Exception {
// Arrange
// Act
SomeResponse response =
target("/somepath")
.request()
.post(Entity.json(""), SomeResponse.class);
// Assert
assertThat(response.getStatus(), is(200));
}
}
In order to resolve this issue I register GsonProvider
for request. The following change makes the test pass:
public class SomeResourceTest extends JerseyTest {
@Override
public Application configure() {
return new MyResourceConfig();
}
@Test
public void someApi_200Returned() throws Exception {
// Arrange
// Act
SomeResponse response =
target("/somepath")
.register(GsonProvider.class)
.request()
.post(Entity.json(""), SomeResponse.class);
// Assert
assertThat(response.getStatus(), is(200));
}
}
So, registration of GsonProvider
in MyResourceConfig
is good for deployment, but JerseyTest
requires additional registration per request.
While I can live with that, it is annoying, time consuming and will be hard to communicate to other team members. Any solution for this issue?
You haven't shown the stacktrace, but I'm pretty sure that if you look closely at it, it will show that it is actually a client side error. What you need to do is register the gson provider with the client also since you are trying to deserialize the response JSON to a POJO
@Override
public void configureClient(ClientConfig config) {
config.register(GsonProvider.class)
}
The configureClient
method is a method in the JerseyTest
that you can override.