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 org.apache.myfaces.shared.view.ViewResponseWrapper;
22  
23  import javax.servlet.ServletOutputStream;
24  import javax.servlet.http.HttpServletResponse;
25  import javax.servlet.http.HttpServletResponseWrapper;
26  import java.io.ByteArrayOutputStream;
27  import java.io.CharArrayWriter;
28  import java.io.IOException;
29  import java.io.PrintWriter;
30  import java.io.Writer;
31  import java.nio.ByteBuffer;
32  import java.nio.CharBuffer;
33  import java.nio.charset.Charset;
34  import java.nio.charset.CharsetDecoder;
35  import javax.servlet.WriteListener;
36  
37  /**
38   * @author Bruno Aranda (latest modification by $Author$)
39   * @version $Revision$ $Date$
40   */
41  public class ServletViewResponseWrapper extends HttpServletResponseWrapper implements ViewResponseWrapper
42  {
43      private PrintWriter _writer;
44      private CharArrayWriter _charArrayWriter;
45      private int _status = HttpServletResponse.SC_OK;
46      private WrappedServletOutputStream _byteArrayWriter;
47  
48      public ServletViewResponseWrapper(HttpServletResponse httpServletResponse)
49      {
50          super(httpServletResponse);
51      }
52  
53      @Override
54      public void sendError(int status) throws IOException
55      {
56          super.sendError(status);
57          _status = status;
58      }
59  
60      @Override
61      public void sendError(int status, String errorMessage) throws IOException
62      {
63          super.sendError(status, errorMessage);
64          _status = status;
65      }
66  
67      @Override
68      public void setStatus(int status)
69      {
70          super.setStatus(status);
71          _status = status;
72      }
73  
74      @Override
75      public void setStatus(int status, String errorMessage)
76      {
77          super.setStatus(status, errorMessage);
78          _status = status;
79      }
80  
81      public int getStatus()
82      {
83          return _status;
84      }
85  
86      public void flushToWrappedResponse() throws IOException
87      {
88          if (_charArrayWriter != null)
89          {
90              _charArrayWriter.writeTo(getResponse().getWriter());
91              _charArrayWriter.reset();
92              _writer.flush();
93          }
94          else if (_byteArrayWriter != null)
95          {
96              // MYFACES-1955 cannot call getWriter() after getOutputStream()
97              // _byteArrayWriter is not null only if getOutputStream() was called
98              // before. This method is called from f:view to flush data before tag
99              // start, or if an error page is flushed after dispatch.
100             // A resource inside /faces/* (see MYFACES-1815) is handled on flushToWriter.
101             // If response.getOuputStream() was called before, an IllegalStateException
102             // is raised on response.getWriter(), so we should try through stream.
103             try
104             {
105                 _byteArrayWriter.writeTo(getResponse().getWriter(), getResponse().getCharacterEncoding());
106             }
107             catch (IllegalStateException e)
108             {
109             getResponse().getOutputStream().write(_byteArrayWriter.toByteArray());
110             }
111             _byteArrayWriter.reset();
112             _byteArrayWriter.flush();
113         }
114     }
115 
116     public void flushToWriter(Writer writer,String encoding) throws IOException
117     {
118         if (_charArrayWriter != null)
119         {
120             _charArrayWriter.writeTo(writer);
121             _charArrayWriter.reset();
122             _writer.flush();
123         }
124         else if (_byteArrayWriter != null)
125         {
126             _byteArrayWriter.writeTo(writer,encoding);
127             _byteArrayWriter.reset();
128             _byteArrayWriter.flush();
129         }
130         writer.flush();
131     }
132 
133     @Override
134     public ServletOutputStream getOutputStream() throws IOException
135     {
136         if (_charArrayWriter != null)
137         {
138             throw new IllegalStateException();
139         }
140         if (_byteArrayWriter == null)
141         {
142             _byteArrayWriter = new WrappedServletOutputStream();
143         }
144         return _byteArrayWriter;
145     }
146 
147     @Override
148     public PrintWriter getWriter() throws IOException
149     {
150         if (_byteArrayWriter != null)
151         {
152             throw new IllegalStateException();
153         }
154         if (_writer == null)
155         {
156             _charArrayWriter = new CharArrayWriter(4096);
157             _writer = new PrintWriter(_charArrayWriter);
158         }
159         return _writer;
160     }
161 
162     @Override
163     public void reset()
164     {
165         if (_charArrayWriter != null)
166         {
167             _charArrayWriter.reset();
168         }
169     }
170 
171     @Override
172     public String toString()
173     {
174         if (_charArrayWriter != null)
175         {
176             return _charArrayWriter.toString();
177         }
178         return null;
179     }
180 
181     static class WrappedServletOutputStream extends ServletOutputStream
182     {
183         private WrappedByteArrayOutputStream _byteArrayOutputStream;
184 
185 
186         public WrappedServletOutputStream()
187         {
188             _byteArrayOutputStream = new WrappedByteArrayOutputStream(1024);
189         }
190 
191         @Override
192         public void write(int i) throws IOException
193         {
194             _byteArrayOutputStream.write(i);
195         }
196 
197         public byte[] toByteArray()
198         {
199             return _byteArrayOutputStream.toByteArray();
200         }
201         
202         /**
203          * Write the data of this stream to the writer, using
204          * the charset encoding supplied or if null the default charset.
205          * 
206          * @param out
207          * @param encoding
208          * @throws IOException
209          */
210         private void writeTo(Writer out, String encoding) throws IOException
211         {
212             //Get the charset based on the encoding or return the default if 
213             //encoding == null
214             Charset charset = (encoding == null) ? 
215                     Charset.defaultCharset() : Charset.forName(encoding);
216             CharsetDecoder decoder = charset.newDecoder();
217             CharBuffer decodedBuffer = decoder.decode(
218                     ByteBuffer.wrap(_byteArrayOutputStream.getInnerArray(),
219                             0,_byteArrayOutputStream.getInnerCount()));
220             if (decodedBuffer.hasArray())
221             {
222                 out.write(decodedBuffer.array());
223             }
224         }
225 
226         public void reset()
227         {
228             _byteArrayOutputStream.reset();
229         }
230 
231         @Override
232         public boolean isReady()
233         {
234             return true;
235         }
236 
237         @Override
238         public void setWriteListener(WriteListener wl)
239         {
240             
241         }
242         
243         /**
244          * This Wrapper is used to provide additional methods to 
245          * get the buf and count variables, to use it to decode
246          * in WrappedServletOutputStream.writeTo and avoid buffer
247          * duplication.
248          */
249         static class WrappedByteArrayOutputStream extends ByteArrayOutputStream
250         {
251             
252             public WrappedByteArrayOutputStream()
253             {
254                 super();
255             }
256             
257             public WrappedByteArrayOutputStream(int size)
258             {
259                 super(size);                
260             }
261             
262             private byte[] getInnerArray()
263             {
264                 return buf; 
265             }
266             
267             private int getInnerCount()
268             {
269                 return count;
270             }
271         }
272 
273     }
274 }