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 }