blob: 549df4c84853a272959bbaf31160bf3b64ba50fb [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.myfaces.view;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import javax.servlet.WriteListener;
/**
* @author Bruno Aranda (latest modification by $Author$)
* @version $Revision$ $Date$
*/
public class ServletViewResponseWrapper extends HttpServletResponseWrapper implements ViewResponseWrapper
{
private PrintWriter _writer;
private CharArrayWriter _charArrayWriter;
private int _status = HttpServletResponse.SC_OK;
private WrappedServletOutputStream _byteArrayWriter;
public ServletViewResponseWrapper(HttpServletResponse httpServletResponse)
{
super(httpServletResponse);
}
@Override
public void sendError(int status) throws IOException
{
super.sendError(status);
_status = status;
}
@Override
public void sendError(int status, String errorMessage) throws IOException
{
super.sendError(status, errorMessage);
_status = status;
}
@Override
public void setStatus(int status)
{
super.setStatus(status);
_status = status;
}
@Override
public void setStatus(int status, String errorMessage)
{
super.setStatus(status, errorMessage);
_status = status;
}
@Override
public int getStatus()
{
return _status;
}
public void flushToWrappedResponse() throws IOException
{
if (_charArrayWriter != null)
{
_charArrayWriter.writeTo(getResponse().getWriter());
_charArrayWriter.reset();
_writer.flush();
}
else if (_byteArrayWriter != null)
{
// MYFACES-1955 cannot call getWriter() after getOutputStream()
// _byteArrayWriter is not null only if getOutputStream() was called
// before. This method is called from f:view to flush data before tag
// start, or if an error page is flushed after dispatch.
// A resource inside /faces/* (see MYFACES-1815) is handled on flushToWriter.
// If response.getOuputStream() was called before, an IllegalStateException
// is raised on response.getWriter(), so we should try through stream.
try
{
_byteArrayWriter.writeTo(getResponse().getWriter(), getResponse().getCharacterEncoding());
}
catch (IllegalStateException e)
{
getResponse().getOutputStream().write(_byteArrayWriter.toByteArray());
}
_byteArrayWriter.reset();
_byteArrayWriter.flush();
}
}
@Override
public void flushToWriter(Writer writer,String encoding) throws IOException
{
if (_charArrayWriter != null)
{
_charArrayWriter.writeTo(writer);
_charArrayWriter.reset();
_writer.flush();
}
else if (_byteArrayWriter != null)
{
_byteArrayWriter.writeTo(writer,encoding);
_byteArrayWriter.reset();
_byteArrayWriter.flush();
}
writer.flush();
}
@Override
public ServletOutputStream getOutputStream() throws IOException
{
if (_charArrayWriter != null)
{
throw new IllegalStateException();
}
if (_byteArrayWriter == null)
{
_byteArrayWriter = new WrappedServletOutputStream();
}
return _byteArrayWriter;
}
@Override
public PrintWriter getWriter() throws IOException
{
if (_byteArrayWriter != null)
{
throw new IllegalStateException();
}
if (_writer == null)
{
_charArrayWriter = new CharArrayWriter(4096);
_writer = new PrintWriter(_charArrayWriter);
}
return _writer;
}
@Override
public void reset()
{
if (_charArrayWriter != null)
{
_charArrayWriter.reset();
}
}
@Override
public String toString()
{
if (_charArrayWriter != null)
{
return _charArrayWriter.toString();
}
return null;
}
static class WrappedServletOutputStream extends ServletOutputStream
{
private WrappedByteArrayOutputStream baos;
public WrappedServletOutputStream()
{
baos = new WrappedByteArrayOutputStream(1024);
}
@Override
public void write(int i) throws IOException
{
baos.write(i);
}
public byte[] toByteArray()
{
return baos.toByteArray();
}
/**
* Write the data of this stream to the writer, using
* the charset encoding supplied or if null the default charset.
*
* @param out
* @param encoding
* @throws IOException
*/
private void writeTo(Writer out, String encoding) throws IOException
{
//Get the charset based on the encoding or return the default if encoding == null
Charset charset = encoding == null
? Charset.defaultCharset()
: Charset.forName(encoding);
CharsetDecoder decoder = charset.newDecoder();
CharBuffer decodedBuffer = decoder.decode(
ByteBuffer.wrap(baos.getInnerArray(), 0, baos.getInnerCount()));
if (decodedBuffer.hasArray())
{
out.write(decodedBuffer.array());
}
}
public void reset()
{
baos.reset();
}
@Override
public boolean isReady()
{
return true;
}
@Override
public void setWriteListener(WriteListener wl)
{
}
/**
* This Wrapper is used to provide additional methods to
* get the buf and count variables, to use it to decode
* in WrappedServletOutputStream.writeTo and avoid buffer
* duplication.
*/
static class WrappedByteArrayOutputStream extends ByteArrayOutputStream
{
public WrappedByteArrayOutputStream()
{
super();
}
public WrappedByteArrayOutputStream(int size)
{
super(size);
}
private byte[] getInnerArray()
{
return buf;
}
private int getInnerCount()
{
return count;
}
}
}
}