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 }