KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > catalina > connector > OutputBuffer


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

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

42 public class OutputBuffer extends Writer JavaDoc
43     implements ByteChunk.ByteOutputChannel {
44
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
53
54     // ----------------------------------------------------- Instance Variables
55

56
57     /**
58      * The byte buffer.
59      */

60     private ByteChunk bb;
61
62
63     /**
64      * State of the output buffer.
65      */

66     private boolean initial = true;
67
68
69     /**
70      * Number of bytes written.
71      */

72     private int bytesWritten = 0;
73
74
75     /**
76      * Number of chars written.
77      */

78     private int charsWritten = 0;
79
80
81     /**
82      * Flag which indicates if the output buffer is closed.
83      */

84     private boolean closed = false;
85
86
87     /**
88      * Do a flush on the next operation.
89      */

90     private boolean doFlush = false;
91
92
93     /**
94      * Byte chunk used to output bytes.
95      */

96     private ByteChunk outputChunk = new ByteChunk();
97
98
99     /**
100      * Encoding to use.
101      */

102     private String JavaDoc enc;
103
104
105     /**
106      * Encoder is set.
107      */

108     private boolean gotEnc = false;
109
110
111     /**
112      * List of encoders.
113      */

114     protected HashMap JavaDoc encoders = new HashMap JavaDoc();
115
116
117     /**
118      * Current char to byte converter.
119      */

120     protected C2BConverter conv;
121
122
123     /**
124      * Associated Coyote response.
125      */

126     private Response coyoteResponse;
127
128
129     /**
130      * Suspended flag. All output bytes will be swallowed if this is true.
131      */

132     private boolean suspended = false;
133
134
135     // ----------------------------------------------------------- Constructors
136

137
138     /**
139      * Default constructor. Allocate the buffer with the default buffer size.
140      */

141     public OutputBuffer() {
142
143         this(DEFAULT_BUFFER_SIZE);
144
145     }
146
147
148     /**
149      * Alternate constructor which allows specifying the initial buffer size.
150      *
151      * @param size Buffer size to use
152      */

153     public OutputBuffer(int size) {
154
155         bb = new ByteChunk(size);
156         bb.setLimit(size);
157         bb.setByteOutputChannel(this);
158
159     }
160
161
162     // ------------------------------------------------------------- Properties
163

164
165     /**
166      * Associated Coyote response.
167      *
168      * @param coyoteResponse Associated Coyote response
169      */

170     public void setResponse(Response coyoteResponse) {
171     this.coyoteResponse = coyoteResponse;
172     }
173
174
175     /**
176      * Get associated Coyote response.
177      *
178      * @return the associated Coyote response
179      */

180     public Response getResponse() {
181         return this.coyoteResponse;
182     }
183
184
185     /**
186      * Is the response output suspended ?
187      *
188      * @return suspended flag value
189      */

190     public boolean isSuspended() {
191         return this.suspended;
192     }
193
194
195     /**
196      * Set the suspended flag.
197      *
198      * @param suspended New suspended flag value
199      */

200     public void setSuspended(boolean suspended) {
201         this.suspended = suspended;
202     }
203
204
205     /**
206      * Is the response output closed ?
207      *
208      * @return closed flag value
209      */

210     public boolean isClosed() {
211         return this.closed;
212     }
213
214
215     // --------------------------------------------------------- Public Methods
216

217
218     /**
219      * Recycle the output buffer.
220      */

221     public void recycle() {
222         
223         initial = true;
224         bytesWritten = 0;
225         charsWritten = 0;
226         
227         bb.recycle();
228         closed = false;
229         suspended = false;
230         
231         if (conv!= null) {
232             conv.recycle();
233         }
234         
235         gotEnc = false;
236         enc = null;
237         
238     }
239
240
241     /**
242      * Clear cached encoders (to save memory for Comet requests).
243      */

244     public void clearEncoders() {
245         encoders.clear();
246     }
247     
248     
249     /**
250      * Close the output buffer. This tries to calculate the response size if
251      * the response has not been committed yet.
252      *
253      * @throws IOException An underlying IOException occurred
254      */

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

285     public void flush()
286         throws IOException JavaDoc {
287         doFlush(true);
288     }
289
290
291     /**
292      * Flush bytes or chars contained in the buffer.
293      *
294      * @throws IOException An underlying IOException occurred
295      */

296     protected void doFlush(boolean realFlush)
297         throws IOException JavaDoc {
298
299         if (suspended)
300             return;
301
302         doFlush = true;
303         if (initial) {
304             coyoteResponse.sendHeaders();
305             initial = false;
306         }
307         if (bb.getLength() > 0) {
308             bb.flushBuffer();
309         }
310         doFlush = false;
311
312         if (realFlush) {
313             coyoteResponse.action(ActionCode.ACTION_CLIENT_FLUSH,
314                                   coyoteResponse);
315             // If some exception occurred earlier, or if some IOE occurred
316
// here, notify the servlet with an IOE
317
if (coyoteResponse.isExceptionPresent()) {
318                 throw new ClientAbortException
319                     (coyoteResponse.getErrorException());
320             }
321         }
322
323     }
324
325
326     // ------------------------------------------------- Bytes Handling Methods
327

328
329     /**
330      * Sends the buffer data to the client output, checking the
331      * state of Response and calling the right interceptors.
332      *
333      * @param buf Byte buffer to be written to the response
334      * @param off Offset
335      * @param cnt Length
336      *
337      * @throws IOException An underlying IOException occurred
338      */

339     public void realWriteBytes(byte buf[], int off, int cnt)
340     throws IOException JavaDoc {
341
342         if (closed)
343             return;
344         if (coyoteResponse == null)
345             return;
346
347         // If we really have something to write
348
if (cnt > 0) {
349             // real write to the adapter
350
outputChunk.setBytes(buf, off, cnt);
351             try {
352                 coyoteResponse.doWrite(outputChunk);
353             } catch (IOException JavaDoc e) {
354                 // An IOException on a write is almost always due to
355
// the remote client aborting the request. Wrap this
356
// so that it can be handled better by the error dispatcher.
357
throw new ClientAbortException(e);
358             }
359         }
360
361     }
362
363
364     public void write(byte b[], int off, int len) throws IOException JavaDoc {
365
366         if (suspended)
367             return;
368
369         writeBytes(b, off, len);
370
371     }
372
373
374     private void writeBytes(byte b[], int off, int len)
375         throws IOException JavaDoc {
376
377         if (closed)
378             return;
379
380         bb.append(b, off, len);
381         bytesWritten += len;
382
383         // if called from within flush(), then immediately flush
384
// remaining bytes
385
if (doFlush) {
386             bb.flushBuffer();
387         }
388
389     }
390
391
392     public void writeByte(int b)
393         throws IOException JavaDoc {
394
395         if (suspended)
396             return;
397
398         bb.append((byte) b);
399         bytesWritten++;
400
401     }
402
403
404     // ------------------------------------------------- Chars Handling Methods
405

406
407     public void write(int c)
408         throws IOException JavaDoc {
409
410         if (suspended)
411             return;
412
413         conv.convert((char) c);
414         conv.flushBuffer();
415         charsWritten++;
416         
417     }
418
419
420     public void write(char c[])
421         throws IOException JavaDoc {
422
423         if (suspended)
424             return;
425
426         write(c, 0, c.length);
427
428     }
429
430
431     public void write(char c[], int off, int len)
432         throws IOException JavaDoc {
433
434         if (suspended)
435             return;
436
437         conv.convert(c, off, len);
438         conv.flushBuffer();
439         charsWritten += len;
440
441     }
442
443
444     /**
445      * Append a string to the buffer
446      */

447     public void write(String JavaDoc s, int off, int len)
448         throws IOException JavaDoc {
449
450         if (suspended)
451             return;
452
453         charsWritten += len;
454         if (s == null)
455             s = "null";
456         conv.convert(s, off, len);
457         conv.flushBuffer();
458
459     }
460
461
462     public void write(String JavaDoc s)
463         throws IOException JavaDoc {
464
465         if (suspended)
466             return;
467
468         if (s == null)
469             s = "null";
470         conv.convert(s);
471         conv.flushBuffer();
472
473     }
474
475
476     public void setEncoding(String JavaDoc s) {
477         enc = s;
478     }
479
480
481     public void checkConverter()
482         throws IOException JavaDoc {
483
484         if (!gotEnc)
485             setConverter();
486
487     }
488
489
490     protected void setConverter()
491         throws IOException JavaDoc {
492
493         if (coyoteResponse != null)
494             enc = coyoteResponse.getCharacterEncoding();
495
496         gotEnc = true;
497         if (enc == null)
498             enc = DEFAULT_ENCODING;
499         conv = (C2BConverter) encoders.get(enc);
500         if (conv == null) {
501             
502             if (System.getSecurityManager() != null){
503                 try{
504                     conv = (C2BConverter)AccessController.doPrivileged(
505                             new PrivilegedExceptionAction JavaDoc(){
506
507                                 public Object JavaDoc run() throws IOException JavaDoc{
508                                     return new C2BConverter(bb, enc);
509                                 }
510
511                             }
512                     );
513                 }catch(PrivilegedActionException JavaDoc ex){
514                     Exception JavaDoc e = ex.getException();
515                     if (e instanceof IOException JavaDoc)
516                         throw (IOException JavaDoc)e;
517                 }
518             } else {
519                 conv = new C2BConverter(bb, enc);
520             }
521             
522             encoders.put(enc, conv);
523
524         }
525     }
526
527     
528     // -------------------- BufferedOutputStream compatibility
529

530
531     /**
532      * Real write - this buffer will be sent to the client
533      */

534     public void flushBytes()
535         throws IOException JavaDoc {
536
537         bb.flushBuffer();
538
539     }
540
541
542     public int getBytesWritten() {
543         return bytesWritten;
544     }
545
546
547     public int getCharsWritten() {
548         return charsWritten;
549     }
550
551
552     public int getContentWritten() {
553         return bytesWritten + charsWritten;
554     }
555
556
557     /**
558      * True if this buffer hasn't been used ( since recycle() ) -
559      * i.e. no chars or bytes have been added to the buffer.
560      */

561     public boolean isNew() {
562         return (bytesWritten == 0) && (charsWritten == 0);
563     }
564
565
566     public void setBufferSize(int size) {
567         if (size > bb.getLimit()) {// ??????
568
bb.setLimit(size);
569         }
570     }
571
572
573     public void reset() {
574
575         bb.recycle();
576         bytesWritten = 0;
577         charsWritten = 0;
578         gotEnc = false;
579         enc = null;
580         initial = true;
581         
582     }
583
584
585     public int getBufferSize() {
586         return bb.getLimit();
587     }
588
589
590 }
591
Popular Tags