This weekend I created a small application that could gather information from the DISCOGS website using a RESTFul webservice they so very kindly provided. While I already had some previous experience using the RestTemplate provided by Spring I figured it would be a good idea to start there.
As with most of things IT related it didn’t all go according to plan. The RestTemplate I used didn’t support the gzip stream that was returned by the REST service published by Discogs. So how to fix this? There are two ways.
Firstly, you can use the exchange method included in the RestTemplate. I personally find this approach a bit verbose. Here it is:
HttpHeaders headers = new HttpHeaders();
headers.set("Accept-Encoding", "gzip,deflate");
T response = getRestTemplate().exchange(url,
HttpMethod.POST,
new HttpEntity<String>(headers),
responseType).getBody();
A nicer solution (as far as I am concerned) is extending the RestTemplate to create a GZip enabled template. Stephan Oudmaijer provided me with this code which I found pretty useful. In the code it’s more obvious you are using the resttemplate with gzip support, and you don’t have to change any code if you need the standard RestTemplate. It is somewhat less verbose than using the exchange method.
package org.walgemoed.util;
import java.io.IOException;
import java.net.URI;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.Assert;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
/**
* Fix for missing gzip support in Spring RestTemplate
*/
public class GZipRestTemplate extends RestTemplate {
@Override
protected <T> T doExecute(URI url, HttpMethod method,
RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "'url' must not be null");
Assert.notNull(method, "'method' must not be null");
ClientHttpResponse response = null;
try {
ClientHttpRequest request = createRequest(url, method);
request.getHeaders().add("Accept-Encoding", "gzip,deflate");
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
if (!getErrorHandler().hasError(response)) {
logResponseStatus(method, url, response);
} else {
handleResponseError(method, url, response);
}
if (responseExtractor != null) {
return responseExtractor.extractData(response);
} else {
return null;
}
} catch (IOException ex) {
throw new ResourceAccessException("I/O error: " + ex.getMessage(),
ex);
} finally {
if (response != null) {
response.close();
}
}
}
private void handleResponseError(HttpMethod method, URI url,
ClientHttpResponse response) throws IOException {
if (logger.isWarnEnabled()) {
try {
logger.warn(method.name() + " request for \"" + url
+ "\" resulted in " + response.getStatusCode() + " ("
+ response.getStatusText()
+ "); invoking error handler");
} catch (IOException e) {
// ignore
}
}
}
private void logResponseStatus(HttpMethod method, URI url,
ClientHttpResponse response) {
if (logger.isDebugEnabled()) {
try {
logger.debug(method.name() + " request for \"" + url
+ "\" resulted in " + response.getStatusCode() + " ("
+ response.getStatusText() + ")");
} catch (IOException e) {
// ignore
}
}
}
}
Some handling is still required, as you can see Exceptions are ignored in this snippet. The highlighted lines show where the gzip accept header is added to the mix in the template. After setting this header the resulting stream will be handled correctly. After using this template I was able to consume the REST services from Discogs.
Would still be nice to see easy gzip support in the RestTemplate by default though. Maybe in the future? For now this will do. For any of you interested in the code to execute searches on Discogs, let me know. I may be tempted to clean up the code and provide a generic API.