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: struberg $)
38 * @version $Revision: 1188643 $ $Date: 2011-10-25 08:13:09 -0500 (Tue, 25 Oct 2011) $
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 }