2

I have a Jersey REST service set up like this:

@GET
@Path(ResourceConstants.PATH_START_BATCH)
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public ServiceResponse startBatch(@QueryParam(ResourceConstants.PARAM_BATCH_ID) String batchId)
{
    ServiceResponse serviceResponse = new ServiceResponse();

    try
    {
        // will throw exception on error
        this.processorService.startBatch(batchId);

        Map<String, String> responseDetails = serviceResponse.getResponseDetails();
        responseDetails.put("success", "success");

        return serviceResponse;
    }
    catch (Exception ex)
    {
        Map<String, String> responseDetails = serviceResponse.getResponseDetails();
        responseDetails.put("Failure", "Failed to start with exception " + ex.getMessage());
        throw WebServiceException.generate(ex, serviceResponse);
    }
    finally
    {
        logger.debug("Exit startBatch");
    }
}

where the ServiceResponse looking like this:

@XmlRootElement
public class ServiceResponse implements Serializable
{
    private static final long serialVersionUID = 1L;

    private HashMap<String, String> responseDetails;

    public ServiceResponse()
    {
        this.setResponseDetails(new HashMap<String, String>());
    }

    public HashMap<String, String> getResponseDetails()
    {
        return responseDetails;
    }

    public void setResponseDetails(HashMap<String, String> responseDetails)
    {
        this.responseDetails = responseDetails;
    }
}

I've written a little test client that looks like this:

public class ProcessorClient
{
    public static void main(String[] args)
    {
        String batchId = "472539";

        Client client = ClientBuilder.newClient();
        client.register(JacksonFeature.class);

        WebTarget webTarget = client.target("http://localhost:8080/")
                .path(ResourceConstants.PATH_START_BATCH)
                .queryParam("batchId", batchId);

        Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_XML);

        Response response = invocationBuilder.get();

        int responseStatus = response.getStatus();

        ServiceResponse serviceResponse = response.readEntity(ServiceResponse.class);

        serviceResponse.getResponseDetails();
    }
}

Running the client works fine, the XML in the body is unmarshalled into a ServiceResponse object. However, when we change:

        Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_XML);

to:

        Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON);

we get errors. I use Maven and have included the following dependency:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.4</version>
</dependency>

and as shown above am registering the JacksonFeature on the client. But, I must be missing something. I haven't been able to find any good how-to/examples using Jackson and Jersey 2.x. What is missing so that the client can create the ServiceResponse object from the json string in the response body?

ERROR:

Exception in thread "main" javax.ws.rs.ProcessingException: Error reading entity from input stream.
    at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:867)
    at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:785)
    at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:96)
    at org.glassfish.jersey.client.ScopedJaxrsResponse.access$001(ScopedJaxrsResponse.java:56)
    at org.glassfish.jersey.client.ScopedJaxrsResponse$1.call(ScopedJaxrsResponse.java:77)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:397)
    at org.glassfish.jersey.client.ScopedJaxrsResponse.readEntity(ScopedJaxrsResponse.java:74)
    at ca.shaw.tno.oss.dvc.processor.client.ProcessorClient.main(ProcessorClient.java:36)
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.lang.String out of START_ARRAY token
 at [Source: org.glassfish.jersey.message.internal.EntityInputStream@7ce5b708; line: 1, column: 21] (through reference chain: ca.shaw.tno.oss.dvc.common.webresource.ServiceResponse["responseDetails"])
    at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163)
    at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:219)
    at org.codehaus.jackson.map.deser.std.StringDeserializer.deserialize(StringDeserializer.java:44)
    at org.codehaus.jackson.map.deser.std.StringDeserializer.deserialize(StringDeserializer.java:13)
    at org.codehaus.jackson.map.deser.std.MapDeserializer._readAndBind(MapDeserializer.java:319)
    at org.codehaus.jackson.map.deser.std.MapDeserializer.deserialize(MapDeserializer.java:249)
    at org.codehaus.jackson.map.deser.std.MapDeserializer.deserialize(MapDeserializer.java:33)
    at org.codehaus.jackson.map.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:299)
    at org.codehaus.jackson.map.deser.SettableBeanProperty$MethodProperty.deserializeAndSet(SettableBeanProperty.java:414)
    at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:697)
    at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:580)
    at org.codehaus.jackson.map.ObjectMapper._readValue(ObjectMapper.java:2704)
    at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1315)
    at org.codehaus.jackson.jaxrs.JacksonJsonProvider.readFrom(JacksonJsonProvider.java:419)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:239)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:211)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:139)
    at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1109)
    at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:851)
    ... 10 more

EDIT

Here's the response body back from the browser when requesting JSON:

{"responseDetails":{"entry":[{"key":"Failure","value":"Failed to start with exception Could not find batch with ID=abc"}]}}
sdoca
  • 7,832
  • 23
  • 70
  • 127
  • Updated with stacktrace – sdoca Jun 24 '14 at 14:31
  • But in general from REST interface point of view, I would say that it is better to use HTTP response codes and messages, than such solution to notify about result. – win_wave Jun 24 '14 at 20:24
  • was wrong with comment that it is duplicate, it is quite similar :) – win_wave Jun 24 '14 at 20:26
  • Are you sure that ServiceResponse object used in form response is exactly the same as used to read response? If you will get from you browser response from service, what kind of response it gets? i.e. access from browser `http://localhost:8080/"ResourceConstants.PATH_START_BATCH"?batchId=472539` – win_wave Jun 24 '14 at 20:28
  • checkout that resource: http://stackoverflow.com/questions/18002132/deserializing-into-a-hashmap-of-custom-objects-with-jackson – win_wave Jun 24 '14 at 20:30
  • Yes, it is the same ServiceResponse object that is used to both create and receive the response. The service and client are using a common library which contains the object. When I access the service using a browser, I get the ServiceResponse marshalled correctly as either JSON or XML dependending on my request headers. – sdoca Jun 25 '14 at 14:21
  • Can you publish JSON what you see in a browser – win_wave Jun 25 '14 at 17:05
  • Added browser response body (JSON) – sdoca Jun 25 '14 at 20:18
  • The JSON is incorrect, you serializer done something wrong. Can you try to have `Map` instead of `HashMap` in ServiceResponse? – win_wave Jun 25 '14 at 20:22

0 Answers0