KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > coyote > tomcat4 > OutputBuffer


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.coyote.tomcat4;
18
19 import java.io.IOException JavaDoc;
20 import java.io.Writer JavaDoc;
21 import java.util.Hashtable JavaDoc;
22
23 import org.apache.catalina.connector.ClientAbortException;
24 import org.apache.coyote.ActionCode;
25 import org.apache.coyote.Response;
26 import org.apache.tomcat.util.buf.ByteChunk;
27 import org.apache.tomcat.util.buf.C2BConverter;
28 import org.apache.tomcat.util.buf.CharChunk;
29
30
31 /**
32  * The buffer used by Tomcat response. This is a derivative of the Tomcat 3.3
33  * OutputBuffer, with the removal of some of the state handling (which in
34  * Coyote is mostly the Processor's responsability).
35  *
36  * @author Costin Manolache
37  * @author Remy Maucherat
38  */

39 public class OutputBuffer extends Writer JavaDoc
40     implements ByteChunk.ByteOutputChannel, CharChunk.CharOutputChannel {
41
42
43     private static org.apache.commons.logging.Log log=
44         org.apache.commons.logging.LogFactory.getLog( OutputBuffer.class );
45
46     // -------------------------------------------------------------- Constants
47

48
49     public static final String JavaDoc DEFAULT_ENCODING =
50         org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
51     public static final int DEFAULT_BUFFER_SIZE = 8*1024;
52     static final int debug = 0;
53
54
55     // The buffer can be used for byte[] and char[] writing
56
// ( this is needed to support ServletOutputStream and for
57
// efficient implementations of templating systems )
58
public final int INITIAL_STATE = 0;
59     public final int CHAR_STATE = 1;
60     public final int BYTE_STATE = 2;
61
62
63     // ----------------------------------------------------- Instance Variables
64

65
66     /**
67      * The byte buffer.
68      */

69     private ByteChunk bb;
70
71
72     /**
73      * The chunk buffer.
74      */

75     private CharChunk cb;
76
77
78     /**
79      * State of the output buffer.
80      */

81     private int state = 0;
82
83
84     /**
85      * Number of bytes written.
86      */

87     private int bytesWritten = 0;
88
89
90     /**
91      * Number of chars written.
92      */

93     private int charsWritten = 0;
94
95
96     /**
97      * Flag which indicates if the output buffer is closed.
98      */

99     private boolean closed = false;
100
101
102     /**
103      * Do a flush on the next operation.
104      */

105     private boolean doFlush = false;
106
107
108     /**
109      * Byte chunk used to output bytes.
110      */

111     private ByteChunk outputChunk = new ByteChunk();
112
113
114     /**
115      * Encoding to use.
116      */

117     private String JavaDoc enc;
118
119
120     /**
121      * Encoder is set.
122      */

123     private boolean gotEnc = false;
124
125
126     /**
127      * List of encoders.
128      */

129     protected Hashtable JavaDoc encoders = new Hashtable JavaDoc();
130
131
132     /**
133      * Current char to byte converter.
134      */

135     protected C2BConverter conv;
136
137
138     /**
139      * Associated Coyote response.
140      */

141     private Response coyoteResponse;
142
143
144     /**
145      * Suspended flag. All output bytes will be swallowed if this is true.
146      */

147     private boolean suspended = false;
148
149
150     // ----------------------------------------------------------- Constructors
151

152
153     /**
154      * Default constructor. Allocate the buffer with the default buffer size.
155      */

156     public OutputBuffer() {
157
158         this(DEFAULT_BUFFER_SIZE);
159
160     }
161
162
163     /**
164      * Alternate constructor which allows specifying the initial buffer size.
165      *
166      * @param size Buffer size to use
167      */

168     public OutputBuffer(int size) {
169
170         bb = new ByteChunk(size);
171         bb.setLimit(size);
172         bb.setByteOutputChannel(this);
173         cb = new CharChunk(size);
174         cb.setCharOutputChannel(this);
175         cb.setLimit(size);
176
177     }
178
179
180     // ------------------------------------------------------------- Properties
181

182
183     /**
184      * Associated Coyote response.
185      *
186      * @param coyoteResponse Associated Coyote response
187      */

188     public void setResponse(Response coyoteResponse) {
189     this.coyoteResponse = coyoteResponse;
190     }
191
192
193     /**
194      * Get associated Coyote response.
195      *
196      * @return the associated Coyote response
197      */

198     public Response getResponse() {
199         return this.coyoteResponse;
200     }
201
202
203     /**
204      * Is the response output suspended ?
205      *
206      * @return suspended flag value
207      */

208     public boolean isSuspended() {
209         return this.suspended;
210     }
211
212
213     /**
214      * Set the suspended flag.
215      *
216      * @param suspended New suspended flag value
217      */

218     public void setSuspended(boolean suspended) {
219         this.suspended = suspended;
220     }
221
222
223     // --------------------------------------------------------- Public Methods
224

225
226     /**
227      * Recycle the output buffer.
228      */

229     public void recycle() {
230
231     if (debug > 0)
232             log("recycle()");
233
234     state = INITIAL_STATE;
235     bytesWritten = 0;
236     charsWritten = 0;
237
238         cb.recycle();
239         bb.recycle();
240         closed = false;
241         suspended = false;
242
243         if (conv!= null) {
244             conv.recycle();
245         }
246
247         gotEnc = false;
248         enc = null;
249
250     }
251
252
253     /**
254      * Close the output buffer. This tries to calculate the response size if
255      * the response has not been committed yet.
256      *
257      * @throws IOException An underlying IOException occurred
258      */

259     public void close()
260         throws IOException JavaDoc {
261
262         if (closed)
263             return;
264         if (suspended)
265             return;
266
267         if ((!coyoteResponse.isCommitted())
268             && (coyoteResponse.getContentLengthLong() == -1)) {
269             // Flushing the char buffer
270
if (state == CHAR_STATE) {
271                 cb.flushBuffer();
272                 state = BYTE_STATE;
273             }
274             // If this didn't cause a commit of the response, the final content
275
// length can be calculated
276
if (!coyoteResponse.isCommitted()) {
277                 coyoteResponse.setContentLength(bb.getLength());
278             }
279         }
280
281         doFlush(false);
282         closed = true;
283
284         coyoteResponse.finish();
285
286     }
287
288
289     /**
290      * Flush bytes or chars contained in the buffer.
291      *
292      * @throws IOException An underlying IOException occurred
293      */

294     public void flush()
295         throws IOException JavaDoc {
296         doFlush(true);
297     }
298
299
300     /**
301      * Flush bytes or chars contained in the buffer.
302      *
303      * @throws IOException An underlying IOException occurred
304      */

305     protected void doFlush(boolean realFlush)
306         throws IOException JavaDoc {
307
308         if (suspended)
309             return;
310
311         doFlush = true;
312         if (state == CHAR_STATE) {
313             cb.flushBuffer();
314             bb.flushBuffer();
315             state = BYTE_STATE;
316         } else if (state == BYTE_STATE) {
317             bb.flushBuffer();
318         } else if (state == INITIAL_STATE)
319             realWriteBytes(null, 0, 0); // nothing written yet
320
doFlush = false;
321
322         if (realFlush) {
323             coyoteResponse.action(ActionCode.ACTION_CLIENT_FLUSH,
324                                   coyoteResponse);
325             // If some exception occurred earlier, or if some IOE occurred
326
// here, notify the servlet with an IOE
327
if (coyoteResponse.isExceptionPresent()) {
328                 throw new ClientAbortException
329                     (coyoteResponse.getErrorException());
330             }
331         }
332
333     }
334
335
336     // ------------------------------------------------- Bytes Handling Methods
337

338
339     /**
340      * Sends the buffer data to the client output, checking the
341      * state of Response and calling the right interceptors.
342      *
343      * @param buf Byte buffer to be written to the response
344      * @param off Offset
345      * @param cnt Length
346      *
347      * @throws IOException An underlying IOException occurred
348      */

349     public void realWriteBytes(byte buf[], int off, int cnt)
350     throws IOException JavaDoc {
351
352         if (debug > 2)
353             log("realWrite(b, " + off + ", " + cnt + ") " + coyoteResponse);
354
355         if (closed)
356             return;
357         if (coyoteResponse == null)
358             return;
359
360         // If we really have something to write
361
if (cnt > 0) {
362             // real write to the adapter
363
outputChunk.setBytes(buf, off, cnt);
364             try {
365                 coyoteResponse.doWrite(outputChunk);
366             } catch (IOException JavaDoc e) {
367                 // An IOException on a write is almost always due to
368
// the remote client aborting the request. Wrap this
369
// so that it can be handled better by the error dispatcher.
370
throw new ClientAbortException(e);
371             }
372         }
373
374     }
375
376
377     public void write(byte b[], int off, int len) throws IOException JavaDoc {
378
379         if (suspended)
380             return;
381
382         if (state == CHAR_STATE)
383             cb.flushBuffer();
384         state = BYTE_STATE;
385         writeBytes(b, off, len);
386
387     }
388
389
390     private void writeBytes(byte b[], int off, int len)
391         throws IOException JavaDoc {
392
393         if (closed)
394             return;
395         if (debug > 0)
396             log("write(b,off,len)");
397
398         bb.append(b, off, len);
399         bytesWritten += len;
400
401         // if called from within flush(), then immediately flush
402
// remaining bytes
403
if (doFlush) {
404             bb.flushBuffer();
405         }
406
407     }
408
409
410     // XXX Char or byte ?
411
public void writeByte(int b)
412         throws IOException JavaDoc {
413
414         if (suspended)
415             return;
416
417         if (state == CHAR_STATE)
418             cb.flushBuffer();
419         state = BYTE_STATE;
420
421         if (debug > 0)
422             log("write(b)");
423
424         bb.append( (byte)b );
425         bytesWritten++;
426
427     }
428
429
430     // ------------------------------------------------- Chars Handling Methods
431

432
433     public void write(int c)
434         throws IOException JavaDoc {
435
436         if (suspended)
437             return;
438
439         state = CHAR_STATE;
440
441         if (debug > 0)
442             log("writeChar(b)");
443
444         cb.append((char) c);
445         charsWritten++;
446
447     }
448
449
450     public void write(char c[])
451         throws IOException JavaDoc {
452
453         if (suspended)
454             return;
455
456         write(c, 0, c.length);
457
458     }
459
460
461     public void write(char c[], int off, int len)
462         throws IOException JavaDoc {
463
464         if (suspended)
465             return;
466
467         state = CHAR_STATE;
468
469         if (debug > 0)
470             log("write(c,off,len)" + cb.getLength() + " " + cb.getLimit());
471
472         cb.append(c, off, len);
473         charsWritten += len;
474
475     }
476
477
478     public void write(StringBuffer JavaDoc sb)
479         throws IOException JavaDoc {
480
481         if (suspended)
482             return;
483
484         state = CHAR_STATE;
485
486         if (debug > 1)
487             log("write(s,off,len)");
488
489         int len = sb.length();
490         charsWritten += len;
491         cb.append(sb);
492
493     }
494
495
496     /**
497      * Append a string to the buffer
498      */

499     public void write(String JavaDoc s, int off, int len)
500         throws IOException JavaDoc {
501
502         if (suspended)
503             return;
504
505         state=CHAR_STATE;
506
507         if (debug > 1)
508             log("write(s,off,len)");
509
510         charsWritten += len;
511         if (s==null)
512             s="null";
513         cb.append( s, off, len );
514
515     }
516
517
518     public void write(String JavaDoc s)
519         throws IOException JavaDoc {
520
521         if (suspended)
522             return;
523
524         state = CHAR_STATE;
525         if (s==null)
526             s="null";
527         write(s, 0, s.length());
528
529     }
530
531
532     public void flushChars()
533         throws IOException JavaDoc {
534
535         if (debug > 0)
536             log("flushChars() " + cb.getLength());
537
538         cb.flushBuffer();
539         state = BYTE_STATE;
540
541     }
542
543
544     public boolean flushCharsNeeded() {
545         return state == CHAR_STATE;
546     }
547
548
549     public void setEncoding(String JavaDoc s) {
550         enc = s;
551     }
552
553
554     public void realWriteChars(char c[], int off, int len)
555         throws IOException JavaDoc {
556
557         if (debug > 0)
558             log("realWrite(c,o,l) " + cb.getOffset() + " " + len);
559
560         if (!gotEnc)
561             setConverter();
562
563         if (debug > 0)
564             log("encoder: " + conv + " " + gotEnc);
565
566         conv.convert(c, off, len);
567         conv.flushBuffer(); // ???
568

569     }
570
571
572     protected void setConverter() {
573
574         if (coyoteResponse != null)
575             enc = coyoteResponse.getCharacterEncoding();
576
577         if (debug > 0)
578             log("Got encoding: " + enc);
579
580         gotEnc = true;
581         if (enc == null)
582             enc = DEFAULT_ENCODING;
583         conv = (C2BConverter) encoders.get(enc);
584         if (conv == null) {
585             try {
586                 conv = new C2BConverter(bb, enc);
587                 encoders.put(enc, conv);
588             } catch (IOException JavaDoc e) {
589                 conv = (C2BConverter) encoders.get(DEFAULT_ENCODING);
590                 if (conv == null) {
591                     try {
592                         conv = new C2BConverter(bb, DEFAULT_ENCODING);
593                         encoders.put(DEFAULT_ENCODING, conv);
594                     } catch (IOException JavaDoc ex) {
595                         // Ignore
596
}
597                 }
598             }
599         }
600     }
601
602     
603     // -------------------- BufferedOutputStream compatibility
604

605
606     /**
607      * Real write - this buffer will be sent to the client
608      */

609     public void flushBytes()
610         throws IOException JavaDoc {
611
612         if (debug > 0)
613             log("flushBytes() " + bb.getLength());
614         bb.flushBuffer();
615
616     }
617
618
619     public int getBytesWritten() {
620         return bytesWritten;
621     }
622
623
624     public int getCharsWritten() {
625         return charsWritten;
626     }
627
628
629     public int getContentWritten() {
630         return bytesWritten + charsWritten;
631     }
632
633
634     /**
635      * True if this buffer hasn't been used ( since recycle() ) -
636      * i.e. no chars or bytes have been added to the buffer.
637      */

638     public boolean isNew() {
639         return (bytesWritten == 0) && (charsWritten == 0);
640     }
641
642
643     public void setBufferSize(int size) {
644         if (size > bb.getLimit()) {// ??????
645
bb.setLimit(size);
646     }
647     }
648
649
650     public void reset() {
651
652         //count=0;
653
bb.recycle();
654         bytesWritten = 0;
655         cb.recycle();
656         charsWritten = 0;
657         gotEnc = false;
658         enc = null;
659
660     }
661
662
663     public int getBufferSize() {
664     return bb.getLimit();
665     }
666
667
668
669     protected void log( String JavaDoc s ) {
670         if (log.isDebugEnabled())
671             log.debug("OutputBuffer: " + s);
672     }
673
674
675 }
676
Popular Tags