~ubuntu-branches/ubuntu/raring/libjboss-remoting-java/raring

« back to all changes in this revision

Viewing changes to src/main/org/jboss/remoting/transport/coyote/OutputBuffer.java

  • Committer: Package Import Robot
  • Author(s): Torsten Werner
  • Date: 2011-09-09 14:01:03 UTC
  • mfrom: (1.1.6 upstream)
  • Revision ID: package-import@ubuntu.com-20110909140103-hqokx61534tas9rg
Tags: 2.5.3.SP1-1
* Newer but not newest upstream release. Do not build samples.
* Change debian/watch to upstream's svn repo.
* Add patch to fix compile error caused by tomcat update.
  (Closes: #628303)
* Switch to source format 3.0.
* Switch to debhelper level 7.
* Remove useless Depends.
* Update Standards-Version: 3.9.2.
* Update README.source.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
18
package org.jboss.remoting.transport.coyote;
 
19
 
 
20
 
 
21
import java.io.IOException;
 
22
import java.io.Writer;
 
23
import java.security.AccessController;
 
24
import java.security.PrivilegedActionException;
 
25
import java.security.PrivilegedExceptionAction;
 
26
import java.util.HashMap;
 
27
import org.apache.coyote.ActionCode;
 
28
import org.apache.coyote.Response;
 
29
import org.apache.tomcat.util.buf.ByteChunk;
 
30
import org.apache.tomcat.util.buf.C2BConverter;
 
31
import org.apache.tomcat.util.buf.CharChunk;
 
32
import org.jboss.remoting.util.SecurityUtility;
 
33
 
 
34
 
 
35
/**
 
36
 * The buffer used by Tomcat response. This is a derivative of the Tomcat 3.3
 
37
 * OutputBuffer, with the removal of some of the state handling (which in
 
38
 * Coyote is mostly the Processor's responsability).
 
39
 *
 
40
 * @author Costin Manolache
 
41
 * @author Remy Maucherat
 
42
 */
 
43
public class OutputBuffer extends Writer
 
44
      implements ByteChunk.ByteOutputChannel, CharChunk.CharOutputChannel
 
45
{
 
46
 
 
47
   // -------------------------------------------------------------- Constants
 
48
 
 
49
 
 
50
   public static final String DEFAULT_ENCODING =
 
51
         org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
 
52
   public static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
 
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
   // ----------------------------------------------------- Instance Variables
 
63
 
 
64
 
 
65
   /**
 
66
    * The byte buffer.
 
67
    */
 
68
   private ByteChunk bb;
 
69
 
 
70
 
 
71
   /**
 
72
    * The chunk buffer.
 
73
    */
 
74
   private CharChunk cb;
 
75
 
 
76
 
 
77
   /**
 
78
    * State of the output buffer.
 
79
    */
 
80
   private int state = 0;
 
81
 
 
82
 
 
83
   /**
 
84
    * Number of bytes written.
 
85
    */
 
86
   private int bytesWritten = 0;
 
87
 
 
88
 
 
89
   /**
 
90
    * Number of chars written.
 
91
    */
 
92
   private int charsWritten = 0;
 
93
 
 
94
 
 
95
   /**
 
96
    * Flag which indicates if the output buffer is closed.
 
97
    */
 
98
   private boolean closed = false;
 
99
 
 
100
 
 
101
   /**
 
102
    * Do a flush on the next operation.
 
103
    */
 
104
   private boolean doFlush = false;
 
105
 
 
106
 
 
107
   /**
 
108
    * Byte chunk used to output bytes.
 
109
    */
 
110
   private ByteChunk outputChunk = new ByteChunk();
 
111
 
 
112
 
 
113
   /**
 
114
    * Encoding to use.
 
115
    */
 
116
   private String enc;
 
117
 
 
118
 
 
119
   /**
 
120
    * Encoder is set.
 
121
    */
 
122
   private boolean gotEnc = false;
 
123
 
 
124
 
 
125
   /**
 
126
    * List of encoders.
 
127
    */
 
128
   protected HashMap encoders = new HashMap();
 
129
 
 
130
 
 
131
   /**
 
132
    * Current char to byte converter.
 
133
    */
 
134
   protected C2BConverter conv;
 
135
 
 
136
 
 
137
   /**
 
138
    * Associated Coyote response.
 
139
    */
 
140
   private Response coyoteResponse;
 
141
 
 
142
 
 
143
   /**
 
144
    * Suspended flag. All output bytes will be swallowed if this is true.
 
145
    */
 
146
   private boolean suspended = false;
 
147
 
 
148
   // ----------------------------------------------------------- Constructors
 
149
 
 
150
 
 
151
   /**
 
152
    * Default constructor. Allocate the buffer with the default buffer size.
 
153
    */
 
154
   public OutputBuffer()
 
155
   {
 
156
 
 
157
      this(DEFAULT_BUFFER_SIZE);
 
158
 
 
159
   }
 
160
 
 
161
 
 
162
   /**
 
163
    * Alternate constructor which allows specifying the initial buffer size.
 
164
    *
 
165
    * @param size Buffer size to use
 
166
    */
 
167
   public OutputBuffer(int size)
 
168
   {
 
169
 
 
170
      bb = new ByteChunk(size);
 
171
      // Allow buffer to grow, to avoid using chunking
 
172
      // FIXME: Allowing chunking could be a better idea
 
173
      bb.setLimit(-1);
 
174
      bb.setByteOutputChannel(this);
 
175
      cb = new CharChunk(size);
 
176
      cb.setCharOutputChannel(this);
 
177
      cb.setLimit(size);
 
178
 
 
179
   }
 
180
 
 
181
   // ------------------------------------------------------------- Properties
 
182
 
 
183
 
 
184
   /**
 
185
    * Associated Coyote response.
 
186
    *
 
187
    * @param coyoteResponse Associated Coyote response
 
188
    */
 
189
   public void setResponse(Response coyoteResponse)
 
190
   {
 
191
      this.coyoteResponse = coyoteResponse;
 
192
   }
 
193
 
 
194
 
 
195
   /**
 
196
    * Get associated Coyote response.
 
197
    *
 
198
    * @return the associated Coyote response
 
199
    */
 
200
   public Response getResponse()
 
201
   {
 
202
      return this.coyoteResponse;
 
203
   }
 
204
 
 
205
 
 
206
   /**
 
207
    * Is the response output suspended ?
 
208
    *
 
209
    * @return suspended flag value
 
210
    */
 
211
   public boolean isSuspended()
 
212
   {
 
213
      return this.suspended;
 
214
   }
 
215
 
 
216
 
 
217
   /**
 
218
    * Set the suspended flag.
 
219
    *
 
220
    * @param suspended New suspended flag value
 
221
    */
 
222
   public void setSuspended(boolean suspended)
 
223
   {
 
224
      this.suspended = suspended;
 
225
   }
 
226
 
 
227
   // --------------------------------------------------------- Public Methods
 
228
 
 
229
 
 
230
   /**
 
231
    * Recycle the output buffer.
 
232
    */
 
233
   public void recycle()
 
234
   {
 
235
 
 
236
      state = INITIAL_STATE;
 
237
      bytesWritten = 0;
 
238
      charsWritten = 0;
 
239
 
 
240
      cb.recycle();
 
241
      bb.recycle();
 
242
      closed = false;
 
243
      suspended = false;
 
244
 
 
245
      if(conv != null)
 
246
      {
 
247
         conv.recycle();
 
248
      }
 
249
 
 
250
      gotEnc = false;
 
251
      enc = null;
 
252
 
 
253
   }
 
254
 
 
255
 
 
256
   /**
 
257
    * Close the output buffer. This tries to calculate the response size if
 
258
    * the response has not been committed yet.
 
259
    *
 
260
    * @throws IOException An underlying IOException occurred
 
261
    */
 
262
   public void close()
 
263
         throws IOException
 
264
   {
 
265
 
 
266
      if(closed)
 
267
      {
 
268
         return;
 
269
      }
 
270
      if(suspended)
 
271
      {
 
272
         return;
 
273
      }
 
274
 
 
275
      if((!coyoteResponse.isCommitted())
 
276
         && (coyoteResponse.getContentLengthLong() == -1))
 
277
      {
 
278
         // Flushing the char buffer
 
279
         if(state == CHAR_STATE)
 
280
         {
 
281
            cb.flushBuffer();
 
282
            state = BYTE_STATE;
 
283
         }
 
284
         // If this didn't cause a commit of the response, the final content
 
285
         // length can be calculated
 
286
         if(!coyoteResponse.isCommitted())
 
287
         {
 
288
            coyoteResponse.setContentLength(bb.getLength());
 
289
         }
 
290
      }
 
291
 
 
292
      doFlush(false);
 
293
      closed = true;
 
294
 
 
295
      coyoteResponse.finish();
 
296
 
 
297
   }
 
298
 
 
299
 
 
300
   /**
 
301
    * Flush bytes or chars contained in the buffer.
 
302
    *
 
303
    * @throws IOException An underlying IOException occurred
 
304
    */
 
305
   public void flush()
 
306
         throws IOException
 
307
   {
 
308
      doFlush(true);
 
309
   }
 
310
 
 
311
 
 
312
   /**
 
313
    * Flush bytes or chars contained in the buffer.
 
314
    *
 
315
    * @throws IOException An underlying IOException occurred
 
316
    */
 
317
   protected void doFlush(boolean realFlush)
 
318
         throws IOException
 
319
   {
 
320
 
 
321
      if(suspended)
 
322
      {
 
323
         return;
 
324
      }
 
325
 
 
326
      doFlush = true;
 
327
      if(state == CHAR_STATE)
 
328
      {
 
329
         cb.flushBuffer();
 
330
         bb.flushBuffer();
 
331
         state = BYTE_STATE;
 
332
      }
 
333
      else if(state == BYTE_STATE)
 
334
      {
 
335
         bb.flushBuffer();
 
336
      }
 
337
      else if(state == INITIAL_STATE)
 
338
      {
 
339
         // If the buffers are empty, commit the response header
 
340
         coyoteResponse.sendHeaders();
 
341
      }
 
342
      doFlush = false;
 
343
 
 
344
      if(realFlush)
 
345
      {
 
346
         coyoteResponse.action(ActionCode.ACTION_CLIENT_FLUSH,
 
347
                               coyoteResponse);
 
348
         // If some exception occurred earlier, or if some IOE occurred
 
349
         // here, notify the servlet with an IOE
 
350
         if(coyoteResponse.isExceptionPresent())
 
351
         {
 
352
            throw new ClientAbortException
 
353
                  (coyoteResponse.getErrorException());
 
354
         }
 
355
      }
 
356
 
 
357
   }
 
358
 
 
359
   // ------------------------------------------------- Bytes Handling Methods
 
360
 
 
361
 
 
362
   /**
 
363
    * Sends the buffer data to the client output, checking the
 
364
    * state of Response and calling the right interceptors.
 
365
    *
 
366
    * @param buf Byte buffer to be written to the response
 
367
    * @param off Offset
 
368
    * @param cnt Length
 
369
    * @throws IOException An underlying IOException occurred
 
370
    */
 
371
   public void realWriteBytes(byte buf[], int off, int cnt)
 
372
         throws IOException
 
373
   {
 
374
 
 
375
      if(closed)
 
376
      {
 
377
         return;
 
378
      }
 
379
      if(coyoteResponse == null)
 
380
      {
 
381
         return;
 
382
      }
 
383
 
 
384
      // If we really have something to write
 
385
      if(cnt > 0)
 
386
      {
 
387
         // real write to the adapter
 
388
         outputChunk.setBytes(buf, off, cnt);
 
389
         try
 
390
         {
 
391
            coyoteResponse.doWrite(outputChunk);
 
392
         }
 
393
         catch(IOException e)
 
394
         {
 
395
            // An IOException on a write is almost always due to
 
396
            // the remote client aborting the request.  Wrap this
 
397
            // so that it can be handled better by the error dispatcher.
 
398
            throw new ClientAbortException(e);
 
399
         }
 
400
      }
 
401
 
 
402
   }
 
403
 
 
404
 
 
405
   public void write(byte b[], int off, int len) throws IOException
 
406
   {
 
407
 
 
408
      if(suspended)
 
409
      {
 
410
         return;
 
411
      }
 
412
 
 
413
      if(state == CHAR_STATE)
 
414
      {
 
415
         cb.flushBuffer();
 
416
      }
 
417
      state = BYTE_STATE;
 
418
      writeBytes(b, off, len);
 
419
 
 
420
   }
 
421
 
 
422
 
 
423
   private void writeBytes(byte b[], int off, int len)
 
424
         throws IOException
 
425
   {
 
426
 
 
427
      if(closed)
 
428
      {
 
429
         return;
 
430
      }
 
431
 
 
432
      bb.append(b, off, len);
 
433
      bytesWritten += len;
 
434
 
 
435
      // if called from within flush(), then immediately flush
 
436
      // remaining bytes
 
437
      if(doFlush)
 
438
      {
 
439
         bb.flushBuffer();
 
440
      }
 
441
 
 
442
   }
 
443
 
 
444
 
 
445
   public void writeByte(int b)
 
446
         throws IOException
 
447
   {
 
448
 
 
449
      if(suspended)
 
450
      {
 
451
         return;
 
452
      }
 
453
 
 
454
      if(state == CHAR_STATE)
 
455
      {
 
456
         cb.flushBuffer();
 
457
      }
 
458
      state = BYTE_STATE;
 
459
 
 
460
      bb.append((byte) b);
 
461
      bytesWritten++;
 
462
 
 
463
   }
 
464
 
 
465
   // ------------------------------------------------- Chars Handling Methods
 
466
 
 
467
 
 
468
   public void write(int c)
 
469
         throws IOException
 
470
   {
 
471
 
 
472
      if(suspended)
 
473
      {
 
474
         return;
 
475
      }
 
476
 
 
477
      state = CHAR_STATE;
 
478
 
 
479
      cb.append((char) c);
 
480
      charsWritten++;
 
481
 
 
482
   }
 
483
 
 
484
 
 
485
   public void write(char c[])
 
486
         throws IOException
 
487
   {
 
488
 
 
489
      if(suspended)
 
490
      {
 
491
         return;
 
492
      }
 
493
 
 
494
      write(c, 0, c.length);
 
495
 
 
496
   }
 
497
 
 
498
 
 
499
   public void write(char c[], int off, int len)
 
500
         throws IOException
 
501
   {
 
502
 
 
503
      if(suspended)
 
504
      {
 
505
         return;
 
506
      }
 
507
 
 
508
      state = CHAR_STATE;
 
509
 
 
510
      cb.append(c, off, len);
 
511
      charsWritten += len;
 
512
 
 
513
   }
 
514
 
 
515
 
 
516
   public void write(StringBuffer sb)
 
517
         throws IOException
 
518
   {
 
519
 
 
520
      if(suspended)
 
521
      {
 
522
         return;
 
523
      }
 
524
 
 
525
      state = CHAR_STATE;
 
526
 
 
527
      int len = sb.length();
 
528
      charsWritten += len;
 
529
      cb.append(sb);
 
530
 
 
531
   }
 
532
 
 
533
 
 
534
   /**
 
535
    * Append a string to the buffer
 
536
    */
 
537
   public void write(String s, int off, int len)
 
538
         throws IOException
 
539
   {
 
540
 
 
541
      if(suspended)
 
542
      {
 
543
         return;
 
544
      }
 
545
 
 
546
      state = CHAR_STATE;
 
547
 
 
548
      charsWritten += len;
 
549
      if(s == null)
 
550
      {
 
551
         s = "null";
 
552
      }
 
553
      cb.append(s, off, len);
 
554
 
 
555
   }
 
556
 
 
557
 
 
558
   public void write(String s)
 
559
         throws IOException
 
560
   {
 
561
 
 
562
      if(suspended)
 
563
      {
 
564
         return;
 
565
      }
 
566
 
 
567
      state = CHAR_STATE;
 
568
      if(s == null)
 
569
      {
 
570
         s = "null";
 
571
      }
 
572
      write(s, 0, s.length());
 
573
 
 
574
   }
 
575
 
 
576
 
 
577
   public void flushChars()
 
578
         throws IOException
 
579
   {
 
580
 
 
581
      cb.flushBuffer();
 
582
      state = BYTE_STATE;
 
583
 
 
584
   }
 
585
 
 
586
 
 
587
   public boolean flushCharsNeeded()
 
588
   {
 
589
      return state == CHAR_STATE;
 
590
   }
 
591
 
 
592
 
 
593
   public void setEncoding(String s)
 
594
   {
 
595
      enc = s;
 
596
   }
 
597
 
 
598
 
 
599
   public void realWriteChars(char c[], int off, int len)
 
600
         throws IOException
 
601
   {
 
602
 
 
603
      if(!gotEnc)
 
604
      {
 
605
         setConverter();
 
606
      }
 
607
 
 
608
      // The following has been updated to conform to jbossweb 2.1.
 
609
      cb.setChars(c, off, len);
 
610
      while (cb.getLength() > 0) {
 
611
          conv.setByteChunk(bb);
 
612
          conv.convert(cb.getChars(), 0, cb.getLength());
 
613
          if (cb.getLength() > 0) {
 
614
              bb.flushBuffer();
 
615
          }
 
616
      }
 
617
 
 
618
   }
 
619
 
 
620
 
 
621
   public void checkConverter()
 
622
         throws IOException
 
623
   {
 
624
 
 
625
      if(!gotEnc)
 
626
      {
 
627
         setConverter();
 
628
      }
 
629
 
 
630
   }
 
631
 
 
632
 
 
633
   protected void setConverter()
 
634
         throws IOException
 
635
   {
 
636
 
 
637
      if(coyoteResponse != null)
 
638
      {
 
639
         enc = coyoteResponse.getCharacterEncoding();
 
640
      }
 
641
 
 
642
      gotEnc = true;
 
643
      if(enc == null)
 
644
      {
 
645
         enc = DEFAULT_ENCODING;
 
646
      }
 
647
      conv = (C2BConverter) encoders.get(enc);
 
648
      
 
649
      // The following has been updated to conform to jbossweb 2.1.
 
650
      if(conv == null)
 
651
      {
 
652
 
 
653
         if(!SecurityUtility.skipAccessControl())
 
654
         {
 
655
            try
 
656
            {
 
657
               conv = (C2BConverter) AccessController.doPrivileged(
 
658
                     new PrivilegedExceptionAction()
 
659
                     {
 
660
 
 
661
                        public Object run() throws IOException
 
662
                        {
 
663
                           return new C2BConverter(enc);
 
664
                        }
 
665
 
 
666
                     }
 
667
               );
 
668
            }
 
669
            catch(PrivilegedActionException ex)
 
670
            {
 
671
               Exception e = ex.getException();
 
672
               if(e instanceof IOException)
 
673
               {
 
674
                  throw (IOException) e;
 
675
               }
 
676
            }
 
677
         }
 
678
         else
 
679
         {
 
680
            conv = new C2BConverter(enc);
 
681
         }
 
682
 
 
683
         encoders.put(enc, conv);
 
684
 
 
685
      }
 
686
   }
 
687
 
 
688
   // --------------------  BufferedOutputStream compatibility
 
689
 
 
690
 
 
691
   /**
 
692
    * Real write - this buffer will be sent to the client
 
693
    */
 
694
   public void flushBytes()
 
695
         throws IOException
 
696
   {
 
697
 
 
698
      bb.flushBuffer();
 
699
 
 
700
   }
 
701
 
 
702
 
 
703
   public int getBytesWritten()
 
704
   {
 
705
      return bytesWritten;
 
706
   }
 
707
 
 
708
 
 
709
   public int getCharsWritten()
 
710
   {
 
711
      return charsWritten;
 
712
   }
 
713
 
 
714
 
 
715
   public int getContentWritten()
 
716
   {
 
717
      return bytesWritten + charsWritten;
 
718
   }
 
719
 
 
720
 
 
721
   /**
 
722
    * True if this buffer hasn't been used ( since recycle() ) -
 
723
    * i.e. no chars or bytes have been added to the buffer.
 
724
    */
 
725
   public boolean isNew()
 
726
   {
 
727
      return (bytesWritten == 0) && (charsWritten == 0);
 
728
   }
 
729
 
 
730
 
 
731
   public void setBufferSize(int size)
 
732
   {
 
733
      if(size > bb.getLimit())
 
734
      {// ??????
 
735
         bb.setLimit(size);
 
736
      }
 
737
   }
 
738
 
 
739
 
 
740
   public void reset()
 
741
   {
 
742
 
 
743
      //count=0;
 
744
      bb.recycle();
 
745
      bytesWritten = 0;
 
746
      cb.recycle();
 
747
      charsWritten = 0;
 
748
      gotEnc = false;
 
749
      enc = null;
 
750
      state = INITIAL_STATE;
 
751
   }
 
752
 
 
753
 
 
754
   public int getBufferSize()
 
755
   {
 
756
      return bb.getLimit();
 
757
   }
 
758
 
 
759
 
 
760
}