You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by mr...@apache.org on 2008/01/25 14:31:18 UTC
svn commit: r615216 -
/struts/struts2/trunk/core/src/main/java/org/apache/struts2/util/FastByteArrayOutputStream.java
Author: mrdon
Date: Fri Jan 25 05:31:16 2008
New Revision: 615216
URL: http://svn.apache.org/viewvc?rev=615216&view=rev
Log:
Fixing problems with fast byte array output stream with large data
WW-2412
Modified:
struts/struts2/trunk/core/src/main/java/org/apache/struts2/util/FastByteArrayOutputStream.java
Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/util/FastByteArrayOutputStream.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/util/FastByteArrayOutputStream.java?rev=615216&r1=615215&r2=615216&view=diff
==============================================================================
--- struts/struts2/trunk/core/src/main/java/org/apache/struts2/util/FastByteArrayOutputStream.java (original)
+++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/util/FastByteArrayOutputStream.java Fri Jan 25 05:31:16 2008
@@ -20,12 +20,16 @@
*/
package org.apache.struts2.util;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.io.Writer;
+import javax.servlet.jsp.JspWriter;
+import java.io.*;
import java.util.Iterator;
import java.util.LinkedList;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.Charset;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.CoderResult;
+import java.nio.CharBuffer;
+import java.nio.ByteBuffer;
/**
@@ -35,186 +39,222 @@
*
*/
public class FastByteArrayOutputStream extends OutputStream {
-
- // Static --------------------------------------------------------
private static final int DEFAULT_BLOCK_SIZE = 8192;
-
- private LinkedList buffers;
-
- // Attributes ----------------------------------------------------
- // internal buffer
- private byte[] buffer;
-
- // is the stream closed?
- private boolean closed;
- private int blockSize;
+ private LinkedList<byte[]> buffers;
+ private byte buffer[];
private int index;
private int size;
+ private int blockSize;
+ private boolean closed;
-
- // Constructors --------------------------------------------------
public FastByteArrayOutputStream() {
this(DEFAULT_BLOCK_SIZE);
}
- public FastByteArrayOutputStream(int aSize) {
- blockSize = aSize;
- buffer = new byte[blockSize];
- }
-
-
- public int getSize() {
- return size + index;
+ public FastByteArrayOutputStream(int blockSize) {
+ buffer = new byte[this.blockSize = blockSize];
}
- public void close() {
- closed = true;
- }
-
- public byte[] toByteArray() {
- byte[] data = new byte[getSize()];
-
- // Check if we have a list of buffers
- int pos = 0;
-
+ public void writeTo(OutputStream out) throws IOException {
if (buffers != null) {
- Iterator iter = buffers.iterator();
-
- while (iter.hasNext()) {
- byte[] bytes = (byte[]) iter.next();
- System.arraycopy(bytes, 0, data, pos, blockSize);
- pos += blockSize;
+ for (byte[] bytes : buffers) {
+ out.write(bytes, 0, blockSize);
}
}
-
- // write the internal buffer directly
- System.arraycopy(buffer, 0, data, pos, index);
-
- return data;
- }
-
- public String toString() {
- return new String(toByteArray());
+ out.write(buffer, 0, index);
}
- // OutputStream overrides ----------------------------------------
- public void write(int datum) throws IOException {
- if (closed) {
- throw new IOException("Stream closed");
- } else {
- if (index == blockSize) {
- addBuffer();
+ public void writeTo(RandomAccessFile out) throws IOException {
+ if (buffers != null) {
+ for (byte[] bytes : buffers) {
+ out.write(bytes, 0, blockSize);
}
-
- // store the byte
- buffer[index++] = (byte) datum;
}
+ out.write(buffer, 0, index);
}
- public void write(byte[] data, int offset, int length) throws IOException {
- if (data == null) {
- throw new NullPointerException();
- } else if ((offset < 0) || ((offset + length) > data.length) || (length < 0)) {
- throw new IndexOutOfBoundsException();
- } else if (closed) {
- throw new IOException("Stream closed");
+ /**
+ * This is a patched method (added for common Writer, needed for tests)
+ * @param out Writer
+ * @param encoding Encoding
+ * @throws IOException If some output failed
+ */
+ public void writeTo(Writer out, String encoding) throws IOException {
+ if (encoding != null) {
+ CharsetDecoder decoder = getDecoder(encoding);
+ // Create buffer for characters decoding
+ CharBuffer charBuffer = CharBuffer.allocate(buffer.length);
+ // Create buffer for bytes
+ float bytesPerChar = decoder.charset().newEncoder().maxBytesPerChar();
+ ByteBuffer byteBuffer = ByteBuffer.allocate((int) (buffer.length + bytesPerChar));
+ if (buffers != null) {
+ for (byte[] bytes : buffers) {
+ decodeAndWriteOut(out, bytes, bytes.length, byteBuffer, charBuffer, decoder, false);
+ }
+ }
+ decodeAndWriteOut(out, buffer, index, byteBuffer, charBuffer, decoder, true);
} else {
- if ((index + length) > blockSize) {
- int copyLength;
-
- do {
- if (index == blockSize) {
- addBuffer();
- }
-
- copyLength = blockSize - index;
-
- if (length < copyLength) {
- copyLength = length;
- }
-
- System.arraycopy(data, offset, buffer, index, copyLength);
- offset += copyLength;
- index += copyLength;
- length -= copyLength;
- } while (length > 0);
- } else {
- // Copy in the subarray
- System.arraycopy(data, offset, buffer, index, length);
- index += length;
+ if (buffers != null) {
+ for (byte[] bytes : buffers) {
+ writeOut(out, bytes, bytes.length);
+ }
}
+ writeOut(out, buffer, index);
}
}
- // Public
- public void writeTo(OutputStream out) throws IOException {
- // Check if we have a list of buffers
- if (buffers != null) {
- Iterator iter = buffers.iterator();
+ private CharsetDecoder getDecoder(String encoding) {
+ Charset charset = Charset.forName(encoding);
+ return charset.newDecoder().
+ onMalformedInput(CodingErrorAction.REPORT).
+ onUnmappableCharacter(CodingErrorAction.REPLACE);
+ }
- while (iter.hasNext()) {
- byte[] bytes = (byte[]) iter.next();
- out.write(bytes, 0, blockSize);
- }
+ /**
+ * This is a patched method (standard)
+ * @param out Writer
+ * @param encoding Encoding
+ * @throws IOException If some output failed
+ */
+ public void writeTo(JspWriter out, String encoding) throws IOException {
+ try {
+ writeTo((Writer) out, encoding);
+ } catch (IOException e) {
+ writeToFile();
+ throw e;
+ } catch (Throwable e) {
+ writeToFile();
+ throw new RuntimeException(e);
}
-
- // write the internal buffer directly
- out.write(buffer, 0, index);
}
- public void writeTo(RandomAccessFile out) throws IOException {
- // Check if we have a list of buffers
- if (buffers != null) {
- Iterator iter = buffers.iterator();
-
- while (iter.hasNext()) {
- byte[] bytes = (byte[]) iter.next();
- out.write(bytes, 0, blockSize);
- }
+ /**
+ * This method is need only for debug. And needed for tests generated files.
+ */
+ private void writeToFile() {
+ try {
+ writeTo(new FileOutputStream("/tmp/" + getClass().getName() + System.currentTimeMillis() + ".log"));
+ } catch (IOException e) {
+ // Ignore
}
+ }
- // write the internal buffer directly
- out.write(buffer, 0, index);
+ private void writeOut(Writer out, byte[] bytes, int length) throws IOException {
+ out.write(new String(bytes, 0, length));
+ }
+
+ private static void decodeAndWriteOut(Writer writer, byte[] bytes, int length, ByteBuffer in, CharBuffer out, CharsetDecoder decoder, boolean endOfInput) throws IOException {
+ // Append bytes to current buffer
+ // Previous data maybe partially decoded, this part will appended to previous
+ in.put(bytes, 0, length);
+ // To begin of data
+ in.flip();
+ decodeAndWriteBuffered(writer, in, out, decoder, endOfInput);
+ }
+
+ private static void decodeAndWriteBuffered(Writer writer, ByteBuffer in, CharBuffer out, CharsetDecoder decoder, boolean endOfInput) throws IOException {
+ // Decode
+ CoderResult result;
+ do {
+ result = decodeAndWrite(writer, in, out, decoder, endOfInput);
+ // Check that all data are decoded
+ if (in.hasRemaining()) {
+ // Move remaining to top of buffer
+ in.compact();
+ if (result.isOverflow() && !result.isError() && !result.isMalformed()) {
+ // Not all buffer chars decoded, spin it again
+ // Set to begin
+ in.flip();
+ }
+ } else {
+ // Clean up buffer
+ in.clear();
+ }
+ } while (in.hasRemaining() && result.isOverflow() && !result.isError() && !result.isMalformed());
}
- public void writeTo(Writer out, String encoding) throws IOException {
- // Check if we have a list of buffers
- if (buffers != null) {
- Iterator iter = buffers.iterator();
+ private static CoderResult decodeAndWrite(Writer writer, ByteBuffer in, CharBuffer out, CharsetDecoder decoder, boolean endOfInput) throws IOException {
+ CoderResult result = decoder.decode(in, out, endOfInput);
+ // To begin of decoded data
+ out.flip();
+ // Output
+ writer.write(out.toString());
+ return result;
+ }
- while (iter.hasNext()) {
- byte[] bytes = (byte[]) iter.next();
+ public int getSize() {
+ return size + index;
+ }
- if (encoding != null) {
- out.write(new String(bytes, encoding));
- } else {
- out.write(new String(bytes));
- }
+ public byte[] toByteArray() {
+ byte data[] = new byte[getSize()];
+ int position = 0;
+ if (buffers != null) {
+ for (byte[] bytes : buffers) {
+ System.arraycopy(bytes, 0, data, position, blockSize);
+ position += blockSize;
}
}
+ System.arraycopy(buffer, 0, data, position, index);
+ return data;
+ }
- // write the internal buffer directly
- if (encoding != null) {
- out.write(new String(buffer, 0, index, encoding));
- } else {
- out.write(new String(buffer, 0, index));
- }
+ public String toString() {
+ return new String(toByteArray());
}
- /**
- * Create a new buffer and store the
- * current one in linked list
- */
protected void addBuffer() {
if (buffers == null) {
- buffers = new LinkedList();
+ buffers = new LinkedList<byte[]>();
}
-
buffers.addLast(buffer);
-
buffer = new byte[blockSize];
size += index;
index = 0;
}
-}
+
+ public void write(int datum) throws IOException {
+ if (closed) {
+ throw new IOException("Stream closed");
+ }
+ if (index == blockSize) {
+ addBuffer();
+ }
+ buffer[index++] = (byte) datum;
+ }
+
+ public void write(byte data[], int offset, int length) throws IOException {
+ if (data == null) {
+ throw new NullPointerException();
+ }
+ if (offset < 0 || offset + length > data.length || length < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (closed) {
+ throw new IOException("Stream closed");
+ }
+ if (index + length > blockSize) {
+ do {
+ if (index == blockSize) {
+ addBuffer();
+ }
+ int copyLength = blockSize - index;
+ if (length < copyLength) {
+ copyLength = length;
+ }
+ System.arraycopy(data, offset, buffer, index, copyLength);
+ offset += copyLength;
+ index += copyLength;
+ length -= copyLength;
+ } while (length > 0);
+ } else {
+ System.arraycopy(data, offset, buffer, index, length);
+ index += length;
+ }
+ }
+
+ public void close() {
+ closed = true;
+ }
+}
\ No newline at end of file