KickJava   Java API By Example, From Geeks To Geeks.

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


1
2
3 /*
4  * The contents of this file are subject to the terms
5  * of the Common Development and Distribution License
6  * (the "License"). You may not use this file except
7  * in compliance with the License.
8  *
9  * You can obtain a copy of the license at
10  * glassfish/bootstrap/legal/CDDLv1.0.txt or
11  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
12  * See the License for the specific language governing
13  * permissions and limitations under the License.
14  *
15  * When distributing Covered Code, include this CDDL
16  * HEADER in each file and include the License file at
17  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
18  * add the following below this CDDL HEADER, with the
19  * fields enclosed by brackets "[]" replaced with your
20  * own identifying information: Portions Copyright [yyyy]
21  * [name of copyright owner]
22  *
23  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24  *
25  * Portions Copyright Apache Software Foundation.
26  */

27
28 package org.apache.coyote.tomcat5;
29
30
31 import java.io.IOException JavaDoc;
32 import java.io.Writer JavaDoc;
33 import java.security.AccessController JavaDoc;
34 import java.security.PrivilegedActionException JavaDoc;
35 import java.security.PrivilegedExceptionAction JavaDoc;
36 import java.util.HashMap JavaDoc;
37
38 import org.apache.catalina.connector.ClientAbortException;
39 import org.apache.catalina.security.SecurityUtil;
40 import org.apache.coyote.ActionCode;
41 import org.apache.coyote.Response;
42 import org.apache.tomcat.util.buf.ByteChunk;
43 import org.apache.tomcat.util.buf.C2BConverter;
44 import org.apache.tomcat.util.buf.CharChunk;
45
46
47
48 /**
49  * The buffer used by Tomcat response. This is a derivative of the Tomcat 3.3
50  * OutputBuffer, with the removal of some of the state handling (which in
51  * Coyote is mostly the Processor's responsability).
52  *
53  * @author Costin Manolache
54  * @author Remy Maucherat
55  */

56 public class OutputBuffer extends Writer JavaDoc
57     implements ByteChunk.ByteOutputChannel, CharChunk.CharOutputChannel {
58
59
60     private static com.sun.org.apache.commons.logging.Log log=
61         com.sun.org.apache.commons.logging.LogFactory.getLog( OutputBuffer.class );
62
63     // -------------------------------------------------------------- Constants
64

65
66     public static final String JavaDoc DEFAULT_ENCODING =
67         org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
68     public static final int DEFAULT_BUFFER_SIZE = 8*1024;
69     static final int debug = 0;
70
71
72     // The buffer can be used for byte[] and char[] writing
73
// ( this is needed to support ServletOutputStream and for
74
// efficient implementations of templating systems )
75
public final int INITIAL_STATE = 0;
76     public final int CHAR_STATE = 1;
77     public final int BYTE_STATE = 2;
78
79
80     // ----------------------------------------------------- Instance Variables
81

82
83     /**
84      * The byte buffer.
85      */

86     private ByteChunk bb;
87
88
89     /**
90      * The chunk buffer.
91      */

92     private CharChunk cb;
93
94
95     /**
96      * State of the output buffer.
97      */

98     private int state = 0;
99
100
101     /**
102      * Number of bytes written.
103      */

104     private int bytesWritten = 0;
105
106
107     /**
108      * Number of chars written.
109      */

110     private int charsWritten = 0;
111
112
113     /**
114      * Flag which indicates if the output buffer is closed.
115      */

116     private boolean closed = false;
117
118
119     /**
120      * Do a flush on the next operation.
121      */

122     private boolean doFlush = false;
123
124
125     /**
126      * Byte chunk used to output bytes.
127      */

128     private ByteChunk outputChunk = new ByteChunk();
129
130
131     /**
132      * Encoding to use.
133      */

134     private String JavaDoc enc;
135
136
137     /**
138      * Encoder is set.
139      */

140     private boolean gotEnc = false;
141
142
143     /**
144      * List of encoders.
145      */

146     protected HashMap JavaDoc encoders = new HashMap JavaDoc();
147
148
149     /**
150      * Current char to byte converter.
151      */

152     protected C2BConverter conv;
153
154
155     /**
156      * Associated Coyote response.
157      */

158     private Response coyoteResponse;
159
160
161     /**
162      * Suspended flag. All output bytes will be swallowed if this is true.
163      */

164     private boolean suspended = false;
165
166
167     // ----------------------------------------------------------- Constructors
168

169
170     /**
171      * Default constructor. Allocate the buffer with the default buffer size.
172      */

173     public OutputBuffer() {
174
175         this(DEFAULT_BUFFER_SIZE);
176
177     }
178
179
180     // START S1AS8 4861933
181
public OutputBuffer(boolean chunkingDisabled) {
182         this(DEFAULT_BUFFER_SIZE, chunkingDisabled);
183     }
184     // END S1AS8 4861933
185

186
187     /**
188      * Alternate constructor which allows specifying the initial buffer size.
189      *
190      * @param size Buffer size to use
191      */

192     public OutputBuffer(int size) {
193         // START S1AS8 4861933
194
/*
195         bb = new ByteChunk(size);
196         bb.setLimit(size);
197         bb.setByteOutputChannel(this);
198         cb = new CharChunk(size);
199         cb.setCharOutputChannel(this);
200         cb.setLimit(size);
201         */

202         this(size, false);
203         // END S1AS8 4861933
204
}
205
206
207     // START S1AS8 4861933
208
public OutputBuffer(int size, boolean chunkingDisabled) {
209         bb = new ByteChunk(size);
210         if (!chunkingDisabled) {
211             bb.setLimit(size);
212         }
213         bb.setByteOutputChannel(this);
214         cb = new CharChunk(size);
215         cb.setCharOutputChannel(this);
216         if (!chunkingDisabled) {
217             cb.setLimit(size);
218         }
219     }
220     // END S1AS8 4861933
221

222
223     // ------------------------------------------------------------- Properties
224

225
226     /**
227      * Associated Coyote response.
228      *
229      * @param coyoteResponse Associated Coyote response
230      */

231     public void setResponse(Response coyoteResponse) {
232     this.coyoteResponse = coyoteResponse;
233     }
234
235
236     /**
237      * Get associated Coyote response.
238      *
239      * @return the associated Coyote response
240      */

241     public Response getResponse() {
242         return this.coyoteResponse;
243     }
244
245
246     /**
247      * Is the response output suspended ?
248      *
249      * @return suspended flag value
250      */

251     public boolean isSuspended() {
252         return this.suspended;
253     }
254
255
256     /**
257      * Set the suspended flag.
258      *
259      * @param suspended New suspended flag value
260      */

261     public void setSuspended(boolean suspended) {
262         this.suspended = suspended;
263     }
264
265
266     // --------------------------------------------------------- Public Methods
267

268
269     /**
270      * Recycle the output buffer.
271      */

272     public void recycle() {
273
274     if (log.isDebugEnabled())
275             log.debug("recycle()");
276
277     state = INITIAL_STATE;
278     bytesWritten = 0;
279     charsWritten = 0;
280
281         cb.recycle();
282         bb.recycle();
283         closed = false;
284         suspended = false;
285
286         if (conv!= null) {
287             conv.recycle();
288         }
289
290         gotEnc = false;
291         enc = null;
292     }
293
294
295     /**
296      * Close the output buffer. This tries to calculate the response size if
297      * the response has not been committed yet.
298      *
299      * @throws IOException An underlying IOException occurred
300      */

301     public void close()
302         throws IOException JavaDoc {
303
304         if (closed)
305             return;
306         if (suspended)
307             return;
308
309         if ((!coyoteResponse.isCommitted())
310             && (coyoteResponse.getContentLength() == -1)) {
311             // Flushing the char buffer
312
if (state == CHAR_STATE) {
313                 cb.flushBuffer();
314                 state = BYTE_STATE;
315             }
316             // If this didn't cause a commit of the response, the final content
317
// length can be calculated
318
if (!coyoteResponse.isCommitted()) {
319                 coyoteResponse.setContentLength(bb.getLength());
320             }
321         }
322
323         doFlush(false);
324         closed = true;
325
326         coyoteResponse.finish();
327
328     }
329
330
331     /**
332      * Flush bytes or chars contained in the buffer.
333      *
334      * @throws IOException An underlying IOException occurred
335      */

336     public void flush()
337         throws IOException JavaDoc {
338         doFlush(true);
339     }
340
341
342     /**
343      * Flush bytes or chars contained in the buffer.
344      *
345      * @throws IOException An underlying IOException occurred
346      */

347     protected void doFlush(boolean realFlush)
348         throws IOException JavaDoc {
349
350         if (suspended)
351             return;
352
353         doFlush = true;
354         if (state == CHAR_STATE) {
355             cb.flushBuffer();
356             bb.flushBuffer();
357             state = BYTE_STATE;
358         } else if (state == BYTE_STATE) {
359             bb.flushBuffer();
360         } else if (state == INITIAL_STATE) {
361             // If the buffers are empty, commit the response header
362
coyoteResponse.sendHeaders();
363         }
364         doFlush = false;
365
366         if (realFlush) {
367             coyoteResponse.action(ActionCode.ACTION_CLIENT_FLUSH,
368                                   coyoteResponse);
369             // If some exception occurred earlier, or if some IOE occurred
370
// here, notify the servlet with an IOE
371
if (coyoteResponse.isExceptionPresent()) {
372                 throw new ClientAbortException
373                     (coyoteResponse.getErrorException());
374             }
375         }
376
377     }
378
379
380     // ------------------------------------------------- Bytes Handling Methods
381

382
383     /**
384      * Sends the buffer data to the client output, checking the
385      * state of Response and calling the right interceptors.
386      *
387      * @param buf Byte buffer to be written to the response
388      * @param off Offset
389      * @param cnt Length
390      *
391      * @throws IOException An underlying IOException occurred
392      */

393     public void realWriteBytes(byte buf[], int off, int cnt)
394     throws IOException JavaDoc {
395
396         if (log.isDebugEnabled())
397             log.debug("realWrite(b, " + off + ", " + cnt + ") " + coyoteResponse);
398
399         if (closed)
400             return;
401         if (coyoteResponse == null)
402             return;
403
404         // If we really have something to write
405
if (cnt > 0) {
406             // real write to the adapter
407
outputChunk.setBytes(buf, off, cnt);
408             try {
409                 coyoteResponse.doWrite(outputChunk);
410             } catch (IOException JavaDoc e) {
411                 // An IOException on a write is almost always due to
412
// the remote client aborting the request. Wrap this
413
// so that it can be handled better by the error dispatcher.
414
throw new ClientAbortException(e);
415             }
416         }
417
418     }
419
420
421     public void write(byte b[], int off, int len) throws IOException JavaDoc {
422
423         if (suspended)
424             return;
425
426         if (state == CHAR_STATE)
427             cb.flushBuffer();
428         state = BYTE_STATE;
429         writeBytes(b, off, len);
430
431     }
432
433
434     private void writeBytes(byte b[], int off, int len)
435         throws IOException JavaDoc {
436
437         if (closed)
438             return;
439         if (log.isDebugEnabled())
440             log.debug("write(b,off,len)");
441
442         bb.append(b, off, len);
443         bytesWritten += len;
444
445         // if called from within flush(), then immediately flush
446
// remaining bytes
447
if (doFlush) {
448             bb.flushBuffer();
449         }
450
451     }
452
453
454     // XXX Char or byte ?
455
public void writeByte(int b)
456         throws IOException JavaDoc {
457
458         if (suspended)
459             return;
460
461         if (state == CHAR_STATE)
462             cb.flushBuffer();
463         state = BYTE_STATE;
464
465         if (log.isDebugEnabled())
466             log.debug("write(b)");
467
468         bb.append( (byte)b );
469         bytesWritten++;
470
471     }
472
473
474     // ------------------------------------------------- Chars Handling Methods
475

476
477     public void write(int c)
478         throws IOException JavaDoc {
479
480         if (suspended)
481             return;
482
483         state = CHAR_STATE;
484
485         if (log.isDebugEnabled())
486             log.debug("writeChar(b)");
487
488         cb.append((char) c);
489         charsWritten++;
490
491     }
492
493
494     public void write(char c[])
495         throws IOException JavaDoc {
496
497         if (suspended)
498             return;
499
500         write(c, 0, c.length);
501
502     }
503
504
505     public void write(char c[], int off, int len)
506         throws IOException JavaDoc {
507
508         if (suspended)
509             return;
510
511         state = CHAR_STATE;
512
513         if (log.isDebugEnabled())
514             log.debug("write(c,off,len)" + cb.getLength() + " " + cb.getLimit());
515
516         cb.append(c, off, len);
517         charsWritten += len;
518
519     }
520
521
522     public void write(StringBuffer JavaDoc sb)
523         throws IOException JavaDoc {
524
525         if (suspended)
526             return;
527
528         state = CHAR_STATE;
529
530         if (log.isDebugEnabled())
531             log.debug("write(s,off,len)");
532
533         int len = sb.length();
534         charsWritten += len;
535         cb.append(sb);
536
537     }
538
539
540     /**
541      * Append a string to the buffer
542      */

543     public void write(String JavaDoc s, int off, int len)
544         throws IOException JavaDoc {
545
546         if (suspended)
547             return;
548
549         state=CHAR_STATE;
550
551         if (log.isDebugEnabled())
552             log.debug("write(s,off,len)");
553
554         charsWritten += len;
555         if (s==null)
556             s="null";
557         cb.append( s, off, len );
558
559     }
560
561
562     public void write(String JavaDoc s)
563         throws IOException JavaDoc {
564
565         if (suspended)
566             return;
567
568         state = CHAR_STATE;
569         if (s==null)
570             s="null";
571         write(s, 0, s.length());
572
573     }
574
575
576     public void flushChars()
577         throws IOException JavaDoc {
578
579         if (log.isDebugEnabled())
580             log.debug("flushChars() " + cb.getLength());
581
582         cb.flushBuffer();
583         state = BYTE_STATE;
584
585     }
586
587
588     public boolean flushCharsNeeded() {
589         return state == CHAR_STATE;
590     }
591
592
593     public void setEncoding(String JavaDoc s) {
594         enc = s;
595     }
596
597
598     public void realWriteChars(char c[], int off, int len)
599         throws IOException JavaDoc {
600
601         if (log.isDebugEnabled())
602             log.debug("realWrite(c,o,l) " + cb.getOffset() + " " + len);
603
604         if (!gotEnc)
605             setConverter();
606
607         if (log.isDebugEnabled())
608             log.debug("encoder: " + conv + " " + gotEnc);
609
610         conv.convert(c, off, len);
611         conv.flushBuffer(); // ???
612

613     }
614
615
616     public void checkConverter()
617         throws IOException JavaDoc {
618
619         if (!gotEnc)
620             setConverter();
621
622     }
623
624
625     protected void setConverter()
626         throws IOException JavaDoc {
627
628         if (coyoteResponse != null)
629             enc = coyoteResponse.getCharacterEncoding();
630
631         if (log.isDebugEnabled())
632             log.debug("Got encoding: " + enc);
633
634         gotEnc = true;
635         if (enc == null)
636             enc = DEFAULT_ENCODING;
637         conv = (C2BConverter) encoders.get(enc);
638         if (conv == null) {
639             if (System.getSecurityManager() != null){
640                 try{
641                     conv = (C2BConverter)AccessController.doPrivileged(
642                             new PrivilegedExceptionAction JavaDoc(){
643
644                                 public Object JavaDoc run() throws IOException JavaDoc{
645                                     return new C2BConverter(bb, enc);
646                                 }
647
648                             }
649                     );
650                 }catch(PrivilegedActionException JavaDoc ex){
651                     Exception JavaDoc e = ex.getException();
652                     if (e instanceof IOException JavaDoc)
653                         throw (IOException JavaDoc)e;
654                     
655                     if (log.isDebugEnabled())
656                         log.debug("setConverter: " + ex.getMessage());
657                 }
658             } else {
659                 conv = new C2BConverter(bb, enc);
660             }
661             encoders.put(enc, conv);
662
663         }
664     }
665
666     
667     // -------------------- BufferedOutputStream compatibility
668

669
670     /**
671      * Real write - this buffer will be sent to the client
672      */

673     public void flushBytes()
674         throws IOException JavaDoc {
675
676         if (log.isDebugEnabled())
677             log.debug("flushBytes() " + bb.getLength());
678         bb.flushBuffer();
679
680     }
681
682
683     public int getBytesWritten() {
684         return bytesWritten;
685     }
686
687
688     public int getCharsWritten() {
689         return charsWritten;
690     }
691
692
693     public int getContentWritten() {
694         return bytesWritten + charsWritten;
695     }
696
697
698     /**
699      * True if this buffer hasn't been used ( since recycle() ) -
700      * i.e. no chars or bytes have been added to the buffer.
701      */

702     public boolean isNew() {
703         return (bytesWritten == 0) && (charsWritten == 0);
704     }
705
706
707     public void setBufferSize(int size) {
708         if (size > bb.getLimit()) {// ??????
709
bb.setLimit(size);
710     }
711     }
712
713
714     public void reset() {
715
716         //count=0;
717
bb.recycle();
718         bytesWritten = 0;
719         cb.recycle();
720         charsWritten = 0;
721         gotEnc = false;
722         enc = null;
723
724     }
725
726
727     public int getBufferSize() {
728     return bb.getLimit();
729     }
730
731 }
732
Popular Tags