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