Spring/apache rest client with both gzip compression and decompression

Well I’ve finally done it, it was a tricky thing but I got to enable both compression and decompression on rest template’s spring methods.

My webapp is hosted on a tomcat6 instance configured to inflate outgoing data so I don’t have to take care of it inside my webapp, pretty neat.

The following is a gzip enabled rest template used to convert xml data to business objects using JAXB.

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.web.client.HttpMessageConverterExtractor;
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;
import org.springframework.web.util.UriTemplate;
import org.springframework.web.util.UriUtils;
/**
 * @author Adrien Ferre
 * @since 22 juin 2012
 */
public class GzipEnabledRestTemplate extends RestTemplate {
private final ResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor();
public GzipEnabledRestTemplate(ClientHttpRequestFactory requestFactory) {
 super(requestFactory);
List<HttpMessageConverter<?>> messageConverters2 = this
 .getMessageConverters();
 Object obj = null;
 for (HttpMessageConverter<?> con : messageConverters2) {
 if (con instanceof Jaxb2RootElementHttpMessageConverter) {
 obj = con;
 }
 }
getMessageConverters().remove(obj);
 getMessageConverters().add(
 new GzipEnabledClientSideMarshallingHttpMessageConverter());
 }
// GET
public <T> T getForObject(String url, Class<T> responseType,
 boolean gzipIn, boolean gzipOut, Object... urlVariables)
 throws RestClientException {
 AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(
 responseType);
 HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<T>(
 responseType, getMessageConverters());
 return execute(url, HttpMethod.GET, requestCallback, responseExtractor,
 urlVariables, gzipIn, gzipOut);
 }
public <T> T getForObject(String url, Class<T> responseType,
 Map<String, ?> urlVariables, boolean gzipIn, boolean gzipOut)
 throws RestClientException {
 AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(
 responseType);
 HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<T>(
 responseType, getMessageConverters());
 return execute(url, HttpMethod.GET, requestCallback, responseExtractor,
 urlVariables, gzipIn, gzipOut);
 }
public <T> T getForObject(URI url, Class<T> responseType, boolean gzipIn,
 boolean gzipOut) throws RestClientException {
 AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(
 responseType);
 HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<T>(
 responseType, getMessageConverters());
 return execute(url, HttpMethod.GET, requestCallback, responseExtractor,
 gzipIn, gzipOut);
 }
public <T> ResponseEntity<T> getForEntity(String url,
 Class<T> responseType, boolean gzipIn, boolean gzipOut,
 Object... urlVariables) throws RestClientException {
 AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(
 responseType);
 ResponseEntityResponseExtractor<T> responseExtractor = new ResponseEntityResponseExtractor<T>(
 responseType);
 return execute(url, HttpMethod.GET, requestCallback, responseExtractor,
 urlVariables, gzipIn, gzipOut);
 }
public <T> ResponseEntity<T> getForEntity(String url,
 Class<T> responseType, boolean gzipIn, boolean gzipOut,
 Map<String, ?> urlVariables) throws RestClientException {
 AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(
 responseType);
 ResponseEntityResponseExtractor<T> responseExtractor = new ResponseEntityResponseExtractor<T>(
 responseType);
 return execute(url, HttpMethod.GET, requestCallback, responseExtractor,
 urlVariables, gzipIn, gzipOut);
 }
public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType,
 boolean gzipIn, boolean gzipOut) throws RestClientException {
 AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(
 responseType);
 ResponseEntityResponseExtractor<T> responseExtractor = new ResponseEntityResponseExtractor<T>(
 responseType);
 return execute(url, HttpMethod.GET, requestCallback, responseExtractor,
 gzipIn, gzipOut);
 }
// HEAD
public HttpHeaders headForHeaders(String url, boolean gzipIn,
 boolean gzipOut, Object... urlVariables) throws RestClientException {
 return execute(url, HttpMethod.HEAD, null, this.headersExtractor,
 urlVariables, gzipIn, gzipOut);
 }
public HttpHeaders headForHeaders(String url, boolean gzipIn,
 boolean gzipOut, Map<String, ?> urlVariables)
 throws RestClientException {
 return execute(url, HttpMethod.HEAD, null, this.headersExtractor,
 urlVariables, gzipIn, gzipOut);
 }
public HttpHeaders headForHeaders(URI url, boolean gzipIn, boolean gzipOut)
 throws RestClientException {
 return execute(url, HttpMethod.HEAD, null, this.headersExtractor,
 gzipIn, gzipOut);
 }
// POST
public URI postForLocation(String url, Object request, boolean gzipIn,
 boolean gzipOut, Object... urlVariables) throws RestClientException {
 HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(
 request);
 HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback,
 this.headersExtractor, gzipIn, gzipOut, urlVariables);
 return headers.getLocation();
 }
public URI postForLocation(String url, Object request, boolean gzipIn,
 boolean gzipOut, Map<String, ?> urlVariables)
 throws RestClientException {
 HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(
 request);
 HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback,
 this.headersExtractor, gzipIn, gzipOut, urlVariables);
 return headers.getLocation();
 }
public URI postForLocation(URI url, Object request, boolean gzipIn,
 boolean gzipOut) throws RestClientException {
 HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(
 request);
 HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback,
 this.headersExtractor, gzipIn, gzipOut);
 return headers.getLocation();
 }
public <T> T postForObject(String url, Object request,
 Class<T> responseType, boolean gzipIn, boolean gzipOut,
 Object... uriVariables) throws RestClientException {
 HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(
 request, responseType);
 HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<T>(
 responseType, getMessageConverters());
 return execute(url, HttpMethod.POST, requestCallback,
 responseExtractor, gzipIn, gzipOut, uriVariables);
 }
public <T> T postForObject(String url, Object request,
 Class<T> responseType, boolean gzipIn, boolean gzipOut,
 Map<String, ?> uriVariables) throws RestClientException {
 HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(
 request, responseType);
 HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<T>(
 responseType, getMessageConverters());
 return execute(url, HttpMethod.POST, requestCallback,
 responseExtractor, gzipIn, gzipOut, uriVariables);
 }
public <T> T postForObject(URI url, Object request, Class<T> responseType,
 boolean gzipIn, boolean gzipOut) throws RestClientException {
 HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(
 request, responseType);
 HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<T>(
 responseType, getMessageConverters());
 return execute(url, HttpMethod.POST, requestCallback,
 responseExtractor, gzipIn, gzipOut);
 }
public <T> ResponseEntity<T> postForEntity(String url, Object request,
 Class<T> responseType, boolean gzipIn, boolean gzipOut,
 Object... uriVariables) throws RestClientException {
 HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(
 request, responseType);
 ResponseEntityResponseExtractor<T> responseExtractor = new ResponseEntityResponseExtractor<T>(
 responseType);
 return execute(url, HttpMethod.POST, requestCallback,
 responseExtractor, gzipIn, gzipOut, uriVariables);
 }
public <T> ResponseEntity<T> postForEntity(String url, Object request,
 Class<T> responseType, boolean gzipIn, boolean gzipOut,
 Map<String, ?> uriVariables) throws RestClientException {
 HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(
 request, responseType);
 ResponseEntityResponseExtractor<T> responseExtractor = new ResponseEntityResponseExtractor<T>(
 responseType);
 return execute(url, HttpMethod.POST, requestCallback,
 responseExtractor, gzipIn, gzipOut, uriVariables);
 }
public <T> ResponseEntity<T> postForEntity(URI url, Object request,
 Class<T> responseType, boolean gzipIn, boolean gzipOut)
 throws RestClientException {
 HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(
 request, responseType);
 ResponseEntityResponseExtractor<T> responseExtractor = new ResponseEntityResponseExtractor<T>(
 responseType);
 return execute(url, HttpMethod.POST, requestCallback,
 responseExtractor, gzipIn, gzipOut);
 }
// PUT
public void put(String url, Object request, boolean gzipIn,
 boolean gzipOut, Object... urlVariables) throws RestClientException {
 HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(
 request);
 execute(url, HttpMethod.PUT, requestCallback, null, gzipIn, gzipOut,
 urlVariables);
 }
public void put(String url, Object request, boolean gzipIn,
 boolean gzipOut, Map<String, ?> urlVariables)
 throws RestClientException {
 HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(
 request);
 execute(url, HttpMethod.PUT, requestCallback, null, gzipIn, gzipOut,
 urlVariables);
 }
public void put(URI url, Object request, boolean gzipIn, boolean gzipOut)
 throws RestClientException {
 HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(
 request);
 execute(url, HttpMethod.PUT, requestCallback, null, gzipIn, gzipOut);
 }
// DELETE
public void delete(String url, boolean gzipIn, boolean gzipOut,
 Object... urlVariables) throws RestClientException {
 execute(url, HttpMethod.DELETE, null, null, gzipIn, gzipOut,
 urlVariables);
 }
public void delete(String url, boolean gzipIn, boolean gzipOut,
 Map<String, ?> urlVariables) throws RestClientException {
 execute(url, HttpMethod.DELETE, null, null, gzipIn, gzipOut,
 urlVariables);
 }
public void delete(URI url, boolean gzipIn, boolean gzipOut)
 throws RestClientException {
 execute(url, HttpMethod.DELETE, null, null, gzipIn, gzipOut);
 }
// OPTIONS
public Set<HttpMethod> optionsForAllow(String url, boolean gzipIn,
 boolean gzipOut, Object... urlVariables) throws RestClientException {
 HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null,
 this.headersExtractor, gzipIn, gzipOut, urlVariables);
 return headers.getAllow();
 }
public Set<HttpMethod> optionsForAllow(String url, boolean gzipIn,
 boolean gzipOut, Map<String, ?> urlVariables)
 throws RestClientException {
 HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null,
 this.headersExtractor, gzipIn, gzipOut, urlVariables);
 return headers.getAllow();
 }
public Set<HttpMethod> optionsForAllow(URI url, boolean gzipIn,
 boolean gzipOut) throws RestClientException {
 HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null,
 this.headersExtractor, gzipIn, gzipOut);
 return headers.getAllow();
 }
// exchange
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
 HttpEntity<?> requestEntity, Class<T> responseType, boolean gzipIn,
 boolean gzipOut, Object... uriVariables) throws RestClientException {
 HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(
 requestEntity, responseType);
 ResponseEntityResponseExtractor<T> responseExtractor = new ResponseEntityResponseExtractor<T>(
 responseType);
 return execute(url, method, requestCallback, responseExtractor, gzipIn,
 gzipOut, uriVariables);
 }
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
 HttpEntity<?> requestEntity, Class<T> responseType, boolean gzipIn,
 boolean gzipOut, Map<String, ?> uriVariables)
 throws RestClientException {
 HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(
 requestEntity, responseType);
 ResponseEntityResponseExtractor<T> responseExtractor = new ResponseEntityResponseExtractor<T>(
 responseType);
 return execute(url, method, requestCallback, responseExtractor, gzipIn,
 gzipOut, uriVariables);
 }
public <T> ResponseEntity<T> exchange(URI url, HttpMethod method,
 HttpEntity<?> requestEntity, Class<T> responseType, boolean gzipIn,
 boolean gzipOut) throws RestClientException {
 HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(
 requestEntity, responseType);
 ResponseEntityResponseExtractor<T> responseExtractor = new ResponseEntityResponseExtractor<T>(
 responseType);
 return execute(url, method, requestCallback, responseExtractor, gzipIn,
 gzipOut);
 }
// general execution
public <T> T execute(String url, HttpMethod method,
 RequestCallback requestCallback,
 ResponseExtractor<T> responseExtractor, boolean gzipIn,
 boolean gzipOut, Object... urlVariables) throws RestClientException {
UriTemplate uriTemplate = new HttpUrlTemplate(url);
 URI expanded = uriTemplate.expand(urlVariables);
 return doExecute(expanded, method, requestCallback, responseExtractor,
 gzipIn, gzipOut);
 }
public <T> T execute(String url, HttpMethod method,
 RequestCallback requestCallback,
 ResponseExtractor<T> responseExtractor, boolean gzipIn,
 boolean gzipOut, Map<String, ?> urlVariables)
 throws RestClientException {
UriTemplate uriTemplate = new HttpUrlTemplate(url);
 URI expanded = uriTemplate.expand(urlVariables);
 return doExecute(expanded, method, requestCallback, responseExtractor,
 gzipIn, gzipOut);
 }
public <T> T execute(URI url, HttpMethod method,
 RequestCallback requestCallback,
 ResponseExtractor<T> responseExtractor, boolean gzipIn,
 boolean gzipOut) throws RestClientException {
return doExecute(url, method, requestCallback, responseExtractor,
 gzipIn, gzipOut);
 }
/**
 * 
 * Will create a request and add the gzip related headers if needed.
 * 
 * @param url
 * the request url
 * @param method
 * the method
 * @param gzipIn
 * expects gizp compressed response if any
 * @param gzipOut
 * expects gzip comrpessed request body if any
 * @return
 * @throws IOException
 */
 protected ClientHttpRequest createGzipRequest(URI url, HttpMethod method,
 boolean gzipIn, boolean gzipOut) throws IOException {
 ClientHttpRequest request = createRequest(url, method);
 if (gzipIn) {
 request.getHeaders().add("Accept-Encoding", "gzip,deflate");
 request.getHeaders().add("Accept", "charset=UTF-8");
 }
 if (gzipOut) {
 request.getHeaders().add("Content-Encoding", "gzip");
 }
 return request;
 }
protected <T> T doExecute(URI url, HttpMethod method,
 RequestCallback requestCallback,
 ResponseExtractor<T> responseExtractor, boolean gzipIn,
 boolean gzipOut) throws RestClientException {
 Assert.notNull(url, "'url' must not be null");
 Assert.notNull(method, "'method' must not be null");
 ClientHttpResponse response = null;
 try {
 ClientHttpRequest request = createGzipRequest(url, method, gzipIn,
 gzipOut);
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
 }
 }
 }
/**
 * Request callback implementation that prepares the request's accept
 * headers.
 */
 private class AcceptHeaderRequestCallback implements RequestCallback {
private final Class<?> responseType;
private AcceptHeaderRequestCallback(Class<?> responseType) {
 this.responseType = responseType;
 }
@SuppressWarnings("unchecked")
 public void doWithRequest(ClientHttpRequest request) throws IOException {
 if (responseType != null) {
 List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
 for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
 if (messageConverter.canRead(responseType, null)) {
 List<MediaType> supportedMediaTypes = messageConverter
 .getSupportedMediaTypes();
 for (MediaType supportedMediaType : supportedMediaTypes) {
 if (supportedMediaType.getCharSet() != null) {
 supportedMediaType = new MediaType(
 supportedMediaType.getType(),
 supportedMediaType.getSubtype());
 }
 allSupportedMediaTypes.add(supportedMediaType);
 }
 }
 }
 if (!allSupportedMediaTypes.isEmpty()) {
 MediaType.sortBySpecificity(allSupportedMediaTypes);
 if (logger.isDebugEnabled()) {
 logger.debug("Setting request Accept header to "
 + allSupportedMediaTypes);
 }
 request.getHeaders().setAccept(allSupportedMediaTypes);
 }
 }
 }
 }
/**
 * Request callback implementation that writes the given object to the
 * request stream.
 */
 private class HttpEntityRequestCallback extends AcceptHeaderRequestCallback {
private final HttpEntity requestEntity;
private HttpEntityRequestCallback(Object requestBody) {
 this(requestBody, null);
 }
@SuppressWarnings("unchecked")
 private HttpEntityRequestCallback(Object requestBody,
 Class<?> responseType) {
 super(responseType);
 if (requestBody instanceof HttpEntity) {
 this.requestEntity = (HttpEntity) requestBody;
 } else if (requestBody != null) {
 this.requestEntity = new HttpEntity(requestBody);
 } else {
 this.requestEntity = HttpEntity.EMPTY;
 }
 }
@Override
 @SuppressWarnings("unchecked")
 public void doWithRequest(ClientHttpRequest httpRequest)
 throws IOException {
 super.doWithRequest(httpRequest);
 if (!requestEntity.hasBody()) {
 HttpHeaders httpHeaders = httpRequest.getHeaders();
 HttpHeaders requestHeaders = requestEntity.getHeaders();
 if (!requestHeaders.isEmpty()) {
 httpHeaders.putAll(requestHeaders);
 }
 if (httpHeaders.getContentLength() == -1) {
 httpHeaders.setContentLength(0L);
 }
 } else {
 Object requestBody = requestEntity.getBody();
 Class<?> requestType = requestBody.getClass();
 HttpHeaders requestHeaders = requestEntity.getHeaders();
 MediaType requestContentType = requestHeaders.getContentType();
 for (HttpMessageConverter messageConverter : getMessageConverters()) {
 if (messageConverter.canWrite(requestType,
 requestContentType)) {
 if (!requestHeaders.isEmpty()) {
 httpRequest.getHeaders().putAll(requestHeaders);
 }
 if (logger.isDebugEnabled()) {
 if (requestContentType != null) {
 logger.debug("Writing [" + requestBody
 + "] as \"" + requestContentType
 + "\" using [" + messageConverter + "]");
 } else {
 logger.debug("Writing [" + requestBody
 + "] using [" + messageConverter + "]");
 }
}
 messageConverter.write(requestBody, requestContentType,
 httpRequest);
 return;
 }
 }
 String message = "Could not write request: no suitable HttpMessageConverter found for request type ["
 + requestType.getName() + "]";
 if (requestContentType != null) {
 message += " and content type [" + requestContentType + "]";
 }
 throw new RestClientException(message);
 }
 }
 }
/**
 * Response extractor for {@link HttpEntity}.
 */
 private class ResponseEntityResponseExtractor<T> implements
 ResponseExtractor<ResponseEntity<T>> {
private final HttpMessageConverterExtractor<T> delegate;
public ResponseEntityResponseExtractor(Class<T> responseType) {
 if (responseType != null && !Void.class.equals(responseType)) {
 this.delegate = new HttpMessageConverterExtractor<T>(
 responseType, getMessageConverters());
 } else {
 this.delegate = null;
 }
 }
public ResponseEntity<T> extractData(ClientHttpResponse response)
 throws IOException {
 if (delegate != null) {
 T body = delegate.extractData(response);
 return new ResponseEntity<T>(body, response.getHeaders(),
 response.getStatusCode());
 } else {
 return new ResponseEntity<T>(response.getHeaders(),
 response.getStatusCode());
 }
 }
 }
/**
 * Response extractor that extracts the response {@link HttpHeaders}.
 */
 private static class HeadersExtractor implements
 ResponseExtractor<HttpHeaders> {
public HttpHeaders extractData(ClientHttpResponse response)
 throws IOException {
 return response.getHeaders();
 }
 }
/**
 * HTTP-specific subclass of UriTemplate, overriding the encode method.
 */
 private static class HttpUrlTemplate extends UriTemplate {
public HttpUrlTemplate(String uriTemplate) {
 super(uriTemplate);
 }
@Override
 protected URI encodeUri(String uri) {
 try {
 String encoded = UriUtils.encodeHttpUrl(uri, "UTF-8");
 return new URI(encoded);
 } catch (UnsupportedEncodingException ex) {
 // should not happen, UTF-8 is always supported
 throw new IllegalStateException(ex);
 } catch (URISyntaxException ex) {
 throw new IllegalArgumentException(
 "Could not create HTTP URL from [" + uri + "]: " + ex,
 ex);
 }
 }
 }
}

The resttemplate is used to request gzip compression/decompression but does not perform the actual work. To gzip/ungzip we need to add a gzip enabled http message converter to the registered ones (see constructor).

And here is the converter:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.List;
import java.util.zip.GZIPOutputStream;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.Assert;
import com.g2mobility.common.core.dto.G2JaxbContext;
import com.g2mobility.common.utilities.GZipStreamSource;
public class GzipEnabledClientSideMarshallingHttpMessageConverter<T> extends
 AbstractHttpMessageConverter<T> {
private final TransformerFactory transformerFactory = TransformerFactory
 .newInstance();
 G2JaxbContext _jaxbContext = G2JaxbContext.getInstance();
private javax.xml.bind.Marshaller marshaller;
 private javax.xml.bind.Unmarshaller unmarshaller;
public GzipEnabledClientSideMarshallingHttpMessageConverter() {
 super(new MediaType("application", "xml"),
 new MediaType("text", "xml"), new MediaType("application",
 "*+xml"));
 try {
 this.marshaller = _jaxbContext.getContext().createMarshaller();
 this.marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
 this.unmarshaller = _jaxbContext.getContext().createUnmarshaller();
 } catch (JAXBException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }
 }
/**
 * Here we need to convert from gzip to normal.
 */
 @Override
 public final T readInternal(Class<? extends T> clazz,
 HttpInputMessage inputMessage) throws IOException {
StreamSource streamSource = null;
 boolean gzip = false;
 HttpHeaders headers = inputMessage.getHeaders();
 List<String> contentEncoding = headers.get("Content-Encoding");
/**
 * If the header specifies that the response body is gzipped, ungzip it.
 */
 if (headers != null && contentEncoding != null) {
 for (String contentEncodingString : contentEncoding) {
 if (contentEncodingString != null
 && "gzip"
 .equalsIgnoreCase(contentEncodingString.trim())) {
 gzip = true;
 }
 }
 }
if (gzip) {
 streamSource = new GZipStreamSource(inputMessage.getBody());
 } else {
 streamSource = new StreamSource(inputMessage.getBody());
 }
return (T) readFromSource(clazz, inputMessage.getHeaders(),
 streamSource);
 }
@Override
 protected final void writeInternal(T t, HttpOutputMessage outputMessage)
 throws IOException {
writeToResult(t, outputMessage.getHeaders(), new StreamResult(
 outputMessage.getBody()));
 }
/**
 * Set the {@link Marshaller} to be used by this message converter.
 */
 public void setMarshaller(Marshaller marshaller) {
 this.marshaller = marshaller;
 }
/**
 * Set the {@link Unmarshaller} to be used by this message converter.
 */
 public void setUnmarshaller(Unmarshaller unmarshaller) {
 this.unmarshaller = unmarshaller;
 }
@Override
 public boolean supports(Class<?> clazz) {
 return true;
 }
/**
 * 
 * @param clazz
 * @param headers
 * @param source
 * @return
 * @throws IOException
 */
 protected Object readFromSource(Class<?> clazz, HttpHeaders headers,
 Source source) throws IOException {
 Assert.notNull(this.unmarshaller, "Property 'unmarshaller' is required");
 try {
 String charset = "UTF-8"; // You should determine it based on
 // response header.
InputStream unzipped = ((StreamSource) source).getInputStream();
InputStreamReader reader = new InputStreamReader(unzipped);
 BufferedReader in = new BufferedReader(reader);
/**
 * Remove the unwanted trailing on the result.
 */
String readed;
 String trimmed = null;
 while ((readed = in.readLine()) != null) {
 trimmed = readed.trim();
 trimmed = trimmed.substring(0, trimmed.length() - 2);
 System.out.println(trimmed);
 }
Reader strReader = new StringReader(trimmed);
 return this.unmarshaller.unmarshal(strReader);
 } catch (JAXBException e) {
 throw new HttpMessageNotReadableException("Could not read ["
 + clazz + "]", e);
 }
 }
/**
 * 
 * @param o
 * @param headers
 * @param result
 * @throws IOException
 */
 protected void writeToResult(Object o, HttpHeaders headers, Result result)
 throws IOException {
 Assert.notNull(this.marshaller, "Property 'marshaller' is required");
 try {
 boolean needGzipCompression = false;
List<String> contentEncoding = headers.get("Content-Encoding");
 List<String> contentType = headers.get("Content-Type");
if (headers != null && contentEncoding != null) {
 for (String contentEncodingString : contentEncoding) {
 if (contentEncodingString != null
 && "gzip".equalsIgnoreCase(contentEncodingString
 .trim())) {
 needGzipCompression = true;
 }
 }
 }
if (needGzipCompression) {
 StreamResult streamResult = (StreamResult) result;
 if (headers != null && contentType != null) {
 for (String contentTypeString : contentType) {
 if (contentTypeString != null
 && "application/xml"
 .equalsIgnoreCase(contentTypeString
 .trim())) {
 headers.set("Content-Type",
 "application/xml; charset=UTF-8");
 }
 }
 }
 GZIPOutputStream gzipOutputStream = new GZIPOutputStream(
 streamResult.getOutputStream());
 streamResult.setOutputStream(gzipOutputStream);
 this.marshaller.marshal(o, streamResult);
 gzipOutputStream.finish();
 // streamResult.getOutputStream().close();
 } else {
 this.marshaller.marshal(o, result);
 }
 } catch (JAXBException e) {
 throw new HttpMessageNotWritableException("Could not write [" + o
 + "]", e);
 }
}
}

The G2JAxbContext object is a custom object and the method getContext() returns a static JaxbContext, so just override this with your defaultjaxb marshaller context.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s