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