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.jasper.runtime;
19
20 import java.io.CharArrayReader;
21 import java.io.IOException;
22 import java.io.Reader;
23 import java.io.Writer;
24 import java.security.AccessController;
25 import java.security.PrivilegedAction;
26
27 import javax.servlet.jsp.JspWriter;
28 import javax.servlet.jsp.tagext.BodyContent;
29
30 import org.apache.jasper.Constants;
31 import org.apache.jasper.compiler.Localizer;
32
33 /**
34 * Write text to a character-output stream, buffering characters so as
35 * to provide for the efficient writing of single characters, arrays,
36 * and strings.
37 *
38 * Provide support for discarding for the output that has been buffered.
39 *
40 * @author Rajiv Mordani
41 * @author Jan Luehe
42 */
43 public class BodyContentImpl extends BodyContent {
44
45 private static final boolean LIMIT_BUFFER;
46 private static final int TAG_BUFFER_SIZE;
47
48 static {
49 if (System.getSecurityManager() == null) {
50 LIMIT_BUFFER = Boolean.parseBoolean(System.getProperty(
51 "org.apache.jasper.runtime.BodyContentImpl.LIMIT_BUFFER", "false"));
52 TAG_BUFFER_SIZE = Integer.getInteger(
53 "org.apache.jasper.runtime.BodyContentImpl.BUFFER_SIZE",
54 Constants.DEFAULT_TAG_BUFFER_SIZE).intValue();
55 } else {
56 LIMIT_BUFFER = AccessController.doPrivileged(
57 new PrivilegedAction<Boolean>() {
58 @Override
59 public Boolean run() {
60 return Boolean.valueOf(System.getProperty(
61 "org.apache.jasper.runtime.BodyContentImpl.LIMIT_BUFFER",
62 "false"));
63 }
64 }
65 ).booleanValue();
66 TAG_BUFFER_SIZE = AccessController.doPrivileged(
67 new PrivilegedAction<Integer>() {
68 @Override
69 public Integer run() {
70 return Integer.getInteger(
71 "org.apache.jasper.runtime.BodyContentImpl.BUFFER_SIZE",
72 Constants.DEFAULT_TAG_BUFFER_SIZE);
73 }
74 }
75 ).intValue();
76 }
77 }
78
79
80 private char[] cb;
81 private int nextChar;
82 private boolean closed;
83
84 /**
85 * Enclosed writer to which any output is written
86 */
87 private Writer writer;
88
89 /**
90 * Constructor.
91 * @param enclosingWriter The wrapped writer
92 */
93 public BodyContentImpl(JspWriter enclosingWriter) {
94 super(enclosingWriter);
95 cb = new char[TAG_BUFFER_SIZE];
96 bufferSize = cb.length;
97 nextChar = 0;
98 closed = false;
99 }
100
101 /**
102 * Write a single character.
103 * @param c The char to write
104 * @throws IOException Error writing to wrapped writer
105 */
106 @Override
107 public void write(int c) throws IOException {
108 if (writer != null) {
109 writer.write(c);
110 } else {
111 ensureOpen();
112 if (nextChar >= bufferSize) {
113 reAllocBuff (1);
114 }
115 cb[nextChar++] = (char) c;
116 }
117 }
118
119 /**
120 * Write a portion of an array of characters.
121 *
122 * <p> Ordinarily this method stores characters from the given array into
123 * this stream's buffer, flushing the buffer to the underlying stream as
124 * needed. If the requested length is at least as large as the buffer,
125 * however, then this method will flush the buffer and write the characters
126 * directly to the underlying stream. Thus redundant
127 * <code>DiscardableBufferedWriter</code>s will not copy data
128 * unnecessarily.
129 *
130 * @param cbuf A character array
131 * @param off Offset from which to start reading characters
132 * @param len Number of characters to write
133 * @throws IOException Error writing to wrapped writer
134 */
135 @Override
136 public void write(char[] cbuf, int off, int len) throws IOException {
137 if (writer != null) {
138 writer.write(cbuf, off, len);
139 } else {
140 ensureOpen();
141
142 if ((off < 0) || (off > cbuf.length) || (len < 0) ||
143 ((off + len) > cbuf.length) || ((off + len) < 0)) {
144 throw new IndexOutOfBoundsException();
145 } else if (len == 0) {
146 return;
147 }
148
149 if (len >= bufferSize - nextChar)
150 reAllocBuff (len);
151
152 System.arraycopy(cbuf, off, cb, nextChar, len);
153 nextChar+=len;
154 }
155 }
156
157 /**
158 * Write an array of characters. This method cannot be inherited from the
159 * Writer class because it must suppress I/O exceptions.
160 * @param buf Content to write
161 * @throws IOException Error writing to wrapped writer
162 */
163 @Override
164 public void write(char[] buf) throws IOException {
165 if (writer != null) {
166 writer.write(buf);
167 } else {
168 write(buf, 0, buf.length);
169 }
170 }
171
172 /**
173 * Write a portion of a String.
174 *
175 * @param s String to be written
176 * @param off Offset from which to start reading characters
177 * @param len Number of characters to be written
178 * @throws IOException Error writing to wrapped writer
179 */
180 @Override
181 public void write(String s, int off, int len) throws IOException {
182 if (writer != null) {
183 writer.write(s, off, len);
184 } else {
185 ensureOpen();
186 if (len >= bufferSize - nextChar)
187 reAllocBuff(len);
188
189 s.getChars(off, off + len, cb, nextChar);
190 nextChar += len;
191 }
192 }
193
194 /**
195 * Write a string. This method cannot be inherited from the Writer class
196 * because it must suppress I/O exceptions.
197 * @param s String to be written
198 * @throws IOException Error writing to wrapped writer
199 */
200 @Override
201 public void write(String s) throws IOException {
202 if (writer != null) {
203 writer.write(s);
204 } else {
205 write(s, 0, s.length());
206 }
207 }
208
209 /**
210 * Write a line separator. The line separator string is defined by the
211 * system property <code>line.separator</code>, and is not necessarily a
212 * single newline ('\n') character.
213 *
214 * @throws IOException Error writing to wrapped writer
215 */
216 @Override
217 public void newLine() throws IOException {
218 if (writer != null) {
219 writer.write(System.lineSeparator());
220 } else {
221 write(System.lineSeparator());
222 }
223 }
224
225 /**
226 * Print a boolean value. The string produced by <code>{@link
227 * java.lang.String#valueOf(boolean)}</code> is translated into bytes
228 * according to the platform's default character encoding, and these bytes
229 * are written in exactly the manner of the <code>{@link
230 * #write(int)}</code> method.
231 *
232 * @param b The <code>boolean</code> to be printed
233 * @throws IOException Error writing to wrapped writer
234 */
235 @Override
236 public void print(boolean b) throws IOException {
237 if (writer != null) {
238 writer.write(b ? "true" : "false");
239 } else {
240 write(b ? "true" : "false");
241 }
242 }
243
244 /**
245 * Print a character. The character is translated into one or more bytes
246 * according to the platform's default character encoding, and these bytes
247 * are written in exactly the manner of the <code>{@link
248 * #write(int)}</code> method.
249 *
250 * @param c The <code>char</code> to be printed
251 * @throws IOException Error writing to wrapped writer
252 */
253 @Override
254 public void print(char c) throws IOException {
255 if (writer != null) {
256 writer.write(String.valueOf(c));
257 } else {
258 write(String.valueOf(c));
259 }
260 }
261
262 /**
263 * Print an integer. The string produced by <code>{@link
264 * java.lang.String#valueOf(int)}</code> is translated into bytes according
265 * to the platform's default character encoding, and these bytes are
266 * written in exactly the manner of the <code>{@link #write(int)}</code>
267 * method.
268 *
269 * @param i The <code>int</code> to be printed
270 * @throws IOException Error writing to wrapped writer
271 */
272 @Override
273 public void print(int i) throws IOException {
274 if (writer != null) {
275 writer.write(String.valueOf(i));
276 } else {
277 write(String.valueOf(i));
278 }
279 }
280
281 /**
282 * Print a long integer. The string produced by <code>{@link
283 * java.lang.String#valueOf(long)}</code> is translated into bytes
284 * according to the platform's default character encoding, and these bytes
285 * are written in exactly the manner of the
286 * <code>{@link #write(int)}</code> method.
287 *
288 * @param l The <code>long</code> to be printed
289 * @throws IOException Error writing to wrapped writer
290 */
291 @Override
292 public void print(long l) throws IOException {
293 if (writer != null) {
294 writer.write(String.valueOf(l));
295 } else {
296 write(String.valueOf(l));
297 }
298 }
299
300 /**
301 * Print a floating-point number. The string produced by <code>{@link
302 * java.lang.String#valueOf(float)}</code> is translated into bytes
303 * according to the platform's default character encoding, and these bytes
304 * are written in exactly the manner of the
305 * <code>{@link #write(int)}</code> method.
306 *
307 * @param f The <code>float</code> to be printed
308 * @throws IOException Error writing to wrapped writer
309 */
310 @Override
311 public void print(float f) throws IOException {
312 if (writer != null) {
313 writer.write(String.valueOf(f));
314 } else {
315 write(String.valueOf(f));
316 }
317 }
318
319 /**
320 * Print a double-precision floating-point number. The string produced by
321 * <code>{@link java.lang.String#valueOf(double)}</code> is translated into
322 * bytes according to the platform's default character encoding, and these
323 * bytes are written in exactly the manner of the <code>{@link
324 * #write(int)}</code> method.
325 *
326 * @param d The <code>double</code> to be printed
327 * @throws IOException Error writing to wrapped writer
328 */
329 @Override
330 public void print(double d) throws IOException {
331 if (writer != null) {
332 writer.write(String.valueOf(d));
333 } else {
334 write(String.valueOf(d));
335 }
336 }
337
338 /**
339 * Print an array of characters. The characters are converted into bytes
340 * according to the platform's default character encoding, and these bytes
341 * are written in exactly the manner of the
342 * <code>{@link #write(int)}</code> method.
343 *
344 * @param s The array of chars to be printed
345 *
346 * @throws NullPointerException If <code>s</code> is <code>null</code>
347 * @throws IOException Error writing to wrapped writer
348 */
349 @Override
350 public void print(char[] s) throws IOException {
351 if (writer != null) {
352 writer.write(s);
353 } else {
354 write(s);
355 }
356 }
357
358 /**
359 * Print a string. If the argument is <code>null</code> then the string
360 * <code>"null"</code> is printed. Otherwise, the string's characters are
361 * converted into bytes according to the platform's default character
362 * encoding, and these bytes are written in exactly the manner of the
363 * <code>{@link #write(int)}</code> method.
364 *
365 * @param s The <code>String</code> to be printed
366 * @throws IOException Error writing to wrapped writer
367 */
368 @Override
369 public void print(String s) throws IOException {
370 if (s == null) s = "null";
371 if (writer != null) {
372 writer.write(s);
373 } else {
374 write(s);
375 }
376 }
377
378 /**
379 * Print an object. The string produced by the <code>{@link
380 * java.lang.String#valueOf(Object)}</code> method is translated into bytes
381 * according to the platform's default character encoding, and these bytes
382 * are written in exactly the manner of the
383 * <code>{@link #write(int)}</code> method.
384 *
385 * @param obj The <code>Object</code> to be printed
386 * @throws IOException Error writing to wrapped writer
387 */
388 @Override
389 public void print(Object obj) throws IOException {
390 if (writer != null) {
391 writer.write(String.valueOf(obj));
392 } else {
393 write(String.valueOf(obj));
394 }
395 }
396
397 /**
398 * Terminate the current line by writing the line separator string. The
399 * line separator string is defined by the system property
400 * <code>line.separator</code>, and is not necessarily a single newline
401 * character (<code>'\n'</code>).
402 *
403 * @throws IOException Error writing to wrapped writer
404 */
405 @Override
406 public void println() throws IOException {
407 newLine();
408 }
409
410 /**
411 * Print a boolean value and then terminate the line. This method behaves
412 * as though it invokes <code>{@link #print(boolean)}</code> and then
413 * <code>{@link #println()}</code>.
414 *
415 * @param x The <code>boolean</code> to be printed
416 * @throws IOException Error writing to wrapped writer
417 */
418 @Override
419 public void println(boolean x) throws IOException {
420 print(x);
421 println();
422 }
423
424 /**
425 * Print a character and then terminate the line. This method behaves as
426 * though it invokes <code>{@link #print(char)}</code> and then
427 * <code>{@link #println()}</code>.
428 *
429 * @param x The <code>char</code> to be printed
430 * @throws IOException Error writing to wrapped writer
431 */
432 @Override
433 public void println(char x) throws IOException {
434 print(x);
435 println();
436 }
437
438 /**
439 * Print an integer and then terminate the line. This method behaves as
440 * though it invokes <code>{@link #print(int)}</code> and then
441 * <code>{@link #println()}</code>.
442 *
443 * @param x The <code>int</code> to be printed
444 * @throws IOException Error writing to wrapped writer
445 */
446 @Override
447 public void println(int x) throws IOException {
448 print(x);
449 println();
450 }
451
452 /**
453 * Print a long integer and then terminate the line. This method behaves
454 * as though it invokes <code>{@link #print(long)}</code> and then
455 * <code>{@link #println()}</code>.
456 *
457 * @param x The <code>long</code> to be printed
458 * @throws IOException Error writing to wrapped writer
459 */
460 @Override
461 public void println(long x) throws IOException {
462 print(x);
463 println();
464 }
465
466 /**
467 * Print a floating-point number and then terminate the line. This method
468 * behaves as though it invokes <code>{@link #print(float)}</code> and then
469 * <code>{@link #println()}</code>.
470 *
471 * @param x The <code>float</code> to be printed
472 * @throws IOException Error writing to wrapped writer
473 */
474 @Override
475 public void println(float x) throws IOException {
476 print(x);
477 println();
478 }
479
480 /**
481 * Print a double-precision floating-point number and then terminate the
482 * line. This method behaves as though it invokes <code>{@link
483 * #print(double)}</code> and then <code>{@link #println()}</code>.
484 *
485 * @param x The <code>double</code> to be printed
486 * @throws IOException Error writing to wrapped writer
487 */
488 @Override
489 public void println(double x) throws IOException{
490 print(x);
491 println();
492 }
493
494 /**
495 * Print an array of characters and then terminate the line. This method
496 * behaves as though it invokes <code>{@link #print(char[])}</code> and
497 * then <code>{@link #println()}</code>.
498 *
499 * @param x The <code>char</code> array to be printed
500 * @throws IOException Error writing to wrapped writer
501 */
502 @Override
503 public void println(char x[]) throws IOException {
504 print(x);
505 println();
506 }
507
508 /**
509 * Print a String and then terminate the line. This method behaves as
510 * though it invokes <code>{@link #print(String)}</code> and then
511 * <code>{@link #println()}</code>.
512 *
513 * @param x The string to be printed
514 * @throws IOException Error writing to wrapped writer
515 */
516 @Override
517 public void println(String x) throws IOException {
518 print(x);
519 println();
520 }
521
522 /**
523 * Print an Object and then terminate the line. This method behaves as
524 * though it invokes <code>{@link #print(Object)}</code> and then
525 * <code>{@link #println()}</code>.
526 *
527 * @param x The object to be printed
528 * @throws IOException Error writing to wrapped writer
529 */
530 @Override
531 public void println(Object x) throws IOException {
532 print(x);
533 println();
534 }
535
536 /**
537 * Clear the contents of the buffer. If the buffer has been already
538 * been flushed then the clear operation shall throw an IOException
539 * to signal the fact that some data has already been irrevocably
540 * written to the client response stream.
541 *
542 * @throws IOException If there is no wrapped writer
543 */
544 @Override
545 public void clear() throws IOException {
546 if (writer != null) {
547 throw new IOException();
548 } else {
549 nextChar = 0;
550 if (LIMIT_BUFFER && (cb.length > TAG_BUFFER_SIZE)) {
551 cb = new char[TAG_BUFFER_SIZE];
552 bufferSize = cb.length;
553 }
554 }
555 }
556
557 /**
558 * Clears the current contents of the buffer. Unlike clear(), this
559 * method will not throw an IOException if the buffer has already been
560 * flushed. It merely clears the current content of the buffer and
561 * returns.
562 *
563 * @throws IOException Should not happen
564 */
565 @Override
566 public void clearBuffer() throws IOException {
567 if (writer == null) {
568 this.clear();
569 }
570 }
571
572 /**
573 * Close the stream, flushing it first. Once a stream has been closed,
574 * further write() or flush() invocations will cause an IOException to be
575 * thrown. Closing a previously-closed stream, however, has no effect.
576 *
577 * @throws IOException Error writing to wrapped writer
578 */
579 @Override
580 public void close() throws IOException {
581 if (writer != null) {
582 writer.close();
583 } else {
584 closed = true;
585 }
586 }
587
588 /**
589 * This method returns the size of the buffer used by the JspWriter.
590 *
591 * @return the size of the buffer in bytes, or 0 is unbuffered.
592 */
593 @Override
594 public int getBufferSize() {
595 // According to the spec, the JspWriter returned by
596 // JspContext.pushBody(java.io.Writer writer) must behave as
597 // though it were unbuffered. This means that its getBufferSize()
598 // must always return 0.
599 return (writer == null) ? bufferSize : 0;
600 }
601
602 /**
603 * @return the number of bytes unused in the buffer
604 */
605 @Override
606 public int getRemaining() {
607 return (writer == null) ? bufferSize-nextChar : 0;
608 }
609
610 /**
611 * Return the value of this BodyJspWriter as a Reader.
612 * Note: this is after evaluation!! There are no scriptlets,
613 * etc in this stream.
614 *
615 * @return the value of this BodyJspWriter as a Reader
616 */
617 @Override
618 public Reader getReader() {
619 return (writer == null) ? new CharArrayReader (cb, 0, nextChar) : null;
620 }
621
622 /**
623 * Return the value of the BodyJspWriter as a String.
624 * Note: this is after evaluation!! There are no scriptlets,
625 * etc in this stream.
626 *
627 * @return the value of the BodyJspWriter as a String
628 */
629 @Override
630 public String getString() {
631 return (writer == null) ? new String(cb, 0, nextChar) : null;
632 }
633
634 /**
635 * Write the contents of this BodyJspWriter into a Writer.
636 * Subclasses are likely to do interesting things with the
637 * implementation so some things are extra efficient.
638 *
639 * @param out The writer into which to place the contents of this body
640 * evaluation
641 * @throws IOException Error writing to writer
642 */
643 @Override
644 public void writeOut(Writer out) throws IOException {
645 if (writer == null) {
646 out.write(cb, 0, nextChar);
647 // Flush not called as the writer passed could be a BodyContent and
648 // it doesn't allow to flush.
649 }
650 }
651
652 /**
653 * Sets the writer to which all output is written.
654 */
655 void setWriter(Writer writer) {
656 this.writer = writer;
657 closed = false;
658 if (writer == null) {
659 clearBody();
660 }
661 }
662
663 /**
664 * This method shall "reset" the internal state of a BodyContentImpl,
665 * releasing all internal references, and preparing it for potential
666 * reuse by a later invocation of {@link PageContextImpl#pushBody(Writer)}.
667 *
668 * <p>Note, that BodyContentImpl instances are usually owned by a
669 * PageContextImpl instance, and PageContextImpl instances are recycled
670 * and reused.
671 *
672 * @see PageContextImpl#release()
673 */
674 protected void recycle() {
675 this.writer = null;
676 try {
677 this.clear();
678 } catch (IOException ex) {
679 // ignore
680 }
681 }
682
683 private void ensureOpen() throws IOException {
684 if (closed) {
685 throw new IOException(Localizer.getMessage("jsp.error.stream.closed"));
686 }
687 }
688
689 /**
690 * Reallocates buffer since the spec requires it to be unbounded.
691 */
692 private void reAllocBuff(int len) {
693
694 if (bufferSize + len <= cb.length) {
695 bufferSize = cb.length;
696 return;
697 }
698
699 if (len < cb.length) {
700 len = cb.length;
701 }
702
703 char[] tmp = new char[cb.length + len];
704 System.arraycopy(cb, 0, tmp, 0, cb.length);
705 cb = tmp;
706 bufferSize = cb.length;
707 }
708
709
710 }
711