001: /*
002: License $Id: JoOutputStream.java,v 1.9 2003/10/14 00:27:22 hendriks73 Exp $
003:
004: Copyright (c) 2001-2005 tagtraum industries.
005:
006: LGPL
007: ====
008:
009: jo! is free software; you can redistribute it and/or
010: modify it under the terms of the GNU Lesser General Public
011: License as published by the Free Software Foundation; either
012: version 2.1 of the License, or (at your option) any later version.
013:
014: jo! is distributed in the hope that it will be useful,
015: but WITHOUT ANY WARRANTY; without even the implied warranty of
016: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: Lesser General Public License for more details.
018:
019: You should have received a copy of the GNU Lesser General Public
020: License along with this library; if not, write to the Free Software
021: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022:
023: For LGPL see <http://www.fsf.org/copyleft/lesser.txt>
024:
025:
026: Sun license
027: ===========
028:
029: This release contains software by Sun Microsystems. Therefore
030: the following conditions have to be met, too. They apply to the
031: files
032:
033: - lib/mail.jar
034: - lib/activation.jar
035: - lib/jsse.jar
036: - lib/jcert.jar
037: - lib/jaxp.jar
038: - lib/crimson.jar
039: - lib/servlet.jar
040: - lib/jnet.jar
041: - lib/jaas.jar
042: - lib/jaasmod.jar
043:
044: contained in this release.
045:
046: a. Licensee may not modify the Java Platform
047: Interface (JPI, identified as classes contained within the javax
048: package or any subpackages of the javax package), by creating additional
049: classes within the JPI or otherwise causing the addition to or modification
050: of the classes in the JPI. In the event that Licensee creates any
051: Java-related API and distribute such API to others for applet or
052: application development, you must promptly publish broadly, an accurate
053: specification for such API for free use by all developers of Java-based
054: software.
055:
056: b. Software is confidential copyrighted information of Sun and
057: title to all copies is retained by Sun and/or its licensors. Licensee
058: shall not modify, decompile, disassemble, decrypt, extract, or otherwise
059: reverse engineer Software. Software may not be leased, assigned, or
060: sublicensed, in whole or in part. Software is not designed or intended
061: for use in on-line control of aircraft, air traffic, aircraft navigation
062: or aircraft communications; or in the design, construction, operation or
063: maintenance of any nuclear facility. Licensee warrants that it will not
064: use or redistribute the Software for such purposes.
065:
066: c. Software is provided "AS IS," without a warranty
067: of any kind. ALL EXPRESS OR IMPLIED REPRESENTATIONS AND WARRANTIES,
068: INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
069: PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
070:
071: d. This License is effective until terminated. Licensee may
072: terminate this License at any time by destroying all copies of Software.
073: This License will terminate immediately without notice from Sun if Licensee
074: fails to comply with any provision of this License. Upon such termination,
075: Licensee must destroy all copies of Software.
076:
077: e. Software, including technical data, is subject to U.S.
078: export control laws, including the U.S. Export Administration Act and its
079: associated regulations, and may be subject to export or import regulations
080: in other countries. Licensee agrees to comply strictly with all such
081: regulations and acknowledges that it has the responsibility to obtain
082: licenses to export, re-export, or import Software. Software may not be
083: downloaded, or otherwise exported or re-exported (i) into, or to a national
084: or resident of, Cuba, Iraq, Iran, North Korea, Libya, Sudan, Syria or any
085: country to which the U.S. has embargoed goods; or (ii) to anyone on the
086: U.S. Treasury Department's list of Specially Designated Nations or the U.S.
087: Commerce Department's Table of Denial Orders.
088:
089:
090: Feedback
091: ========
092:
093: We encourage your feedback and suggestions and want to use your feedback to
094: improve the Software. Send all such feedback to:
095: <feedback@tagtraum.com>
096:
097: For more information on tagtraum industries and jo!
098: please see <http://www.tagtraum.com/>.
099:
100:
101: */
102: package com.tagtraum.jo;
103:
104: import com.tagtraum.framework.http.ChunkedOutputStream;
105:
106: import javax.servlet.ServletOutputStream;
107: import java.io.IOException;
108: import java.io.OutputStream;
109: import java.util.ResourceBundle;
110:
111: /**
112: * Implementation of <code>ServletOutputStreams</code>.
113: *
114: * @author <a href="mailto:hs@tagtraum.com">Hendrik Schreiber</a>
115: * @version 1.1beta1 $Id: JoOutputStream.java,v 1.9 2003/10/14 00:27:22 hendriks73 Exp $
116: * @see JoInputStream
117: * @see JoServletResponse
118: */
119: public class JoOutputStream extends ServletOutputStream {
120:
121: /**
122: * Source-Version
123: */
124: public static String vcid = "$Id: JoOutputStream.java,v 1.9 2003/10/14 00:27:22 hendriks73 Exp $";
125: private static ResourceBundle localStrings = ResourceBundle
126: .getBundle("com.tagtraum.jo.localStrings");
127: public static final byte[] CRLF_BYTES = new byte[] { '\r', '\n' };
128:
129: private JoServletResponse response;
130: private OutputStream outputStream;
131:
132: /** 4k write buffer */
133: private byte[] buffer4k;
134: /** 8k write buffer */
135: private byte[] buffer8k;
136: /** The actual write buffer. */
137: private byte[] buffer;
138:
139: private int bufferPos;
140: private int bytesWritten;
141:
142: private boolean dataWasWritten;
143: private boolean headersWritten;
144: private boolean closed;
145:
146: /**
147: * Constructor.
148: *
149: * @see #init(OutputStream, JoServletResponse)
150: */
151: public JoOutputStream() {
152: buffer4k = new byte[JoServletResponse.FOUR_KB];
153: buffer8k = new byte[JoServletResponse.EIGHT_KB];
154: }
155:
156: /**
157: * Initializes this stream. The stream is re-usable.
158: *
159: * @param out <code>OutputStream</code> to wrap
160: * @param response the associated response object
161: */
162: public void init(OutputStream out, JoServletResponse response) {
163: if (out == null) {
164: throw new NullPointerException();
165: }
166: this .outputStream = out;
167: this .bufferPos = 0;
168: this .buffer = null;
169: this .dataWasWritten = false;
170: this .bytesWritten = 0;
171: this .response = response;
172: this .headersWritten = false;
173: this .closed = false;
174: }
175:
176: /**
177: * Returns the 8k buffer associated with this stream.
178: */
179: public byte[] get8kBuffer() {
180: return buffer8k;
181: }
182:
183: /**
184: * Returns the number of bytes written.
185: */
186: public int getBytesWritten() {
187: return bytesWritten;
188: }
189:
190: /**
191: * Resets the number of bytes written.
192: */
193: public void resetBytesWritten() {
194: bytesWritten = 0;
195: }
196:
197: /**
198: * Sets the buffer size.
199: *
200: * @param size
201: */
202: public int setBufferSize(int size) {
203: if (dataWasWritten)
204: throw new IllegalStateException(localStrings
205: .getString("data_was_written"));
206: if (size <= 0) {
207: buffer = null;
208: } else if (size <= JoServletResponse.FOUR_KB) {
209: buffer = buffer4k;
210: } else if (size <= JoServletResponse.EIGHT_KB) {
211: buffer = buffer8k;
212: } else {
213: buffer = new byte[size];
214: }
215: return getBufferSize();
216: }
217:
218: /**
219: * Returns the size of the buffer.
220: */
221: public int getBufferSize() {
222: if (buffer == null)
223: return 0;
224: return buffer.length;
225: }
226:
227: /**
228: * Resets the stream, if it has not been commited yet.
229: *
230: * @exception IllegalStateException if the buffer has already been committed.
231: */
232: public void reset() {
233: if (headersWritten)
234: throw new IllegalStateException(localStrings
235: .getString("data_was_committed"));
236: bufferPos = 0;
237: }
238:
239: /**
240: * Flush the internal buffer
241: */
242: public void flushBuffer() throws IOException {
243: if (bufferPos > 0) {
244: if (!headersWritten)
245: writeHeaders();
246: outputStream.write(buffer, 0, bufferPos);
247: bytesWritten += bufferPos;
248: bufferPos = 0;
249: }
250: }
251:
252: /**
253: * Prints the string provided.
254: *
255: * @exception IOException if an I/O error has occurred
256: */
257: public void print(String s) throws IOException {
258: write(s.getBytes(JoServletResponse.DEFAULT_CHARENCODING));
259: }
260:
261: /**
262: * Prints a CRLF.
263: *
264: * @exception IOException if an I/O error has occurred
265: */
266: public void println() throws IOException {
267: write(CRLF_BYTES);
268: }
269:
270: /**
271: * Writes a char.
272: *
273: * @param c a char
274: * @see I_JoServletResponse
275: */
276: public void write(int c) throws IOException {
277: dataWasWritten = true;
278: if (buffer == null) {
279: if (!headersWritten)
280: writeHeaders();
281: outputStream.write(c);
282: bytesWritten++;
283: } else {
284: if (bufferPos >= buffer.length)
285: flushBuffer();
286: buffer[bufferPos] = (byte) c;
287: bufferPos++;
288: }
289: }
290:
291: /**
292: * Writes a bytearray.
293: *
294: * @param b byte array
295: * @param off Offset
296: * @param len number of bytes to write
297: * @see I_JoServletResponse#writeHeaders()
298: */
299: public void write(byte b[], int off, int len) throws IOException {
300: dataWasWritten = true;
301: if (buffer == null) {
302: if (!headersWritten)
303: writeHeaders();
304: outputStream.write(b, off, len);
305: bytesWritten += len;
306: } else {
307: if (len >= buffer.length) {
308: flushBuffer();
309: if (!headersWritten)
310: writeHeaders();
311: outputStream.write(b, off, len);
312: bytesWritten += len;
313: return;
314: }
315: if (len > buffer.length - bufferPos) {
316: flushBuffer();
317: }
318: System.arraycopy(b, off, buffer, bufferPos, len);
319: bufferPos += len;
320: }
321: }
322:
323: private void writeHeaders() throws IOException {
324: response.writeHeaders();
325: headersWritten = true;
326: }
327:
328: /**
329: * Writes a bytearray.
330: *
331: * @param b byte array
332: * @see I_JoServletResponse#writeHeaders()
333: */
334: public void write(byte b[]) throws IOException {
335: write(b, 0, b.length);
336: }
337:
338: /**
339: * Flushes the stream.
340: */
341: public void flush() throws IOException {
342: if (outputStream != null)
343: outputStream.flush();
344: }
345:
346: /**
347: * Closes the stream.
348: *
349: * @see ChunkedOutputStream#finish()
350: */
351: public void close() throws IOException {
352: if (closed)
353: throw new IOException("Stream is already closed.");
354: closed = true;
355: flushBuffer();
356: flush();
357: }
358:
359: /**
360: * Closes the stream. If the stream is chunked, it is finished, not closed.
361: * Contrary to close() this method also finishes the ChunkedOuputStream, if there is
362: * one.
363: *
364: * @see ChunkedOutputStream#finish()
365: */
366: void internalClose() throws IOException {
367: if (!closed)
368: flushBuffer();
369: closed = true;
370: if (outputStream instanceof ChunkedOutputStream) {
371: ((ChunkedOutputStream) outputStream).finish();
372: } else {
373: flush();
374: }
375: }
376: }
|