Java tutorial
/* * Copyright 2014 Jakub Jirutka <jakub@jirutka.cz>. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cz.jirutka.spring.http.client.cache.internal; import net.jcip.annotations.Immutable; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.client.ClientHttpResponse; import org.springframework.util.Assert; import java.io.*; /** * HttpResponseReader implementation that reads a response until the specified * size limit. */ @Immutable public class SizeLimitedHttpResponseReader implements HttpResponseReader { public static final int DEFAULT_BUFFER_SIZE = 2048; private static final double INITIAL_CAPACITY_FACTOR = 0.3; private final int maxBodySize; private final int bufferSize; /** * Construct with a {@link #DEFAULT_BUFFER_SIZE default buffer size}. * * @param maxBodySize The maximal size to read in bytes. It must be * greater then zero and should be multiple of the * {@link #DEFAULT_BUFFER_SIZE}. */ public SizeLimitedHttpResponseReader(int maxBodySize) { this(maxBodySize, DEFAULT_BUFFER_SIZE); } /** * @param maxBodySize The maximal size to read in bytes. It must be * greater then zero and should be multiple of the * {@code bufferSize}. * @param bufferSize The buffer size in bytes. It must be greater then zero. */ public SizeLimitedHttpResponseReader(int maxBodySize, int bufferSize) { Assert.isTrue(maxBodySize > 0, "bytesLimit must be greater then zero"); Assert.isTrue(maxBodySize > 0, "bufferSize must be greater then zero"); this.maxBodySize = maxBodySize; this.bufferSize = bufferSize; } /** * Reads the original {@link ClientHttpResponse} to memory, if possible, * and returns a serializable copy. If the response's body size exceeds the * specified {@code maxBodySize} limit, then it throws * {@link ResponseSizeLimitExceededException} with a reconstructed response * that combines an already read bytes in memory and the original response. * * @param response The original response to read. * @return An in-memory copy of the original response. * @throws ResponseSizeLimitExceededException When the response's body size * exceeds the specified {@code maxBodySize} limit. * @throws IOException */ public InMemoryClientHttpResponse readResponse(ClientHttpResponse response) throws ResponseSizeLimitExceededException, IOException { Assert.notNull(response, "response must not be null"); InputStream bodyStream = response.getBody(); ByteArrayOutputStream out = new ByteArrayOutputStream(initialCapacity()); long bytesTotal = 0; byte[] buffer = new byte[bufferSize]; int bytesRead = -1; while ((bytesRead = bodyStream.read(buffer)) != -1) { out.write(buffer, 0, bytesRead); bytesTotal += bytesRead; if (bytesTotal > maxBodySize - 1) { throw new ResponseSizeLimitExceededException(createCombinedResponse(response, out, bodyStream)); } } response.close(); return createInMemoryResponse(response, out); } private int initialCapacity() { return new Double(maxBodySize * INITIAL_CAPACITY_FACTOR).intValue(); } private CombinedClientHttpResponse createCombinedResponse(ClientHttpResponse originalResponse, ByteArrayOutputStream consumedBody, InputStream originalBody) { InputStream combinedBody = new SequenceInputStream(new ByteArrayInputStream(consumedBody.toByteArray()), originalBody); return new CombinedClientHttpResponse(originalResponse, combinedBody); } private InMemoryClientHttpResponse createInMemoryResponse(ClientHttpResponse originalResponse, ByteArrayOutputStream body) throws IOException { HttpStatus status = originalResponse.getStatusCode(); HttpHeaders headers = originalResponse.getHeaders(); return new InMemoryClientHttpResponse(body.toByteArray(), status, headers); } //////// Inner class //////// public static class ResponseSizeLimitExceededException extends IOException { private final ClientHttpResponse response; public ResponseSizeLimitExceededException(ClientHttpResponse response) { this.response = response; } /** * Returns a reconstructed response that can be read as usual. */ public ClientHttpResponse getResponse() { return response; } } }