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 }