2
votes

I have a Java application built on Spring 3.2.0 performing an external call to a REST api serving JSON data.

The call is performed by the Spring RestTemplate class, with Jackson 2.2.3 as serializer/deserializer.

The call is functionnal and supports both plain and gzipped responses.

In order to Junit test the call, I use MockRestServiceServer. Everything works well, until I try to introduce gzip compression. I was unable to find in the offical doc how to activate gzip compression in MockRestServiceServer, so I went the manual route :

  • manually gzip the String content of the response

  • set the "Content-Encoding" to "gzip" in the headers

Unfortunately, I get the same error again and again, thrown by Jackson when deserializing the response body :

org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Illegal character ((CTRL-CHAR, code 31)): 
only regular white space (\r, \n, \t) is allowed between tokens
at [Source: java.io.ByteArrayInputStream@110d68a; line: 1, column: 2];
nested exception is com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 31)): 
only regular white space (\r, \n, \t) is allowed between tokens

Here is the current code (reworked due to corporate data...)

Test class

public class ImportRefCliCSTest {

    @Autowired
    private MyService myService;

    private MockRestServiceServer mockServer;

    @Before
    public void before() {
        mockServer = MockRestServiceServer.createServer(myService.getRestTemplate());
    }

    @Test
    public void testExternalCall() throws IOException {

        String jsonData = "[{\"testing\":\"Hurray!\"}]";
        HttpHeaders headers = new HttpHeaders();
        headers.add( "Content-Encoding", "gzip" );

        DefaultResponseCreator drc = withSuccess( gzip( jsonData ),
            MediaType.APPLICATION_JSON ).headers( headers );

        mockServer.expect( requestTo( myService.EXTERNAL_CALL_URL ) )
            .andExpect( method( HttpMethod.GET ) ).andRespond(drc);

        myService.performCall();
    }


    private static String gzip(String str) throws IOException {
        if (str == null || str.length() == 0) {
            return str;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        GZIPOutputStream gzip = new GZIPOutputStream(out);
        gzip.write(str.getBytes());
        gzip.close();
        String outStr = out.toString();
        return outStr;
    }
}

Service class

@Service
public class MyService {

    public static final String EXTERNAL_CALL_URL = "<myURL>";
    private RestTemplate restTemplate;

    {
        restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory(
            HttpClientBuilder.create().build()));
    }


    public void performCall() {

        try {
            HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.add("Accept-Encoding", "gzip");
            HttpEntity<MyObject[]> requestEntity = new HttpEntity<MyObject[]>(requestHeaders);

            ResponseEntity<MyObject[]> responseEntity = restTemplate.exchange(
                EXTERNAL_CALL_URL, HttpMethod.GET, requestEntity, MyObject[].class);
            MyObject[] array = responseEntity.getBody();
            if (array == null || array.length == 0) {
                return null;
            }
            return null;
        } catch (RestClientException e) {
            return null;
        }
    }

    public RestTemplate getRestTemplate(){
        return restTemplate;
    }
}

I feel I am missing something. The manual gzip compression seems rather suspicious.

Does anyone have an idea on this ?

Thanks in advance for your answers !

1

1 Answers

0
votes

When program convert gzip content to string, some bytes are collapsed. So client cannot decompress it and throw Exception. A solution is return byte[]:

private static byte[] gzip(String str) throws IOException {
    if (str == null || str.length() == 0) {
        return new byte[0];
    }
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    GZIPOutputStream gzip = new GZIPOutputStream(out);
    gzip.write(str.getBytes());//consider to use str.getBytes("UTF-8")
    gzip.close();
    return out.toByteArray();
}