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 }