KickJava   Java API By Example, From Geeks To Geeks.

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


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.nio.ByteBuffer JavaDoc;
22
23 import org.apache.tomcat.jni.Socket;
24 import org.apache.tomcat.util.buf.ByteChunk;
25 import org.apache.tomcat.util.buf.CharChunk;
26 import org.apache.tomcat.util.buf.MessageBytes;
27 import org.apache.tomcat.util.http.HttpMessages;
28 import org.apache.tomcat.util.http.MimeHeaders;
29 import org.apache.tomcat.util.res.StringManager;
30
31 import org.apache.coyote.ActionCode;
32 import org.apache.coyote.OutputBuffer;
33 import org.apache.coyote.Response;
34
35 /**
36  * Output buffer.
37  *
38  * @author <a HREF="mailto:remm@apache.org">Remy Maucherat</a>
39  */

40 public class InternalAprOutputBuffer
41     implements OutputBuffer {
42
43
44     // -------------------------------------------------------------- Constants
45

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

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

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

61     public InternalAprOutputBuffer(Response response, int headerBufferSize) {
62
63         this.response = response;
64         headers = response.getMimeHeaders();
65
66         buf = new byte[headerBufferSize];
67         if (headerBufferSize < (8 * 1024)) {
68             bbuf = ByteBuffer.allocateDirect(6 * 1500);
69         } else {
70             bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500);
71         }
72
73         outputStreamOutputBuffer = new SocketOutputBuffer();
74
75         filterLibrary = new OutputFilter[0];
76         activeFilters = new OutputFilter[0];
77         lastActiveFilter = -1;
78
79         committed = false;
80         finished = false;
81
82         // Cause loading of HttpMessages
83
HttpMessages.getMessage(200);
84
85     }
86
87
88     // -------------------------------------------------------------- Variables
89

90
91     /**
92      * The string manager for this package.
93      */

94     protected static StringManager sm =
95         StringManager.getManager(Constants.Package);
96
97
98     // ----------------------------------------------------- Instance Variables
99

100
101     /**
102      * Associated Coyote response.
103      */

104     protected Response response;
105
106
107     /**
108      * Headers of the associated request.
109      */

110     protected MimeHeaders headers;
111
112
113     /**
114      * Committed flag.
115      */

116     protected boolean committed;
117
118
119     /**
120      * Finished flag.
121      */

122     protected boolean finished;
123
124
125     /**
126      * Pointer to the current write buffer.
127      */

128     protected byte[] buf;
129
130
131     /**
132      * Position in the buffer.
133      */

134     protected int pos;
135
136
137     /**
138      * Underlying socket.
139      */

140     protected long socket;
141
142
143     /**
144      * Underlying output buffer.
145      */

146     protected OutputBuffer outputStreamOutputBuffer;
147
148
149     /**
150      * Filter library.
151      * Note: Filter[0] is always the "chunked" filter.
152      */

153     protected OutputFilter[] filterLibrary;
154
155
156     /**
157      * Active filter (which is actually the top of the pipeline).
158      */

159     protected OutputFilter[] activeFilters;
160
161
162     /**
163      * Index of the last active filter.
164      */

165     protected int lastActiveFilter;
166
167
168     /**
169      * Direct byte buffer used for writing.
170      */

171     protected ByteBuffer JavaDoc bbuf = null;
172
173     
174     // ------------------------------------------------------------- Properties
175

176
177     /**
178      * Set the underlying socket.
179      */

180     public void setSocket(long socket) {
181         this.socket = socket;
182         Socket.setsbb(this.socket, bbuf);
183     }
184
185
186     /**
187      * Get the underlying socket input stream.
188      */

189     public long getSocket() {
190         return socket;
191     }
192
193
194     /**
195      * Set the socket buffer size.
196      */

197     public void setSocketBuffer(int socketBufferSize) {
198         // FIXME: Remove
199
}
200
201
202     /**
203      * Add an output filter to the filter library.
204      */

205     public void addFilter(OutputFilter filter) {
206
207         OutputFilter[] newFilterLibrary =
208             new OutputFilter[filterLibrary.length + 1];
209         for (int i = 0; i < filterLibrary.length; i++) {
210             newFilterLibrary[i] = filterLibrary[i];
211         }
212         newFilterLibrary[filterLibrary.length] = filter;
213         filterLibrary = newFilterLibrary;
214
215         activeFilters = new OutputFilter[filterLibrary.length];
216
217     }
218
219
220     /**
221      * Get filters.
222      */

223     public OutputFilter[] getFilters() {
224
225         return filterLibrary;
226
227     }
228
229
230     /**
231      * Clear filters.
232      */

233     public void clearFilters() {
234
235         filterLibrary = new OutputFilter[0];
236         lastActiveFilter = -1;
237
238     }
239
240
241     /**
242      * Add an output filter to the filter library.
243      */

244     public void addActiveFilter(OutputFilter filter) {
245
246         if (lastActiveFilter == -1) {
247             filter.setBuffer(outputStreamOutputBuffer);
248         } else {
249             for (int i = 0; i <= lastActiveFilter; i++) {
250                 if (activeFilters[i] == filter)
251                     return;
252             }
253             filter.setBuffer(activeFilters[lastActiveFilter]);
254         }
255
256         activeFilters[++lastActiveFilter] = filter;
257
258         filter.setResponse(response);
259
260     }
261
262
263     // --------------------------------------------------------- Public Methods
264

265
266     /**
267      * Flush the response.
268      *
269      * @throws IOException an undelying I/O error occured
270      */

271     public void flush()
272         throws IOException JavaDoc {
273
274         if (!committed) {
275
276             // Send the connector a request for commit. The connector should
277
// then validate the headers, send them (using sendHeader) and
278
// set the filters accordingly.
279
response.action(ActionCode.ACTION_COMMIT, null);
280
281         }
282
283         // Flush the current buffer
284
flushBuffer();
285
286     }
287
288
289     /**
290      * Reset current response.
291      *
292      * @throws IllegalStateException if the response has already been committed
293      */

294     public void reset() {
295
296         if (committed)
297             throw new IllegalStateException JavaDoc(/*FIXME:Put an error message*/);
298
299         // Recycle Request object
300
response.recycle();
301
302     }
303
304
305     /**
306      * Recycle the output buffer. This should be called when closing the
307      * connection.
308      */

309     public void recycle() {
310
311         // Recycle Request object
312
response.recycle();
313         bbuf.clear();
314
315         socket = 0;
316         pos = 0;
317         lastActiveFilter = -1;
318         committed = false;
319         finished = false;
320
321     }
322
323
324     /**
325      * End processing of current HTTP request.
326      * Note: All bytes of the current request should have been already
327      * consumed. This method only resets all the pointers so that we are ready
328      * to parse the next HTTP request.
329      */

330     public void nextRequest() {
331
332         // Recycle Request object
333
response.recycle();
334
335         // Recycle filters
336
for (int i = 0; i <= lastActiveFilter; i++) {
337             activeFilters[i].recycle();
338         }
339
340         // Reset pointers
341
pos = 0;
342         lastActiveFilter = -1;
343         committed = false;
344         finished = false;
345
346     }
347
348
349     /**
350      * End request.
351      *
352      * @throws IOException an undelying I/O error occured
353      */

354     public void endRequest()
355         throws IOException JavaDoc {
356
357         if (!committed) {
358
359             // Send the connector a request for commit. The connector should
360
// then validate the headers, send them (using sendHeader) and
361
// set the filters accordingly.
362
response.action(ActionCode.ACTION_COMMIT, null);
363
364         }
365
366         if (finished)
367             return;
368
369         if (lastActiveFilter != -1)
370             activeFilters[lastActiveFilter].end();
371
372         flushBuffer();
373
374         finished = true;
375
376     }
377
378
379     // ------------------------------------------------ HTTP/1.1 Output Methods
380

381
382     /**
383      * Send an acknoledgement.
384      */

385     public void sendAck()
386         throws IOException JavaDoc {
387
388         if (!committed) {
389             if (Socket.send(socket, Constants.ACK_BYTES, 0, Constants.ACK_BYTES.length) < 0)
390                 throw new IOException JavaDoc(sm.getString("iib.failedwrite"));
391         }
392
393     }
394
395
396     /**
397      * Send the response status line.
398      */

399     public void sendStatus() {
400
401         // Write protocol name
402
write(Constants.HTTP_11_BYTES);
403         buf[pos++] = Constants.SP;
404
405         // Write status code
406
int status = response.getStatus();
407         switch (status) {
408         case 200:
409             write(Constants._200_BYTES);
410             break;
411         case 400:
412             write(Constants._400_BYTES);
413             break;
414         case 404:
415             write(Constants._404_BYTES);
416             break;
417         default:
418             write(status);
419         }
420
421         buf[pos++] = Constants.SP;
422
423         // Write message
424
String JavaDoc message = response.getMessage();
425         if (message == null) {
426             write(HttpMessages.getMessage(status));
427         } else {
428             write(message);
429         }
430
431         // End the response status line
432
buf[pos++] = Constants.CR;
433         buf[pos++] = Constants.LF;
434
435     }
436
437
438     /**
439      * Send a header.
440      *
441      * @param name Header name
442      * @param value Header value
443      */

444     public void sendHeader(MessageBytes name, MessageBytes value) {
445
446         write(name);
447         buf[pos++] = Constants.COLON;
448         buf[pos++] = Constants.SP;
449         write(value);
450         buf[pos++] = Constants.CR;
451         buf[pos++] = Constants.LF;
452
453     }
454
455
456     /**
457      * Send a header.
458      *
459      * @param name Header name
460      * @param value Header value
461      */

462     public void sendHeader(ByteChunk name, ByteChunk value) {
463
464         write(name);
465         buf[pos++] = Constants.COLON;
466         buf[pos++] = Constants.SP;
467         write(value);
468         buf[pos++] = Constants.CR;
469         buf[pos++] = Constants.LF;
470
471     }
472
473
474     /**
475      * Send a header.
476      *
477      * @param name Header name
478      * @param value Header value
479      */

480     public void sendHeader(String JavaDoc name, String JavaDoc value) {
481
482         write(name);
483         buf[pos++] = Constants.COLON;
484         buf[pos++] = Constants.SP;
485         write(value);
486         buf[pos++] = Constants.CR;
487         buf[pos++] = Constants.LF;
488
489     }
490
491
492     /**
493      * End the header block.
494      */

495     public void endHeaders() {
496
497         buf[pos++] = Constants.CR;
498         buf[pos++] = Constants.LF;
499
500     }
501
502
503     // --------------------------------------------------- OutputBuffer Methods
504

505
506     /**
507      * Write the contents of a byte chunk.
508      *
509      * @param chunk byte chunk
510      * @return number of bytes written
511      * @throws IOException an undelying I/O error occured
512      */

513     public int doWrite(ByteChunk chunk, Response res)
514         throws IOException JavaDoc {
515
516         if (!committed) {
517
518             // Send the connector a request for commit. The connector should
519
// then validate the headers, send them (using sendHeaders) and
520
// set the filters accordingly.
521
response.action(ActionCode.ACTION_COMMIT, null);
522
523         }
524
525         if (lastActiveFilter == -1)
526             return outputStreamOutputBuffer.doWrite(chunk, res);
527         else
528             return activeFilters[lastActiveFilter].doWrite(chunk, res);
529
530     }
531
532
533     // ------------------------------------------------------ Protected Methods
534

535
536     /**
537      * Commit the response.
538      *
539      * @throws IOException an undelying I/O error occured
540      */

541     protected void commit()
542         throws IOException JavaDoc {
543
544         // The response is now committed
545
committed = true;
546         response.setCommitted(true);
547
548         if (pos > 0) {
549             // Sending the response header buffer
550
bbuf.put(buf, 0, pos);
551         }
552
553     }
554
555
556     /**
557      * This method will write the contents of the specyfied message bytes
558      * buffer to the output stream, without filtering. This method is meant to
559      * be used to write the response header.
560      *
561      * @param mb data to be written
562      */

563     protected void write(MessageBytes mb) {
564
565         if (mb.getType() == MessageBytes.T_BYTES) {
566             ByteChunk bc = mb.getByteChunk();
567             write(bc);
568         } else if (mb.getType() == MessageBytes.T_CHARS) {
569             CharChunk cc = mb.getCharChunk();
570             write(cc);
571         } else {
572             write(mb.toString());
573         }
574
575     }
576
577
578     /**
579      * This method will write the contents of the specyfied message bytes
580      * buffer to the output stream, without filtering. This method is meant to
581      * be used to write the response header.
582      *
583      * @param bc data to be written
584      */

585     protected void write(ByteChunk bc) {
586
587         // Writing the byte chunk to the output buffer
588
System.arraycopy(bc.getBytes(), bc.getStart(), buf, pos,
589                          bc.getLength());
590         pos = pos + bc.getLength();
591
592     }
593
594
595     /**
596      * This method will write the contents of the specyfied char
597      * buffer to the output stream, without filtering. This method is meant to
598      * be used to write the response header.
599      *
600      * @param cc data to be written
601      */

602     protected void write(CharChunk cc) {
603
604         int start = cc.getStart();
605         int end = cc.getEnd();
606         char[] cbuf = cc.getBuffer();
607         for (int i = start; i < end; i++) {
608             char c = cbuf[i];
609             // Note: This is clearly incorrect for many strings,
610
// but is the only consistent approach within the current
611
// servlet framework. It must suffice until servlet output
612
// streams properly encode their output.
613
if ((c <= 31) && (c != 9)) {
614                 c = ' ';
615             } else if (c == 127) {
616                 c = ' ';
617             }
618             buf[pos++] = (byte) c;
619         }
620
621     }
622
623
624     /**
625      * This method will write the contents of the specyfied byte
626      * buffer to the output stream, without filtering. This method is meant to
627      * be used to write the response header.
628      *
629      * @param b data to be written
630      */

631     public void write(byte[] b) {
632
633         // Writing the byte chunk to the output buffer
634
System.arraycopy(b, 0, buf, pos, b.length);
635         pos = pos + b.length;
636
637     }
638
639
640     /**
641      * This method will write the contents of the specyfied String to the
642      * output stream, without filtering. This method is meant to be used to
643      * write the response header.
644      *
645      * @param s data to be written
646      */

647     protected void write(String JavaDoc s) {
648
649         if (s == null)
650             return;
651
652         // From the Tomcat 3.3 HTTP/1.0 connector
653
int len = s.length();
654         for (int i = 0; i < len; i++) {
655             char c = s.charAt (i);
656             // Note: This is clearly incorrect for many strings,
657
// but is the only consistent approach within the current
658
// servlet framework. It must suffice until servlet output
659
// streams properly encode their output.
660
if ((c <= 31) && (c != 9)) {
661                 c = ' ';
662             } else if (c == 127) {
663                 c = ' ';
664             }
665             buf[pos++] = (byte) c;
666         }
667
668     }
669
670
671     /**
672      * This method will print the specified integer to the output stream,
673      * without filtering. This method is meant to be used to write the
674      * response header.
675      *
676      * @param i data to be written
677      */

678     protected void write(int i) {
679
680         write(String.valueOf(i));
681
682     }
683
684
685     /**
686      * Callback to write data from the buffer.
687      */

688     protected void flushBuffer()
689         throws IOException JavaDoc {
690         if (bbuf.position() > 0) {
691             if (Socket.sendbb(socket, 0, bbuf.position()) < 0) {
692                 throw new IOException JavaDoc();
693             }
694             bbuf.clear();
695         }
696     }
697
698
699     // ----------------------------------- OutputStreamOutputBuffer Inner Class
700

701
702     /**
703      * This class is an output buffer which will write data to an output
704      * stream.
705      */

706     protected class SocketOutputBuffer
707         implements OutputBuffer {
708
709
710         /**
711          * Write chunk.
712          */

713         public int doWrite(ByteChunk chunk, Response res)
714             throws IOException JavaDoc {
715
716             int len = chunk.getLength();
717             int start = chunk.getStart();
718             byte[] b = chunk.getBuffer();
719             while (len > 0) {
720                 int thisTime = len;
721                 if (bbuf.position() == bbuf.capacity()) {
722                     flushBuffer();
723                 }
724                 if (thisTime > bbuf.capacity() - bbuf.position()) {
725                     thisTime = bbuf.capacity() - bbuf.position();
726                 }
727                 bbuf.put(b, start, thisTime);
728                 len = len - thisTime;
729                 start = start + thisTime;
730             }
731             return chunk.getLength();
732
733         }
734
735
736     }
737
738
739 }
740
Popular Tags