diff --git a/spring-core/src/main/java/org/springframework/util/ResizableByteArrayOutputStream.java b/spring-core/src/main/java/org/springframework/util/ResizableByteArrayOutputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..727c55b86882ebea065c4f68883dccf0bbd6df7f --- /dev/null +++ b/spring-core/src/main/java/org/springframework/util/ResizableByteArrayOutputStream.java @@ -0,0 +1,155 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * 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 org.springframework.util; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * A variation of {@link java.io.ByteArrayOutputStream} that: + *
ByteArrayOutputStream
with the default buffer size of 32 bytes.
+ */
+ public ResizableByteArrayOutputStream() {
+ this(INITIAL_BUFFER_SIZE);
+ }
+
+ /**
+ * Create a new ByteArrayOutputStream
with a specified initial buffer size.
+ *
+ * @param size The initial buffer size in bytes
+ */
+ public ResizableByteArrayOutputStream(int size) {
+ buffer = new byte[size];
+ count = 0;
+ }
+
+ /**
+ * Return the size of the internal buffer.
+ */
+ public int size() {
+ return buffer.length;
+ }
+
+ /**
+ * Return the number of bytes that have been written to the buffer so far.
+ */
+ public int count() {
+ return count;
+ }
+
+ /**
+ * Discard all bytes written to the internal buffer by setting the count
variable to 0.
+ */
+ public void reset() {
+ count = 0;
+ }
+
+ /**
+ * Grow the internal buffer size
+ * @param add number of bytes to add to the current buffer size
+ * @see ResizableByteArrayOutputStream#size()
+ */
+ public void grow(int add) {
+ if (count + add > buffer.length) {
+ int newlen = Math.max(buffer.length * 2, count + add);
+ resize(newlen);
+ }
+ }
+
+ /**
+ * Resize the internal buffer size to a specified value
+ * @param size the size of the buffer
+ * @throws java.lang.IllegalArgumentException if the given size is
+ * smaller than the actual size of the content stored in the buffer
+ * @see ResizableByteArrayOutputStream#size()
+ */
+ public void resize(int size) {
+ Assert.isTrue(size >= count);
+
+ byte[] newbuf = new byte[size];
+ System.arraycopy(buffer, 0, newbuf, 0, count);
+ buffer = newbuf;
+ }
+
+ /**
+ * Write the specified byte into the internal buffer, thus incrementing the
+ * {{@link org.springframework.util.ResizableByteArrayOutputStream#count()}}
+ * @param oneByte the byte to be written in the buffer
+ * @see ResizableByteArrayOutputStream#count()
+ */
+ public void write(int oneByte) {
+ grow(1);
+ buffer[count++] = (byte) oneByte;
+ }
+
+ /**
+ * Write add
bytes from the passed in array
+ * inBuffer
starting at index offset
into the
+ * internal buffer.
+ *
+ * @param inBuffer The byte array to write data from
+ * @param offset The index into the buffer to start writing data from
+ * @param add The number of bytes to write
+ * @see ResizableByteArrayOutputStream#count()
+ */
+ public void write(byte[] inBuffer, int offset, int add) {
+ if (add >= 0) {
+ grow(add);
+ }
+ System.arraycopy(inBuffer, offset, buffer, count, add);
+ count += add;
+ }
+
+ /**
+ * Write all bytes that have been written to the specified OutputStream
.
+ *
+ * @param out The OutputStream
to write to
+ * @exception IOException If an error occurs
+ */
+ public void writeTo(OutputStream out) throws IOException {
+ out.write(buffer, 0, count);
+ }
+
+ /**
+ * Return a byte array containing the bytes that have been written to this stream so far.
+ */
+ public byte[] toByteArray() {
+ byte[] ret = new byte[count];
+ System.arraycopy(buffer, 0, ret, 0, count);
+ return ret;
+ }
+
+}
diff --git a/spring-core/src/test/java/org/springframework/util/ResizableByteArrayOutputStreamTests.java b/spring-core/src/test/java/org/springframework/util/ResizableByteArrayOutputStreamTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..e5ec17bb8495b986f58d456c71d63bcfb1e277af
--- /dev/null
+++ b/spring-core/src/test/java/org/springframework/util/ResizableByteArrayOutputStreamTests.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * 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 org.springframework.util;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Brian Clozel
+ */
+public class ResizableByteArrayOutputStreamTests {
+
+ private ResizableByteArrayOutputStream baos;
+
+ private String helloString;
+
+ private byte[] helloBytes;
+
+ private static final int INITIAL_SIZE = 32;
+
+ @Before
+ public void setUp() throws Exception {
+ this.baos = new ResizableByteArrayOutputStream(INITIAL_SIZE);
+ this.helloString = "Hello World";
+ this.helloBytes = helloString.getBytes("UTF-8");
+ }
+
+ @Test
+ public void resize() throws Exception {
+ assertEquals(INITIAL_SIZE, this.baos.buffer.length);
+ this.baos.write(helloBytes);
+ int size = 64;
+ this.baos.resize(size);
+ assertEquals(size, this.baos.buffer.length);
+ assertByteArrayEqualsString(helloString, this.baos);
+ }
+
+ @Test
+ public void autoGrow() {
+ assertEquals(INITIAL_SIZE, this.baos.buffer.length);
+ for(int i= 0; i < 33; i++) {
+ this.baos.write(0);
+ }
+ assertEquals(64, this.baos.buffer.length);
+ }
+
+ @Test
+ public void grow() throws Exception {
+ assertEquals(INITIAL_SIZE, this.baos.buffer.length);
+ this.baos.write(helloBytes);
+ this.baos.grow(100);
+ assertEquals(this.helloString.length() + 100, this.baos.buffer.length);
+ assertByteArrayEqualsString(helloString, this.baos);
+ }
+
+ @Test
+ public void write() throws Exception{
+ this.baos.write(helloBytes);
+ assertByteArrayEqualsString(helloString, this.baos);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void failResize() throws Exception{
+ this.baos.write(helloBytes);
+ this.baos.resize(5);
+ }
+
+ private void assertByteArrayEqualsString(String expected, ResizableByteArrayOutputStream actual) {
+ String actualString = new String(actual.buffer, 0, actual.count());
+ assertEquals(expected, actualString);
+ }
+
+}
diff --git a/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java b/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java
index 2414c1989e8502f9150052eb8ccb882ebf595ecd..1179f2a8421d74c2b968492f5c103560633d432d 100644
--- a/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java
+++ b/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java
@@ -16,7 +16,6 @@
package org.springframework.web.filter;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
@@ -30,6 +29,7 @@ import javax.servlet.http.HttpServletResponseWrapper;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
+import org.springframework.util.ResizableByteArrayOutputStream;
import org.springframework.util.DigestUtils;
import org.springframework.util.StreamUtils;
import org.springframework.web.util.WebUtils;
@@ -175,7 +175,7 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
*/
private static class ShallowEtagResponseWrapper extends HttpServletResponseWrapper {
- private final ByteArrayOutputStream content = new ByteArrayOutputStream();
+ private final ResizableByteArrayOutputStream content = new ResizableByteArrayOutputStream();
private final ServletOutputStream outputStream = new ResponseServletOutputStream();
@@ -214,6 +214,7 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
@Override
public void setContentLength(int len) {
+ this.content.resize(len);
}
@Override