KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > coyote > http11 > InternalOutputBuffer


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.coyote.http11;
19
20 import java.io.IOException JavaDoc;
21 import java.io.OutputStream JavaDoc;
22 import java.security.AccessController JavaDoc;
23 import java.security.PrivilegedAction JavaDoc;
24
25 import org.apache.tomcat.util.buf.ByteChunk;
26 import org.apache.tomcat.util.buf.CharChunk;
27 import org.apache.tomcat.util.buf.MessageBytes;
28 import org.apache.tomcat.util.http.HttpMessages;
29 import org.apache.tomcat.util.http.MimeHeaders;
30 import org.apache.tomcat.util.res.StringManager;
31
32 import org.apache.coyote.ActionCode;
33 import org.apache.coyote.OutputBuffer;
34 import org.apache.coyote.Response;
35
36 /**
37  * Output buffer.
38  *
39  * @author <a HREF="mailto:remm@apache.org">Remy Maucherat</a>
40  */

41 public class InternalOutputBuffer
42     implements OutputBuffer, ByteChunk.ByteOutputChannel {
43
44     // -------------------------------------------------------------- Constants
45

46
47     // ----------------------------------------------------------- Constructors
48

49
50     /**
51      * Default constructor.
52      */

53     public InternalOutputBuffer(Response response) {
54         this(response, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
55     }
56
57
58     /**
59      * Alternate constructor.
60      */

61     public InternalOutputBuffer(Response response, int headerBufferSize) {
62
63         this.response = response;
64         
65         headers = response.getMimeHeaders();
66
67         buf = new byte[headerBufferSize];
68
69         outputStreamOutputBuffer = new OutputStreamOutputBuffer();
70
71         filterLibrary = new OutputFilter[0];
72         activeFilters = new OutputFilter[0];
73         lastActiveFilter = -1;
74
75         socketBuffer = new ByteChunk();
76         socketBuffer.setByteOutputChannel(this);
77
78         committed = false;
79         finished = false;
80
81     }
82
83
84     // -------------------------------------------------------------- Variables
85

86
87     /**
88      * The string manager for this package.
89      */

90     protected static StringManager sm =
91         StringManager.getManager(Constants.Package);
92
93
94     // ----------------------------------------------------- Instance Variables
95

96
97     /**
98      * Associated Coyote response.
99      */

100     protected Response response;
101
102
103     /**
104      * Headers of the associated request.
105      */

106     protected MimeHeaders headers;
107
108
109     /**
110      * Committed flag.
111      */

112     protected boolean committed;
113
114
115     /**
116      * Finished flag.
117      */

118     protected boolean finished;
119
120
121     /**
122      * The buffer used for header composition.
123      */

124     protected byte[] buf;
125
126
127     /**
128      * Position in the buffer.
129      */

130     protected int pos;
131
132
133     /**
134      * Underlying output stream.
135      */

136     protected OutputStream JavaDoc outputStream;
137
138
139     /**
140      * Underlying output buffer.
141      */

142     protected OutputBuffer outputStreamOutputBuffer;
143
144
145     /**
146      * Filter library.
147      * Note: Filter[0] is always the "chunked" filter.
148      */

149     protected OutputFilter[] filterLibrary;
150
151
152     /**
153      * Active filter (which is actually the top of the pipeline).
154      */

155     protected OutputFilter[] activeFilters;
156
157
158     /**
159      * Index of the last active filter.
160      */

161     protected int lastActiveFilter;
162
163
164     /**
165      * Socket buffer.
166      */

167     protected ByteChunk socketBuffer;
168
169
170     /**
171      * Socket buffer (extra buffering to reduce number of packets sent).
172      */

173     protected boolean useSocketBuffer = false;
174
175
176     // ------------------------------------------------------------- Properties
177

178
179     /**
180      * Set the underlying socket output stream.
181      */

182     public void setOutputStream(OutputStream JavaDoc outputStream) {
183
184         // FIXME: Check for null ?
185

186         this.outputStream = outputStream;
187
188     }
189
190
191     /**
192      * Get the underlying socket output stream.
193      */

194     public OutputStream JavaDoc getOutputStream() {
195
196         return outputStream;
197
198     }
199
200
201     /**
202      * Set the socket buffer size.
203      */

204     public void setSocketBuffer(int socketBufferSize) {
205
206         if (socketBufferSize > 500) {
207             useSocketBuffer = true;
208             socketBuffer.allocate(socketBufferSize, socketBufferSize);
209         } else {
210             useSocketBuffer = false;
211         }
212
213     }
214
215
216     /**
217      * Add an output filter to the filter library.
218      */

219     public void addFilter(OutputFilter filter) {
220
221         OutputFilter[] newFilterLibrary =
222             new OutputFilter[filterLibrary.length + 1];
223         for (int i = 0; i < filterLibrary.length; i++) {
224             newFilterLibrary[i] = filterLibrary[i];
225         }
226         newFilterLibrary[filterLibrary.length] = filter;
227         filterLibrary = newFilterLibrary;
228
229         activeFilters = new OutputFilter[filterLibrary.length];
230
231     }
232
233
234     /**
235      * Get filters.
236      */

237     public OutputFilter[] getFilters() {
238
239         return filterLibrary;
240
241     }
242
243
244     /**
245      * Clear filters.
246      */

247     public void clearFilters() {
248
249         filterLibrary = new OutputFilter[0];
250         lastActiveFilter = -1;
251
252     }
253
254
255     /**
256      * Add an output filter to the filter library.
257      */

258     public void addActiveFilter(OutputFilter filter) {
259
260         if (lastActiveFilter == -1) {
261             filter.setBuffer(outputStreamOutputBuffer);
262         } else {
263             for (int i = 0; i <= lastActiveFilter; i++) {
264                 if (activeFilters[i] == filter)
265                     return;
266             }
267             filter.setBuffer(activeFilters[lastActiveFilter]);
268         }
269
270         activeFilters[++lastActiveFilter] = filter;
271
272         filter.setResponse(response);
273
274     }
275
276
277     // --------------------------------------------------------- Public Methods
278

279
280     /**
281      * Flush the response.
282      *
283      * @throws IOException an undelying I/O error occured
284      */

285     public void flush()
286         throws IOException JavaDoc {
287
288         if (!committed) {
289
290             // Send the connector a request for commit. The connector should
291
// then validate the headers, send them (using sendHeader) and
292
// set the filters accordingly.
293
response.action(ActionCode.ACTION_COMMIT, null);
294
295         }
296
297         // Flush the current buffer
298
if (useSocketBuffer) {
299             socketBuffer.flushBuffer();
300         }
301
302     }
303
304
305     /**
306      * Reset current response.
307      *
308      * @throws IllegalStateException if the response has already been committed
309      */

310     public void reset() {
311
312         if (committed)
313             throw new IllegalStateException JavaDoc(/*FIXME:Put an error message*/);
314
315         // Recycle Request object
316
response.recycle();
317
318     }
319
320
321     /**
322      * Recycle the output buffer. This should be called when closing the
323      * connection.
324      */

325     public void recycle() {
326
327         // Recycle Request object
328
response.recycle();
329         socketBuffer.recycle();
330
331         outputStream = null;
332         pos = 0;
333         lastActiveFilter = -1;
334         committed = false;
335         finished = false;
336
337     }
338
339
340     /**
341      * End processing of current HTTP request.
342      * Note: All bytes of the current request should have been already
343      * consumed. This method only resets all the pointers so that we are ready
344      * to parse the next HTTP request.
345      */

346     public void nextRequest() {
347
348         // Recycle Request object
349
response.recycle();
350         socketBuffer.recycle();
351
352         // Recycle filters
353
for (int i = 0; i <= lastActiveFilter; i++) {
354             activeFilters[i].recycle();
355         }
356
357         // Reset pointers
358
pos = 0;
359         lastActiveFilter = -1;
360         committed = false;
361         finished = false;
362
363     }
364
365
366     /**
367      * End request.
368      *
369      * @throws IOException an undelying I/O error occured
370      */

371     public void endRequest()
372         throws IOException JavaDoc {
373
374         if (!committed) {
375
376             // Send the connector a request for commit. The connector should
377
// then validate the headers, send them (using sendHeader) and
378
// set the filters accordingly.
379
response.action(ActionCode.ACTION_COMMIT, null);
380
381         }
382
383         if (finished)
384             return;
385
386         if (lastActiveFilter != -1)
387             activeFilters[lastActiveFilter].end();
388
389         if (useSocketBuffer) {
390             socketBuffer.flushBuffer();
391         }
392
393         finished = true;
394
395     }
396
397
398     // ------------------------------------------------ HTTP/1.1 Output Methods
399

400
401     /**
402      * Send an acknoledgement.
403      */

404     public void sendAck()
405         throws IOException JavaDoc {
406
407         if (!committed)
408             outputStream.write(Constants.ACK_BYTES);
409
410     }
411
412
413     /**
414      * Send the response status line.
415      */

416     public void sendStatus() {
417
418         // Write protocol name
419
write(Constants.HTTP_11_BYTES);
420         buf[pos++] = Constants.SP;
421
422         // Write status code
423
int status = response.getStatus();
424         switch (status) {
425         case 200:
426             write(Constants._200_BYTES);
427             break;
428         case 400:
429             write(Constants._400_BYTES);
430             break;
431         case 404:
432             write(Constants._404_BYTES);
433             break;
434         default:
435             write(status);
436         }
437
438         buf[pos++] = Constants.SP;
439
440         // Write message
441
String JavaDoc message = response.getMessage();
442         if (message == null) {
443             write(getMessage(status));
444         } else {
445             write(message);
446         }
447
448         // End the response status line
449
if (System.getSecurityManager() != null){
450            AccessController.doPrivileged(
451                 new PrivilegedAction JavaDoc(){
452                     public Object JavaDoc run(){
453                         buf[pos++] = Constants.CR;
454                         buf[pos++] = Constants.LF;
455                         return null;
456                     }
457                 }
458            );
459         } else {
460             buf[pos++] = Constants.CR;
461             buf[pos++] = Constants.LF;
462         }
463
464     }
465
466     private String JavaDoc getMessage(final int message){
467         if (System.getSecurityManager() != null){
468            return (String JavaDoc)AccessController.doPrivileged(
469                 new PrivilegedAction JavaDoc(){
470                     public Object JavaDoc run(){
471                         return HttpMessages.getMessage(message);
472                     }
473                 }
474            );
475         } else {
476             return HttpMessages.getMessage(message);
477         }
478     }
479
480     /**
481      * Send a header.
482      *
483      * @param name Header name
484      * @param value Header value
485      */

486     public void sendHeader(MessageBytes name, MessageBytes value) {
487
488         write(name);
489         buf[pos++] = Constants.COLON;
490         buf[pos++] = Constants.SP;
491         write(value);
492         buf[pos++] = Constants.CR;
493         buf[pos++] = Constants.LF;
494
495     }
496
497
498     /**
499      * Send a header.
500      *
501      * @param name Header name
502      * @param value Header value
503      */

504     public void sendHeader(ByteChunk name, ByteChunk value) {
505
506         write(name);
507         buf[pos++] = Constants.COLON;
508         buf[pos++] = Constants.SP;
509         write(value);
510         buf[pos++] = Constants.CR;
511         buf[pos++] = Constants.LF;
512
513     }
514
515
516     /**
517      * Send a header.
518      *
519      * @param name Header name
520      * @param value Header value
521      */

522     public void sendHeader(String JavaDoc name, String JavaDoc value) {
523
524         write(name);
525         buf[pos++] = Constants.COLON;
526         buf[pos++] = Constants.SP;
527         write(value);
528         buf[pos++] = Constants.CR;
529         buf[pos++] = Constants.LF;
530
531     }
532
533
534     /**
535      * End the header block.
536      */

537     public void endHeaders() {
538
539         buf[pos++] = Constants.CR;
540         buf[pos++] = Constants.LF;
541
542     }
543
544
545     // --------------------------------------------------- OutputBuffer Methods
546

547
548     /**
549      * Write the contents of a byte chunk.
550      *
551      * @param chunk byte chunk
552      * @return number of bytes written
553      * @throws IOException an undelying I/O error occured
554      */

555     public int doWrite(ByteChunk chunk, Response res)
556         throws IOException JavaDoc {
557
558         if (!committed) {
559
560             // Send the connector a request for commit. The connector should
561
// then validate the headers, send them (using sendHeaders) and
562
// set the filters accordingly.
563
response.action(ActionCode.ACTION_COMMIT, null);
564
565         }
566
567         if (lastActiveFilter == -1)
568             return outputStreamOutputBuffer.doWrite(chunk, res);
569         else
570             return activeFilters[lastActiveFilter].doWrite(chunk, res);
571
572     }
573
574
575     // ------------------------------------------------------ Protected Methods
576

577
578     /**
579      * Commit the response.
580      *
581      * @throws IOException an undelying I/O error occured
582      */

583     protected void commit()
584         throws IOException JavaDoc {
585
586         // The response is now committed
587
committed = true;
588         response.setCommitted(true);
589
590         if (pos > 0) {
591             // Sending the response header buffer
592
if (useSocketBuffer) {
593                 socketBuffer.append(buf, 0, pos);
594             } else {
595                 outputStream.write(buf, 0, pos);
596             }
597         }
598
599     }
600
601
602     /**
603      * This method will write the contents of the specyfied message bytes
604      * buffer to the output stream, without filtering. This method is meant to
605      * be used to write the response header.
606      *
607      * @param mb data to be written
608      */

609     protected void write(MessageBytes mb) {
610
611         if (mb.getType() == MessageBytes.T_BYTES) {
612             ByteChunk bc = mb.getByteChunk();
613             write(bc);
614         } else if (mb.getType() == MessageBytes.T_CHARS) {
615             CharChunk cc = mb.getCharChunk();
616             write(cc);
617         } else {
618             write(mb.toString());
619         }
620
621     }
622
623
624     /**
625      * This method will write the contents of the specyfied message bytes
626      * buffer to the output stream, without filtering. This method is meant to
627      * be used to write the response header.
628      *
629      * @param bc data to be written
630      */

631     protected void write(ByteChunk bc) {
632
633         // Writing the byte chunk to the output buffer
634
System.arraycopy(bc.getBytes(), bc.getStart(), buf, pos,
635                          bc.getLength());
636         pos = pos + bc.getLength();
637
638     }
639
640
641     /**
642      * This method will write the contents of the specyfied char
643      * buffer to the output stream, without filtering. This method is meant to
644      * be used to write the response header.
645      *
646      * @param cc data to be written
647      */

648     protected void write(CharChunk cc) {
649
650         int start = cc.getStart();
651         int end = cc.getEnd();
652         char[] cbuf = cc.getBuffer();
653         for (int i = start; i < end; i++) {
654             char c = cbuf[i];
655             // Note: This is clearly incorrect for many strings,
656
// but is the only consistent approach within the current
657
// servlet framework. It must suffice until servlet output
658
// streams properly encode their output.
659
if ((c <= 31) && (c != 9)) {
660                 c = ' ';
661             } else if (c == 127) {
662                 c = ' ';
663             }
664             buf[pos++] = (byte) c;
665         }
666
667     }
668
669
670     /**
671      * This method will write the contents of the specyfied byte
672      * buffer to the output stream, without filtering. This method is meant to
673      * be used to write the response header.
674      *
675      * @param b data to be written
676      */

677     public void write(byte[] b) {
678
679         // Writing the byte chunk to the output buffer
680
System.arraycopy(b, 0, buf, pos, b.length);
681         pos = pos + b.length;
682
683     }
684
685
686     /**
687      * This method will write the contents of the specyfied String to the
688      * output stream, without filtering. This method is meant to be used to
689      * write the response header.
690      *
691      * @param s data to be written
692      */

693     protected void write(String JavaDoc s) {
694
695         if (s == null)
696             return;
697
698         // From the Tomcat 3.3 HTTP/1.0 connector
699
int len = s.length();
700         for (int i = 0; i < len; i++) {
701             char c = s.charAt (i);
702             // Note: This is clearly incorrect for many strings,
703
// but is the only consistent approach within the current
704
// servlet framework. It must suffice until servlet output
705
// streams properly encode their output.
706
if ((c <= 31) && (c != 9)) {
707                 c = ' ';
708             } else if (c == 127) {
709                 c = ' ';
710             }
711             buf[pos++] = (byte) c;
712         }
713
714     }
715
716
717     /**
718      * This method will print the specified integer to the output stream,
719      * without filtering. This method is meant to be used to write the
720      * response header.
721      *
722      * @param i data to be written
723      */

724     protected void write(int i) {
725
726         write(String.valueOf(i));
727
728     }
729
730
731     /**
732      * Callback to write data from the buffer.
733      */

734     public void realWriteBytes(byte cbuf[], int off, int len)
735         throws IOException JavaDoc {
736         if (len > 0) {
737             outputStream.write(cbuf, off, len);
738         }
739     }
740
741
742     // ----------------------------------- OutputStreamOutputBuffer Inner Class
743

744
745     /**
746      * This class is an output buffer which will write data to an output
747      * stream.
748      */

749     protected class OutputStreamOutputBuffer
750         implements OutputBuffer {
751
752
753         /**
754          * Write chunk.
755          */

756         public int doWrite(ByteChunk chunk, Response res)
757             throws IOException JavaDoc {
758
759             if (useSocketBuffer) {
760                 socketBuffer.append(chunk.getBuffer(), chunk.getStart(),
761                                    chunk.getLength());
762             } else {
763                 outputStream.write(chunk.getBuffer(), chunk.getStart(),
764                                    chunk.getLength());
765             }
766             return chunk.getLength();
767
768         }
769
770
771     }
772
773
774 }
775
Popular Tags