Gzip compression for spring-android RestTemplate

The org.springframework.web.client.RestTemplate lacks support for gzip encoding. The RestTemplate is a class that is provided by the spring-android module for calling RESTfull services.

The Accept-Encoding header for gzip is not being added to the request by spring-android. I have added support for gzip to the RestTemplate and I created a patch for it. See the extended RestTemplate below:

package com.oudmaijer.android.rest;

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.*;

import java.io.IOException;
import java.net.URI;

/**
 * Adds support for gzip encoding to the Spring RestTemplate
 */
public class GzipRestTemplate extends RestTemplate {

    /**
     * {@inheritDoc}
     */
    @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);
            if( request.getHeaders() != null ) {
                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
            }
        }
        getErrorHandler().handleError(response);
    }


    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
            }
        }
    }

}

The least unobtrusive way to change the RestTemplate is not to change it. I decided to override the doExecute method because I want the RestTemplate functionality as it is right now. I also had to included the private functions being called by the RestTemplate. I could have used the exchange() function in the RestTemplate but this is to abstract IMO.

How to use

Instead of instantiating the Spring RestTemplate you can now use the GzipRestTemplate. And example of using the RestTemplate for reading JSON responses:

...
RestTemplate restTemplate = new GzipRestTemplate();
restTemplate.setRequestFactory(new SimpleClientHttpRequestFactory());

List<HttpMessageConverter<?>> mc = restTemplate.getMessageConverters();
MappingJacksonHttpMessageConverter json = new MappingJacksonHttpMessageConverter();

List<MediaType> supportedMediaTypes = new ArrayList<MediaType>();
supportedMediaTypes.add(new MediaType("text", "html"));
supportedMediaTypes.add(new MediaType("application", "json"));
json.setSupportedMediaTypes(supportedMediaTypes);
mc.add(json);
restTemplate.setMessageConverters(mc);

JsonResult jsonResult = restTemplate.getForObject(url, JsonResult.class);
...

And a pom.xml of an application that uses the RestTemplate:

...
<build>
	<sourceDirectory>src</sourceDirectory>
	<finalName>${project.artifactId}</finalName>
	<plugins>
		<plugin>
			<groupId>com.jayway.maven.plugins.android.generation2</groupId>
			<artifactId>maven-android-plugin</artifactId>
			<version>2.8.3</version>
			<configuration>
				<sdk>
					<path>/path/to/android-sdk-windows</path>
					<platform>8</platform>
				</sdk>
				<emulator>
					<avd>my_avd</avd>
				</emulator>
				<deleteConflictingFiles>true</deleteConflictingFiles>
				<undeployBeforeDeploy>true</undeployBeforeDeploy>
			</configuration>
			<extensions>true</extensions>
		</plugin>
		<plugin>
			<artifactId>maven-compiler-plugin</artifactId>
			<version>2.3.2</version>
		</plugin>
	</plugins>
</build>
<dependencies>
        ...
	<dependency>
		<groupId>com.google.android</groupId>
		<artifactId>android</artifactId>
		<version>2.2.1</version>
		<scope>provided</scope>
	</dependency>
	<dependency>
		<groupId>org.springframework.android</groupId>
		<artifactId>spring-android-rest-template</artifactId>
		<version>1.0.0.BUILD-SNAPSHOT</version>
		<scope>compile</scope>
	</dependency>
	<dependency>
		<groupId>commons-httpclient</groupId>
		<artifactId>commons-httpclient</artifactId>
		<version>3.1</version>
		<scope>compile</scope>
	</dependency>
	<dependency>
		<groupId>org.codehaus.jackson</groupId>
		<artifactId>jackson-mapper-asl</artifactId>
		<version>1.6.2</version>
		<scope>compile</scope>
	</dependency>
</dependencies>
<repositories>
	<!-- For testing against latest Spring snapshots -->
	<repository>
		<id>org.springframework.maven.snapshot</id>
		<name>Spring Maven Snapshot Repository</name>
		<url>http://maven.springframework.org/snapshot</url>
		<releases><enabled>false</enabled></releases>
		<snapshots><enabled>true</enabled></snapshots>
	</repository>
	<!-- For developing against latest Spring milestones -->
	<repository>
		<id>org.springframework.maven.milestone</id>
		<name>Spring Maven Milestone Repository</name>
		<url>http://maven.springframework.org/milestone</url>
		<snapshots><enabled>false</enabled></snapshots>
	</repository>
</repositories>
...

2 Responses to “Gzip compression for spring-android RestTemplate”

  1. […] This post was mentioned on Twitter by Ron Thijssen, Stephan Oudmaijer. Stephan Oudmaijer said: Gzip enabled RestTemplate for spring-android: http://www.oudmaijer.com/2011/01/28/gzip-compression-for-spring-android/ […]

  2. Roy Clarkson says:

    We’ve added gzip compression support in the Spring Android 1.0.0.M4 release. Thanks for your interest in the project!

    http://www.springsource.org/spring-android/news/1.0.0.m4-released