001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source 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, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.server.connection;
031:
032: import com.caucho.log.Log;
033: import com.caucho.util.L10N;
034: import com.caucho.vfs.Encoding;
035: import com.caucho.vfs.TempBuffer;
036: import com.caucho.vfs.i18n.EncodingWriter;
037:
038: import java.io.IOException;
039: import java.io.UnsupportedEncodingException;
040: import java.util.Locale;
041: import java.util.logging.Logger;
042:
043: /**
044: * Handles the dual char/byte buffering for the response stream.
045: */
046: public abstract class ToByteResponseStream extends
047: AbstractResponseStream {
048: private static final Logger log = Log
049: .open(ToByteResponseStream.class);
050: private static final L10N L = new L10N(ToByteResponseStream.class);
051:
052: protected static final int SIZE = TempBuffer.SIZE;
053:
054: private final char[] _charBuffer = new char[SIZE];
055: private int _charLength;
056:
057: // head of the expandable buffer
058: private TempBuffer _head = TempBuffer.allocate();
059: private TempBuffer _tail;
060:
061: private byte[] _byteBuffer;
062: private int _byteLength;
063:
064: private int _bufferCapacity;
065: private int _bufferSize;
066:
067: private boolean _isOutputStreamOnly;
068:
069: private boolean _isHead;
070: private boolean _isClosed;
071: protected boolean _isFinished;
072:
073: private EncodingWriter _toByte = Encoding.getLatin1Writer();
074:
075: protected ToByteResponseStream() {
076: }
077:
078: /**
079: * Initializes the Buffered Response stream at the beginning of a request.
080: */
081: @Override
082: public void start() {
083: _bufferCapacity = SIZE;
084:
085: _charLength = 0;
086:
087: _head.clear();
088: _tail = _head;
089: _byteBuffer = _tail.getBuffer();
090: _byteLength = 0;
091:
092: _bufferSize = 0;
093:
094: _isHead = false;
095: _isClosed = false;
096: _isFinished = false;
097: _isOutputStreamOnly = false;
098:
099: _toByte = Encoding.getLatin1Writer();
100: }
101:
102: /**
103: * Returns true for a caucho response stream.
104: */
105: public boolean isCauchoResponseStream() {
106: return true;
107: }
108:
109: @Override
110: public void setOutputStreamOnly(boolean isOutputStreamOnly) {
111: _isOutputStreamOnly = isOutputStreamOnly;
112: }
113:
114: /**
115: * Sets the head.
116: */
117: @Override
118: public void setHead() {
119: _isHead = true;
120: }
121:
122: /**
123: * Sets the encoding.
124: */
125: @Override
126: public void setEncoding(String encoding)
127: throws UnsupportedEncodingException {
128: EncodingWriter toByte;
129:
130: if (encoding == null)
131: toByte = Encoding.getLatin1Writer();
132: else
133: toByte = Encoding.getWriteEncoding(encoding);
134:
135: if (toByte != null)
136: _toByte = toByte;
137: else {
138: _toByte = Encoding.getLatin1Writer();
139:
140: throw new UnsupportedEncodingException(encoding);
141: }
142: }
143:
144: /**
145: * Sets the locale.
146: */
147: @Override
148: public void setLocale(Locale locale)
149: throws UnsupportedEncodingException {
150: }
151:
152: /**
153: * Returns the char buffer.
154: */
155: public char[] getCharBuffer() {
156: return _charBuffer;
157: }
158:
159: /**
160: * Returns the char offset.
161: */
162: public int getCharOffset() throws IOException {
163: return _charLength;
164: }
165:
166: /**
167: * Sets the char offset.
168: */
169: public void setCharOffset(int offset) throws IOException {
170: _charLength = offset;
171:
172: if (_charLength == SIZE)
173: flushCharBuffer();
174: }
175:
176: /**
177: * Returns the byte buffer.
178: */
179: public byte[] getBuffer() throws IOException {
180: return _byteBuffer;
181: }
182:
183: /**
184: * Returns the byte offset.
185: */
186: public int getBufferOffset() throws IOException {
187: return _byteLength;
188: }
189:
190: /**
191: * Sets the byte offset.
192: */
193: public void setBufferOffset(int offset) throws IOException {
194: if (_charLength > 0)
195: flushCharBuffer();
196:
197: _byteLength = offset;
198: }
199:
200: /**
201: * Returns the buffer capacity.
202: */
203: public int getBufferSize() {
204: return _bufferCapacity;
205: }
206:
207: /**
208: * Returns true for extended buffers.
209: */
210: /*
211: public boolean isExtendedBuffer()
212: {
213: return _bufferCapacity < SIZE;
214: }
215: */
216:
217: /**
218: * Sets the buffer capacity.
219: */
220: public void setBufferSize(int size) {
221: _bufferCapacity = SIZE * ((size + SIZE - 1) / SIZE);
222:
223: if (_bufferCapacity <= 0)
224: _bufferCapacity = 0;
225: }
226:
227: /**
228: * Returns the remaining value left.
229: */
230: public int getRemaining() {
231: return _bufferCapacity - getBufferLength();
232: }
233:
234: /**
235: * Returns the remaining value left.
236: */
237: protected int getBufferLength() {
238: return _bufferSize + _byteLength + _charLength;
239: }
240:
241: /**
242: * Clears the response buffer.
243: */
244: public void clearBuffer() {
245: TempBuffer next = _head.getNext();
246: if (next != null) {
247: _head.setNext(null);
248: TempBuffer.freeAll(next);
249: }
250: _head.clear();
251: _tail = _head;
252: _byteBuffer = _tail.getBuffer();
253: _byteLength = 0;
254:
255: _charLength = 0;
256:
257: _bufferSize = 0;
258: }
259:
260: /**
261: * Writes a character to the output.
262: */
263: public void write(int ch) throws IOException {
264: if (_isClosed)
265: return;
266: else if (_isHead) {
267: return;
268: }
269:
270: if (_charLength > 0)
271: flushCharBuffer();
272:
273: if (_bufferCapacity <= _bufferSize + _byteLength + 1) {
274: flushByteBuffer();
275: } else if (_byteLength == SIZE) {
276: _tail.setLength(_byteLength);
277: _bufferSize += _byteLength;
278:
279: TempBuffer tempBuf = TempBuffer.allocate();
280: _tail.setNext(tempBuf);
281: _tail = tempBuf;
282:
283: _byteBuffer = _tail.getBuffer();
284: _byteLength = 0;
285: }
286:
287: _byteBuffer[_byteLength++] = (byte) ch;
288: }
289:
290: /**
291: * Writes a chunk of bytes to the stream.
292: */
293: public void write(byte[] buffer, int offset, int length)
294: throws IOException {
295: boolean isFinished = false;
296:
297: if (_isClosed)
298: return;
299: else if (_isHead)
300: return;
301:
302: if (_charLength > 0)
303: flushCharBuffer();
304:
305: if (_bufferCapacity <= _bufferSize + _byteLength + length) {
306: if (_bufferSize + _byteLength > 0)
307: flushByteBuffer();
308:
309: if (_bufferCapacity <= length) {
310: _bufferSize = length;
311: writeNext(buffer, offset, length, isFinished);
312: _bufferSize = 0;
313: return;
314: }
315: }
316:
317: int byteLength = _byteLength;
318: while (length > 0) {
319: if (SIZE <= byteLength) {
320: _tail.setLength(byteLength);
321: _bufferSize += byteLength;
322:
323: TempBuffer tempBuf = TempBuffer.allocate();
324: _tail.setNext(tempBuf);
325: _tail = tempBuf;
326:
327: _byteBuffer = _tail.getBuffer();
328: byteLength = 0;
329: }
330:
331: int sublen = length;
332: if (SIZE - byteLength < sublen)
333: sublen = SIZE - byteLength;
334:
335: System.arraycopy(buffer, offset, _byteBuffer, byteLength,
336: sublen);
337:
338: offset += sublen;
339: length -= sublen;
340: byteLength += sublen;
341: }
342:
343: _byteLength = byteLength;
344: }
345:
346: /**
347: * Writes a character to the output.
348: */
349: public void print(int ch) throws IOException {
350: if (_isClosed)
351: return;
352: else if (_isHead)
353: return;
354:
355: _charBuffer[_charLength++] = (char) ch;
356:
357: if (_charLength == SIZE)
358: flushCharBuffer();
359: }
360:
361: /**
362: * Writes a char array to the output.
363: */
364: public void print(char[] buffer, int offset, int length)
365: throws IOException {
366: if (_isClosed)
367: return;
368: else if (_isHead)
369: return;
370:
371: int charLength = _charLength;
372:
373: while (length > 0) {
374: int sublen = SIZE - charLength;
375:
376: if (length < sublen)
377: sublen = length;
378:
379: System.arraycopy(buffer, offset, _charBuffer, charLength,
380: sublen);
381:
382: offset += sublen;
383: length -= sublen;
384: charLength += sublen;
385:
386: if (charLength == SIZE) {
387: _charLength = charLength;
388: charLength = 0;
389: flushCharBuffer();
390: }
391: }
392:
393: _charLength = charLength;
394: }
395:
396: /**
397: * Flushes the buffer.
398: */
399: public void flushBuffer() throws IOException {
400: if (_charLength > 0)
401: flushCharBuffer();
402:
403: flushByteBuffer();
404: }
405:
406: /**
407: * Flushes the buffered response to the output stream.
408: */
409: public void flush() throws IOException {
410: flushBuffer();
411: }
412:
413: /**
414: * Closes the response stream.
415: */
416: public void close() throws IOException {
417: flushBuffer();
418:
419: _isClosed = true;
420: }
421:
422: /**
423: * Converts the char buffer.
424: */
425: public char[] nextCharBuffer(int offset) throws IOException {
426: _charLength = offset;
427: flushCharBuffer();
428:
429: return _charBuffer;
430: }
431:
432: /**
433: * Converts the char buffer.
434: */
435: protected void flushCharBuffer() throws IOException {
436: int charLength = _charLength;
437: _charLength = 0;
438:
439: if (charLength > 0 && !_isOutputStreamOnly) {
440: _toByte.write(this , _charBuffer, 0, charLength);
441:
442: if (_bufferCapacity <= _byteLength + _bufferSize)
443: flushByteBuffer();
444: }
445: }
446:
447: /**
448: * Returns the next byte buffer.
449: */
450: public byte[] nextBuffer(int offset) throws IOException {
451: if (_byteLength + _bufferSize < _bufferCapacity) {
452: _tail.setLength(offset);
453: _bufferSize += offset;
454:
455: TempBuffer tempBuf = TempBuffer.allocate();
456: _tail.setNext(tempBuf);
457: _tail = tempBuf;
458:
459: _byteBuffer = _tail.getBuffer();
460: _byteLength = 0;
461: } else {
462: _byteLength = offset;
463: flushByteBuffer();
464: }
465:
466: return _byteBuffer;
467: }
468:
469: /**
470: * Flushes the buffered response to the output stream.
471: */
472: protected void flushByteBuffer() throws IOException {
473: _tail.setLength(_byteLength);
474: _bufferSize += _byteLength;
475: _byteLength = 0;
476:
477: TempBuffer ptr = _head;
478: do {
479: _head = ptr;
480:
481: TempBuffer next = ptr.getNext();
482: ptr.setNext(null);
483:
484: writeNext(ptr.getBuffer(), 0, ptr.getLength(), _isFinished);
485:
486: if (next != null) {
487: TempBuffer.free(ptr);
488: ptr = null;
489: }
490:
491: ptr = next;
492: } while (ptr != null);
493:
494: _tail = _head;
495: _byteBuffer = _tail.getBuffer();
496: _bufferSize = 0;
497: }
498:
499: /**
500: * Writes the chunk to the downward stream.
501: */
502: abstract protected void writeNext(byte[] buffer, int offset,
503: int length, boolean isEnd) throws IOException;
504:
505: /**
506: * Clears the close.
507: */
508: public void clearClose() {
509: _isClosed = false;
510: }
511: }
|