/*
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is XML RenderKit for JSF.
*
* The Initial Developer of the Original Code is
* Orbeon, Inc (info@orbeon.com)
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*/
package org.ajax4jsf.webapp;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.ajax4jsf.Messages;
import org.ajax4jsf.io.FastBufferInputStream;
import org.ajax4jsf.io.FastBufferOutputStream;
import org.ajax4jsf.io.FastBufferReader;
import org.ajax4jsf.io.FastBufferWriter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.InputSource;
/**
* Base wrapper save JSF page response, for parse to XML with different parsers
* @author shura (latest modification by $Author: alexsmirnov $)
* @version $Revision: 1.1.2.1 $ $Date: 2007/01/09 18:58:17 $
*
*/
public class FilterServletResponseWrapper extends HttpServletResponseWrapper {
private static final Log log = LogFactory.getLog(FilterServletResponseWrapper.class);
public static final String DEFAULT_ENCODING = "UTF-8";
protected FastBufferOutputStream byteStream = null;
private ServletOutputStream servletOutputStream;
private boolean useStream = false;
private FastBufferWriter stringWriter;
private PrintWriter printWriter;
private boolean useWriter = false;
private String contentType;
private String charterEncoding = null;
private Map<String, Object> headers = new HashMap<String, Object>();
private int bufferSize = 2048;
private int contentLength = Integer.MIN_VALUE;
private String redirectLocation = null;
private boolean useNullStream = false;
private boolean error = false;
private List<Cookie> cookies = new ArrayList<Cookie>();
public FilterServletResponseWrapper(HttpServletResponse response) {
super(response);
}
/**
* Convert saved context to stream for parsing
* @return stream from saved content
*/
public InputStream getContentAsStream() {
int length = 0;
FastBufferInputStream result = null;
String encoding = this.getCharacterEncoding() ;
/// byte[] content;
if (isUseStream()) {
try {
servletOutputStream.flush();
} catch (IOException ex) {
log.warn(Messages.getMessage(Messages.FLUSH_BUFFERED_STREAM_ERROR), ex);
}
/// content = byteStream.toByteArray();
result = new FastBufferInputStream(byteStream);
} else if (isUseWriter()) {
printWriter.flush();
printWriter.close();
length = stringWriter.getLength();
/// String stringContent = stringWriter.toString();
try {
FastBufferOutputStream stream = stringWriter.convertToOutputStream(encoding);
result = new FastBufferInputStream(stream);
/// content = stringContent.getBytes(encoding);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
log.warn(Messages.getMessage(Messages.UNSUPPORTED_ENCODING_WARNING));
FastBufferOutputStream stream = stringWriter.convertToOutputStream();
result = new FastBufferInputStream(stream);
/// content = stringContent.getBytes();
}
} else {
if (log.isDebugEnabled()) {
log.debug(Messages.getMessage(Messages.NO_WRITER_CALLED_INFO));
}
return null;
}
/// if (content.length > 0) {
if (length > 0) {
if (log.isDebugEnabled()) {
log.debug(Messages.getMessage(Messages.PARSE_XML_DOCUMENT_INFO));
// try {
// log.debug(new String(content, encoding));
// } catch (UnsupportedEncodingException e) {
// }
}
}
return result;
/// return new ByteArrayInputStream(content);
}
/**
* Convert saved context to stream for parsing
* @return stream from saved content
*/
public Reader getContentAsReader() {
FastBufferReader result = null;
String encoding = this.getCharacterEncoding() ;
/// String content;
if (isUseWriter()) {
printWriter.flush();
printWriter.close();
result = new FastBufferReader(stringWriter);
/// content = stringWriter.toString();
} else if (isUseStream()) {
try {
servletOutputStream.flush();
} catch (IOException ex) {
log.warn(Messages.getMessage(Messages.FLUSH_BUFFERED_STREAM_ERROR), ex);
}
try {
FastBufferWriter writer = byteStream.convertToWriter(encoding);
result = new FastBufferReader(writer);
/// content = byteStream.toString(encoding);
} catch (UnsupportedEncodingException e) {
log.warn(Messages.getMessage(Messages.UNSUPPORTED_ENCODING_WARNING_2), e);
FastBufferWriter writer = byteStream.convertToWriter();
result = new FastBufferReader(writer);
/// content = byteStream.toString();
}
} else {
if (log.isDebugEnabled()) {
log.debug(Messages.getMessage(Messages.NO_WRITER_CALLED_INFO));
}
return null;
}
// if (content.length() > 0) {
// if (log.isDebugEnabled()) {
// log.debug("XML document to parse in filter, return as Reader: ");
// log.debug(content);
// }
// }
/// return new StringReader(content);
return result;
}
public PrintWriter getWriter() throws IOException {
if (useStream) {
throw new IllegalStateException(Messages.getMessage(Messages.NO_WRITER_POSSIBLE_ERROR));
}
if (printWriter == null) {
if(log.isDebugEnabled()) {
log.debug(Messages.getMessage(Messages.CREATE_WRITER_INFO));
}
printWriter = new PrintWriter(new ServletStringWriter());
useWriter = true;
}
return printWriter;
}
public ServletOutputStream getOutputStream() throws IOException {
if (useWriter) {
if (isUseNullStream()) {
return new NullServletOutputStream();
}
throw new IllegalStateException(Messages.getMessage(Messages.NO_STREAM_POSSIBLE_ERROR));
}
if (servletOutputStream == null) {
// byteStream = new ByteArrayOutputStream();
servletOutputStream = new ByteArrayServletOutputStream();
}
if(log.isDebugEnabled()) {
log.debug(Messages.getMessage(Messages.CREATE_STREAM_INFO));
}
useStream = true;
return servletOutputStream;
}
public class ByteArrayServletOutputStream extends ServletOutputStream {
private boolean opened = true;
/* (non-Javadoc)
* @see java.io.OutputStream#close()
*/
public void close() throws IOException {
flush();
opened=false;
}
/* (non-Javadoc)
* @see java.io.OutputStream#write(byte[])
*/
public void write(byte[] b) throws IOException {
// TODO Auto-generated method stub
if (opened) {
byteStream.write(b);
}
}
public ByteArrayServletOutputStream() {
byteStream = new FastBufferOutputStream(bufferSize);
}
public void write(int b) throws IOException {
if (opened) {
byteStream.write(b);
}
}
/* (non-Javadoc)
* @see java.io.OutputStream#write(byte[], int, int)
*/
public void write(byte[] b, int off, int len) throws IOException {
if (opened) {
byteStream.write(b, off, len);
}
}
}
public static class NullServletOutputStream extends ServletOutputStream {
/* (non-Javadoc)
* @see java.io.OutputStream#write(byte[], int, int)
*/
public void write(byte[] b, int off, int len) throws IOException {
// this-is null stream, do nothing !
}
/* (non-Javadoc)
* @see java.io.OutputStream#write(byte[])
*/
public void write(byte[] b) throws IOException {
// this-is null stream, do nothing !
}
public void write(int b) throws IOException {
// this-is null stream, do nothing !
}
}
public class ServletStringWriter extends Writer {
private boolean opened = true;
public ServletStringWriter() {
super();
stringWriter = new FastBufferWriter(bufferSize);
}
public void write(char[] cbuf, int off, int len) throws IOException {
if (opened) {
stringWriter.write(cbuf, off, len);
}
}
/* (non-Javadoc)
* @see java.io.Writer#write(char[])
*/
public void write(char[] cbuf) throws IOException {
// TODO Auto-generated method stub
if (opened) {
stringWriter.write(cbuf);
}
}
/* (non-Javadoc)
* @see java.io.Writer#write(int)
*/
public void write(int c) throws IOException {
// TODO Auto-generated method stub
if (opened) {
stringWriter.write(c);
}
}
public void flush() throws IOException {
stringWriter.flush();
}
public void close() throws IOException {
stringWriter.close();
opened = false;
}
}
// public boolean isCommitted() {
// // NOTE: What makes sense here?
// return false;
// }
/**
* Return a charset from a content-type.
*/
public static String getContentTypeCharset(String contentType) {
if (contentType == null)
return null;
int semicolumnIndex = contentType.indexOf(";");
if (semicolumnIndex == -1)
return null;
int charsetIndex = contentType.indexOf("charset=", semicolumnIndex);
if (charsetIndex == -1)
return null;
// FIXME: There may be other attributes after charset, right?
String afterCharset = contentType.substring(charsetIndex + 8);
afterCharset = afterCharset.replace('"', ' ');
return afterCharset.trim();
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServletResponse#addHeader(java.lang.String, java.lang.String)
*/
public void addHeader(String name, String value) {
if("Content-Type".equals(name)){
setContentType(value);
} else {
this.headers.put(name,value);
super.addHeader(name, value);
}
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServletResponse#setHeader(java.lang.String, java.lang.String)
*/
public void setHeader(String name, String value) {
// HACK - weblogic do not use setContentType, instead directly set header !
if("Content-Type".equals(name)){
setContentType(value);
} else {
this.headers.put(name,value);
super.setHeader(name, value);
}
}
public Map<String, Object> getHeaders(){
return this.headers;
}
/* (non-Javadoc)
* @see javax.servlet.ServletResponse#getCharacterEncoding()
*/
public String getCharacterEncoding() {
// return this.charterEncoding;
if (null != this.charterEncoding) {
return this.charterEncoding;
} else {
// For cases of implicit setting
return super.getCharacterEncoding();
}
}
/* (non-Javadoc)
* @see javax.servlet.ServletResponse#setCharacterEncoding(java.lang.String)
*/
public void setCharacterEncoding(String charset) {
this.charterEncoding = charset;
//super.setCharacterEncoding(charset);
}
public void setContentType(String type) {
String contentTypeCharset = getContentTypeCharset(type);
if (null != contentTypeCharset) {
this.charterEncoding = contentTypeCharset;
}
this.contentType = type;
// super.setContentType(type);
}
public void setLocale(Locale loc) {
super.setLocale(loc);
}
public String getContentType() {
return contentType;
}
/**
* Send buffered content directly to output stream.
* @param outputStream
* @throws IOException
*/
public void sendContent(ServletOutputStream outputStream) throws IOException {
if(getContentLength() >0 ){
super.setContentLength(getContentLength());
}
if (isUseWriter()) {
printWriter.flush();
printWriter.close();
stringWriter.printTo(outputStream);
/// outputStream.print(stringWriter.toString());
} else if (isUseStream()) {
try {
servletOutputStream.flush();
} catch (IOException ex) {
log.warn(Messages.getMessage(Messages.FLUSH_BUFFERED_STREAM_ERROR), ex);
}
byteStream.writeTo(outputStream);
}
}
/**
* Send buffered content directly to output stream.
* @param outputStream
* @throws IOException
*/
public void sendContent(Writer output) throws IOException {
if(getContentLength() >0 ){
super.setContentLength(getContentLength());
}
if (isUseWriter()) {
printWriter.flush();
printWriter.close();
stringWriter.writeTo(output);
/// outputStream.print(stringWriter.toString());
} else if (isUseStream()) {
try {
servletOutputStream.flush();
} catch (IOException ex) {
log.warn(Messages.getMessage(Messages.FLUSH_BUFFERED_STREAM_ERROR), ex);
}
byteStream.writeTo(output,getCharacterEncoding());
}
}
/**
* @return Returns the useStream.
*/
public boolean isUseStream() {
return this.useStream;
}
/**
* @return Returns the useWriter.
*/
public boolean isUseWriter() {
return this.useWriter;
}
/**
* @return
* @throws RuntimeException
*/
public InputSource getContentAsInputSource() throws RuntimeException {
// Create InputSource
InputSource inputSource = null;
String encoding = this.getCharacterEncoding() ;
if(isUseWriter()) {
inputSource = new InputSource(getContentAsReader());
} else if(isUseStream()) {
inputSource = new InputSource(getContentAsStream());
if (encoding != null)
inputSource.setEncoding(encoding);
} else {
if (log.isDebugEnabled()) {
log.debug(Messages.getMessage(Messages.NO_WRITER_CALLED_INFO));
}
return null;
}
return inputSource;
}
/* (non-Javadoc)
* @see javax.servlet.ServletResponseWrapper#flushBuffer()
*/
public void flushBuffer() throws IOException {
if(isUseStream()){
servletOutputStream.flush();
} else if(isUseWriter()){
printWriter.flush();
}
}
/* (non-Javadoc)
* @see javax.servlet.ServletResponseWrapper#getBufferSize()
*/
public int getBufferSize() {
// TODO Auto-generated method stub
return bufferSize;
}
/* (non-Javadoc)
* @see javax.servlet.ServletResponseWrapper#resetBuffer()
*/
public void resetBuffer() {
this.printWriter = null;
this.stringWriter = null;
this.servletOutputStream = null;
this.byteStream = null;
this.useStream = false;
this.useWriter = false;
}
/* (non-Javadoc)
* @see javax.servlet.ServletResponseWrapper#setBufferSize(int)
*/
public void setBufferSize(int arg0) {
// TODO Auto-generated method stub
bufferSize = arg0;
}
/* (non-Javadoc)
* @see javax.servlet.ServletResponseWrapper#reset()
*/
public void reset() {
// TODO Auto-generated method stub
this.resetBuffer();
this.headers = new HashMap<String, Object>();
this.contentType=null;
this.charterEncoding=null;
super.reset();
}
/**
* @return Returns the contentLength.
*/
public int getContentLength() {
return contentLength;
}
/**
* @param contentLength The contentLength to set.
*/
public void setContentLength(int contentLength) {
this.contentLength = contentLength;
}
/**
* Parse saved content to output stream ( and store as Dom Document tree )
* @param out stream to send parsed content, if null - only build dom tree.
* @return parsed document as dom tree.
* @throws IOException
*/
public void parseContent(Writer out,HtmlParser parser) throws IOException {
parser.setInputEncoding(getCharacterEncoding());
if (isUseWriter()) {
parser.parseHtml(getContentAsReader(),out);
} else if(isUseStream()){
parser.parseHtml(getContentAsStream(),out);
}
}
public void sendRedirect(String arg0) throws IOException {
// TODO Auto-generated method stub
this.redirectLocation = arg0;
}
public String getRedirectLocation() {
return redirectLocation;
}
/**
* @return the useNullStream
*/
public boolean isUseNullStream() {
return useNullStream;
}
/**
* @param useNullStream the useNullStream to set
*/
public void setUseNullStream(boolean useNullStream) {
this.useNullStream = useNullStream;
}
public void addCookie(Cookie cookie) {
cookies.add(cookie);
super.addCookie(cookie);
}
public Collection<Cookie> getCookies() {
return cookies;
}
public void sendError(int sc) throws IOException {
this.error = true;
super.sendError(sc);
}
public void sendError(int sc, String msg) throws IOException {
this.error = true;
super.sendError(sc, msg);
}
public boolean isError() {
return error;
}
}