View Javadoc

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.struts2.jasper.runtime;
19  
20  import org.apache.struts2.jasper.Constants;
21  import org.apache.struts2.jasper.compiler.Localizer;
22  import org.apache.struts2.jasper.security.SecurityUtil;
23  
24  import javax.servlet.ServletResponse;
25  import javax.servlet.jsp.JspWriter;
26  import java.io.IOException;
27  import java.io.Writer;
28  import java.security.AccessController;
29  import java.security.PrivilegedAction;
30  
31  /***
32   * Write text to a character-output stream, buffering characters so as
33   * to provide for the efficient writing of single characters, arrays,
34   * and strings.
35   * <p/>
36   * Provide support for discarding for the output that has been
37   * buffered.
38   * <p/>
39   * This needs revisiting when the buffering problems in the JSP spec
40   * are fixed -akv
41   *
42   * @author Anil K. Vijendran
43   */
44  public class JspWriterImpl extends JspWriter {
45  
46      private Writer out;
47      private ServletResponse response;
48      private char cb[];
49      private int nextChar;
50      private boolean flushed = false;
51      private boolean closed = false;
52  
53      public JspWriterImpl() {
54          super(Constants.DEFAULT_BUFFER_SIZE, true);
55      }
56  
57      /***
58       * Create a buffered character-output stream that uses a default-sized
59       * output buffer.
60       *
61       * @param response A Servlet Response
62       */
63      public JspWriterImpl(ServletResponse response) {
64          this(response, Constants.DEFAULT_BUFFER_SIZE, true);
65      }
66  
67      /***
68       * Create a new buffered character-output stream that uses an output
69       * buffer of the given size.
70       *
71       * @param response A Servlet Response
72       * @param sz       Output-buffer size, a positive integer
73       * @throws IllegalArgumentException If sz is <= 0
74       */
75      public JspWriterImpl(ServletResponse response, int sz,
76                           boolean autoFlush) {
77          super(sz, autoFlush);
78          if (sz < 0)
79              throw new IllegalArgumentException("Buffer size <= 0");
80          this.response = response;
81          cb = sz == 0 ? null : new char[sz];
82          nextChar = 0;
83      }
84  
85      void init(ServletResponse response, int sz, boolean autoFlush) {
86          this.response = response;
87          if (sz > 0 && (cb == null || sz > cb.length))
88              cb = new char[sz];
89          nextChar = 0;
90          this.autoFlush = autoFlush;
91          this.bufferSize = sz;
92      }
93  
94      /***
95       * Package-level access
96       */
97      void recycle() {
98          flushed = false;
99          closed = false;
100         out = null;
101         nextChar = 0;
102         response = null;
103     }
104 
105     /***
106      * Flush the output buffer to the underlying character stream, without
107      * flushing the stream itself.  This method is non-private only so that it
108      * may be invoked by PrintStream.
109      */
110     protected final void flushBuffer() throws IOException {
111         if (bufferSize == 0)
112             return;
113         flushed = true;
114         ensureOpen();
115         if (nextChar == 0)
116             return;
117         initOut();
118         out.write(cb, 0, nextChar);
119         nextChar = 0;
120     }
121 
122     private void initOut() throws IOException {
123         if (out == null) {
124             out = response.getWriter();
125         }
126     }
127 
128     private String getLocalizeMessage(final String message) {
129         if (SecurityUtil.isPackageProtectionEnabled()) {
130             return (String) AccessController.doPrivileged(new PrivilegedAction() {
131                 public Object run() {
132                     return Localizer.getMessage(message);
133                 }
134             });
135         } else {
136             return Localizer.getMessage(message);
137         }
138     }
139 
140     /***
141      * Discard the output buffer.
142      */
143     public final void clear() throws IOException {
144         if ((bufferSize == 0) && (out != null))
145             // clear() is illegal after any unbuffered output (JSP.5.5)
146             throw new IllegalStateException(
147                     getLocalizeMessage("jsp.error.ise_on_clear"));
148         if (flushed)
149             throw new IOException(
150                     getLocalizeMessage("jsp.error.attempt_to_clear_flushed_buffer"));
151         ensureOpen();
152         nextChar = 0;
153     }
154 
155     public void clearBuffer() throws IOException {
156         if (bufferSize == 0)
157             throw new IllegalStateException(
158                     getLocalizeMessage("jsp.error.ise_on_clear"));
159         ensureOpen();
160         nextChar = 0;
161     }
162 
163     private final void bufferOverflow() throws IOException {
164         throw new IOException(getLocalizeMessage("jsp.error.overflow"));
165     }
166 
167     /***
168      * Flush the stream.
169      */
170     public void flush() throws IOException {
171         flushBuffer();
172         if (out != null) {
173             out.flush();
174         }
175     }
176 
177     /***
178      * Close the stream.
179      */
180     public void close() throws IOException {
181         if (response == null || closed)
182             // multiple calls to close is OK
183             return;
184         flush();
185         if (out != null)
186             out.close();
187         out = null;
188         closed = true;
189     }
190 
191     /***
192      * @return the number of bytes unused in the buffer
193      */
194     public int getRemaining() {
195         return bufferSize - nextChar;
196     }
197 
198     /***
199      * check to make sure that the stream has not been closed
200      */
201     private void ensureOpen() throws IOException {
202         if (response == null || closed)
203             throw new IOException("Stream closed");
204     }
205 
206 
207     /***
208      * Write a single character.
209      */
210     public void write(int c) throws IOException {
211         ensureOpen();
212         if (bufferSize == 0) {
213             initOut();
214             out.write(c);
215         } else {
216             if (nextChar >= bufferSize)
217                 if (autoFlush)
218                     flushBuffer();
219                 else
220                     bufferOverflow();
221             cb[nextChar++] = (char) c;
222         }
223     }
224 
225     /***
226      * Our own little min method, to avoid loading java.lang.Math if we've run
227      * out of file descriptors and we're trying to print a stack trace.
228      */
229     private int min(int a, int b) {
230         if (a < b) return a;
231         return b;
232     }
233 
234     /***
235      * Write a portion of an array of characters.
236      * <p/>
237      * <p> Ordinarily this method stores characters from the given array into
238      * this stream's buffer, flushing the buffer to the underlying stream as
239      * needed.  If the requested length is at least as large as the buffer,
240      * however, then this method will flush the buffer and write the characters
241      * directly to the underlying stream.  Thus redundant
242      * <code>DiscardableBufferedWriter</code>s will not copy data unnecessarily.
243      *
244      * @param cbuf A character array
245      * @param off  Offset from which to start reading characters
246      * @param len  Number of characters to write
247      */
248     public void write(char cbuf[], int off, int len)
249             throws IOException {
250         ensureOpen();
251 
252         if (bufferSize == 0) {
253             initOut();
254             out.write(cbuf, off, len);
255             return;
256         }
257 
258         if ((off < 0) || (off > cbuf.length) || (len < 0) ||
259                 ((off + len) > cbuf.length) || ((off + len) < 0)) {
260             throw new IndexOutOfBoundsException();
261         } else if (len == 0) {
262             return;
263         }
264 
265         if (len >= bufferSize) {
266             /* If the request length exceeds the size of the output buffer,
267              flush the buffer and then write the data directly.  In this
268              way buffered streams will cascade harmlessly. */
269             if (autoFlush)
270                 flushBuffer();
271             else
272                 bufferOverflow();
273             initOut();
274             out.write(cbuf, off, len);
275             return;
276         }
277 
278         int b = off, t = off + len;
279         while (b < t) {
280             int d = min(bufferSize - nextChar, t - b);
281             System.arraycopy(cbuf, b, cb, nextChar, d);
282             b += d;
283             nextChar += d;
284             if (nextChar >= bufferSize)
285                 if (autoFlush)
286                     flushBuffer();
287                 else
288                     bufferOverflow();
289         }
290 
291     }
292 
293     /***
294      * Write an array of characters.  This method cannot be inherited from the
295      * Writer class because it must suppress I/O exceptions.
296      */
297     public void write(char buf[]) throws IOException {
298         write(buf, 0, buf.length);
299     }
300 
301     /***
302      * Write a portion of a String.
303      *
304      * @param s   String to be written
305      * @param off Offset from which to start reading characters
306      * @param len Number of characters to be written
307      */
308     public void write(String s, int off, int len) throws IOException {
309         ensureOpen();
310         if (bufferSize == 0) {
311             initOut();
312             out.write(s, off, len);
313             return;
314         }
315         int b = off, t = off + len;
316         while (b < t) {
317             int d = min(bufferSize - nextChar, t - b);
318             s.getChars(b, b + d, cb, nextChar);
319             b += d;
320             nextChar += d;
321             if (nextChar >= bufferSize)
322                 if (autoFlush)
323                     flushBuffer();
324                 else
325                     bufferOverflow();
326         }
327     }
328 
329     /***
330      * Write a string.  This method cannot be inherited from the Writer class
331      * because it must suppress I/O exceptions.
332      */
333     public void write(String s) throws IOException {
334         // Simple fix for Bugzilla 35410
335         // Calling the other write function so as to init the buffer anyways
336         if (s == null) {
337             write(s, 0, 0);
338         } else {
339             write(s, 0, s.length());
340         }
341     }
342 
343 
344     static String lineSeparator = System.getProperty("line.separator");
345 
346     /***
347      * Write a line separator.  The line separator string is defined by the
348      * system property <tt>line.separator</tt>, and is not necessarily a single
349      * newline ('\n') character.
350      *
351      * @throws IOException If an I/O error occurs
352      */
353 
354     public void newLine() throws IOException {
355         write(lineSeparator);
356     }
357 
358 
359     /* Methods that do not terminate lines */
360 
361     /***
362      * Print a boolean value.  The string produced by <code>{@link
363      * java.lang.String#valueOf(boolean)}</code> is translated into bytes
364      * according to the platform's default character encoding, and these bytes
365      * are written in exactly the manner of the <code>{@link
366      * #write(int)}</code> method.
367      *
368      * @param b The <code>boolean</code> to be printed
369      */
370     public void print(boolean b) throws IOException {
371         write(b ? "true" : "false");
372     }
373 
374     /***
375      * Print a character.  The character is translated into one or more bytes
376      * according to the platform's default character encoding, and these bytes
377      * are written in exactly the manner of the <code>{@link
378      * #write(int)}</code> method.
379      *
380      * @param c The <code>char</code> to be printed
381      */
382     public void print(char c) throws IOException {
383         write(String.valueOf(c));
384     }
385 
386     /***
387      * Print an integer.  The string produced by <code>{@link
388      * java.lang.String#valueOf(int)}</code> is translated into bytes according
389      * to the platform's default character encoding, and these bytes are
390      * written in exactly the manner of the <code>{@link #write(int)}</code>
391      * method.
392      *
393      * @param i The <code>int</code> to be printed
394      */
395     public void print(int i) throws IOException {
396         write(String.valueOf(i));
397     }
398 
399     /***
400      * Print a long integer.  The string produced by <code>{@link
401      * java.lang.String#valueOf(long)}</code> is translated into bytes
402      * according to the platform's default character encoding, and these bytes
403      * are written in exactly the manner of the <code>{@link #write(int)}</code>
404      * method.
405      *
406      * @param l The <code>long</code> to be printed
407      */
408     public void print(long l) throws IOException {
409         write(String.valueOf(l));
410     }
411 
412     /***
413      * Print a floating-point number.  The string produced by <code>{@link
414      * java.lang.String#valueOf(float)}</code> is translated into bytes
415      * according to the platform's default character encoding, and these bytes
416      * are written in exactly the manner of the <code>{@link #write(int)}</code>
417      * method.
418      *
419      * @param f The <code>float</code> to be printed
420      */
421     public void print(float f) throws IOException {
422         write(String.valueOf(f));
423     }
424 
425     /***
426      * Print a double-precision floating-point number.  The string produced by
427      * <code>{@link java.lang.String#valueOf(double)}</code> is translated into
428      * bytes according to the platform's default character encoding, and these
429      * bytes are written in exactly the manner of the <code>{@link
430      * #write(int)}</code> method.
431      *
432      * @param d The <code>double</code> to be printed
433      */
434     public void print(double d) throws IOException {
435         write(String.valueOf(d));
436     }
437 
438     /***
439      * Print an array of characters.  The characters are converted into bytes
440      * according to the platform's default character encoding, and these bytes
441      * are written in exactly the manner of the <code>{@link #write(int)}</code>
442      * method.
443      *
444      * @param s The array of chars to be printed
445      * @throws NullPointerException If <code>s</code> is <code>null</code>
446      */
447     public void print(char s[]) throws IOException {
448         write(s);
449     }
450 
451     /***
452      * Print a string.  If the argument is <code>null</code> then the string
453      * <code>"null"</code> is printed.  Otherwise, the string's characters are
454      * converted into bytes according to the platform's default character
455      * encoding, and these bytes are written in exactly the manner of the
456      * <code>{@link #write(int)}</code> method.
457      *
458      * @param s The <code>String</code> to be printed
459      */
460     public void print(String s) throws IOException {
461         if (s == null) {
462             s = "null";
463         }
464         write(s);
465     }
466 
467     /***
468      * Print an object.  The string produced by the <code>{@link
469      * java.lang.String#valueOf(Object)}</code> method is translated into bytes
470      * according to the platform's default character encoding, and these bytes
471      * are written in exactly the manner of the <code>{@link #write(int)}</code>
472      * method.
473      *
474      * @param obj The <code>Object</code> to be printed
475      */
476     public void print(Object obj) throws IOException {
477         write(String.valueOf(obj));
478     }
479 
480     /* Methods that do terminate lines */
481 
482     /***
483      * Terminate the current line by writing the line separator string.  The
484      * line separator string is defined by the system property
485      * <code>line.separator</code>, and is not necessarily a single newline
486      * character (<code>'\n'</code>).
487      * <p/>
488      * Need to change this from PrintWriter because the default
489      * println() writes  to the sink directly instead of through the
490      * write method...
491      */
492     public void println() throws IOException {
493         newLine();
494     }
495 
496     /***
497      * Print a boolean value and then terminate the line.  This method behaves
498      * as though it invokes <code>{@link #print(boolean)}</code> and then
499      * <code>{@link #println()}</code>.
500      */
501     public void println(boolean x) throws IOException {
502         print(x);
503         println();
504     }
505 
506     /***
507      * Print a character and then terminate the line.  This method behaves as
508      * though it invokes <code>{@link #print(char)}</code> and then <code>{@link
509      * #println()}</code>.
510      */
511     public void println(char x) throws IOException {
512         print(x);
513         println();
514     }
515 
516     /***
517      * Print an integer and then terminate the line.  This method behaves as
518      * though it invokes <code>{@link #print(int)}</code> and then <code>{@link
519      * #println()}</code>.
520      */
521     public void println(int x) throws IOException {
522         print(x);
523         println();
524     }
525 
526     /***
527      * Print a long integer and then terminate the line.  This method behaves
528      * as though it invokes <code>{@link #print(long)}</code> and then
529      * <code>{@link #println()}</code>.
530      */
531     public void println(long x) throws IOException {
532         print(x);
533         println();
534     }
535 
536     /***
537      * Print a floating-point number and then terminate the line.  This method
538      * behaves as though it invokes <code>{@link #print(float)}</code> and then
539      * <code>{@link #println()}</code>.
540      */
541     public void println(float x) throws IOException {
542         print(x);
543         println();
544     }
545 
546     /***
547      * Print a double-precision floating-point number and then terminate the
548      * line.  This method behaves as though it invokes <code>{@link
549      * #print(double)}</code> and then <code>{@link #println()}</code>.
550      */
551     public void println(double x) throws IOException {
552         print(x);
553         println();
554     }
555 
556     /***
557      * Print an array of characters and then terminate the line.  This method
558      * behaves as though it invokes <code>{@link #print(char[])}</code> and then
559      * <code>{@link #println()}</code>.
560      */
561     public void println(char x[]) throws IOException {
562         print(x);
563         println();
564     }
565 
566     /***
567      * Print a String and then terminate the line.  This method behaves as
568      * though it invokes <code>{@link #print(String)}</code> and then
569      * <code>{@link #println()}</code>.
570      */
571     public void println(String x) throws IOException {
572         print(x);
573         println();
574     }
575 
576     /***
577      * Print an Object and then terminate the line.  This method behaves as
578      * though it invokes <code>{@link #print(Object)}</code> and then
579      * <code>{@link #println()}</code>.
580      */
581     public void println(Object x) throws IOException {
582         print(x);
583         println();
584     }
585 
586 }