View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.application.jsp;
20  
21  import javax.servlet.ServletOutputStream;
22  import javax.servlet.http.HttpServletResponse;
23  import javax.servlet.http.HttpServletResponseWrapper;
24  
25  import org.apache.myfaces.shared.view.ViewResponseWrapper;
26  
27  import java.io.*;
28  import java.nio.ByteBuffer;
29  import java.nio.CharBuffer;
30  import java.nio.charset.Charset;
31  import java.nio.charset.CharsetDecoder;
32  
33  /**
34   * @author Bruno Aranda (latest modification by $Author: bommel $)
35   * @version $Revision: 1187700 $ $Date: 2011-10-22 07:19:37 -0500 (Sat, 22 Oct 2011) $
36   */
37  public class ServletViewResponseWrapper extends HttpServletResponseWrapper implements ViewResponseWrapper
38  {
39      private PrintWriter _writer;
40      private CharArrayWriter _charArrayWriter;
41      private int _status = HttpServletResponse.SC_OK;
42      private WrappedServletOutputStream _byteArrayWriter;
43  
44      public ServletViewResponseWrapper(HttpServletResponse httpServletResponse)
45      {
46          super(httpServletResponse);
47      }
48  
49      @Override
50      public void sendError(int status) throws IOException
51      {
52          super.sendError(status);
53          _status = status;
54      }
55  
56      @Override
57      public void sendError(int status, String errorMessage) throws IOException
58      {
59          super.sendError(status, errorMessage);
60          _status = status;
61      }
62  
63      @Override
64      public void setStatus(int status)
65      {
66          super.setStatus(status);
67          _status = status;
68      }
69  
70      @Override
71      public void setStatus(int status, String errorMessage)
72      {
73          super.setStatus(status, errorMessage);
74          _status = status;
75      }
76  
77      public int getStatus()
78      {
79          return _status;
80      }
81  
82      public void flushToWrappedResponse() throws IOException
83      {
84          if (_charArrayWriter != null)
85          {
86              _charArrayWriter.writeTo(getResponse().getWriter());
87              _charArrayWriter.reset();
88              _writer.flush();
89          }
90          else if (_byteArrayWriter != null)
91          {
92              // MYFACES-1955 cannot call getWriter() after getOutputStream()
93              // _byteArrayWriter is not null only if getOutputStream() was called
94              // before. This method is called from f:view to flush data before tag
95              // start, or if an error page is flushed after dispatch.
96              // A resource inside /faces/* (see MYFACES-1815) is handled on flushToWriter.
97              // If response.getOuputStream() was called before, an IllegalStateException
98              // is raised on response.getWriter(), so we should try through stream.
99              try
100             {
101                 _byteArrayWriter.writeTo(getResponse().getWriter(), getResponse().getCharacterEncoding());
102             }
103             catch (IllegalStateException e)
104             {
105             getResponse().getOutputStream().write(_byteArrayWriter.toByteArray());
106             }
107             _byteArrayWriter.reset();
108             _byteArrayWriter.flush();
109         }
110     }
111 
112     public void flushToWriter(Writer writer,String encoding) throws IOException
113     {
114         if (_charArrayWriter != null)
115         {
116             _charArrayWriter.writeTo(writer);
117             _charArrayWriter.reset();
118             _writer.flush();
119         }
120         else if (_byteArrayWriter != null)
121         {
122             _byteArrayWriter.writeTo(writer,encoding);
123             _byteArrayWriter.reset();
124             _byteArrayWriter.flush();
125         }
126         writer.flush();
127     }
128 
129     @Override
130     public ServletOutputStream getOutputStream() throws IOException
131     {
132         if (_charArrayWriter != null) throw new IllegalStateException();
133         if (_byteArrayWriter == null)
134         {
135             _byteArrayWriter = new WrappedServletOutputStream();
136         }
137         return _byteArrayWriter;
138     }
139 
140     @Override
141     public PrintWriter getWriter() throws IOException
142     {
143         if (_byteArrayWriter != null) throw new IllegalStateException();
144         if (_writer == null)
145         {
146             _charArrayWriter = new CharArrayWriter(4096);
147             _writer = new PrintWriter(_charArrayWriter);
148         }
149         return _writer;
150     }
151 
152     @Override
153     public void reset()
154     {
155         if (_charArrayWriter != null)
156         {
157             _charArrayWriter.reset();
158         }
159     }
160 
161     @Override
162     public String toString()
163     {
164         if (_charArrayWriter != null)
165         {
166             return _charArrayWriter.toString();
167         }
168         return null;
169     }
170 
171     static class WrappedServletOutputStream extends ServletOutputStream
172     {
173         private WrappedByteArrayOutputStream _byteArrayOutputStream;
174 
175 
176         public WrappedServletOutputStream()
177         {
178             _byteArrayOutputStream = new WrappedByteArrayOutputStream(1024);
179         }
180 
181         @Override
182         public void write(int i) throws IOException
183         {
184             _byteArrayOutputStream.write(i);
185         }
186 
187         public byte[] toByteArray()
188         {
189             return _byteArrayOutputStream.toByteArray();
190         }
191         
192         /**
193          * Write the data of this stream to the writer, using
194          * the charset encoding supplied or if null the default charset.
195          * 
196          * @param out
197          * @param encoding
198          * @throws IOException
199          */
200         private void writeTo(Writer out, String encoding) throws IOException{
201             //Get the charset based on the encoding or return the default if 
202             //encoding == null
203             Charset charset = (encoding == null) ? 
204                     Charset.defaultCharset() : Charset.forName(encoding);
205             CharsetDecoder decoder = charset.newDecoder();
206             CharBuffer decodedBuffer = decoder.decode(
207                     ByteBuffer.wrap(_byteArrayOutputStream.getInnerArray(),
208                             0,_byteArrayOutputStream.getInnerCount()));
209             if (decodedBuffer.hasArray()){
210                 out.write(decodedBuffer.array());
211             }
212         }
213 
214         public void reset()
215         {
216             _byteArrayOutputStream.reset();
217         }
218         
219         /**
220          * This Wrapper is used to provide additional methods to 
221          * get the buf and count variables, to use it to decode
222          * in WrappedServletOutputStream.writeTo and avoid buffer
223          * duplication.
224          */
225         static class WrappedByteArrayOutputStream extends ByteArrayOutputStream{
226             
227             public WrappedByteArrayOutputStream(){
228                 super();
229             }
230             
231             public WrappedByteArrayOutputStream(int size){
232                 super(size);                
233             }
234             
235             private byte[] getInnerArray(){
236                 return buf; 
237             }
238             
239             private int getInnerCount(){
240                 return count;
241             }
242         }
243 
244     }
245 }