package railo.runtime.writer; import java.io.IOException; import java.io.OutputStream; import java.util.zip.GZIPOutputStream; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import railo.commons.lang.StringUtil; import railo.runtime.Info; import railo.runtime.cache.legacy.CacheItem; import railo.runtime.net.http.HttpServletResponseWrap; import railo.runtime.net.http.ReqRspUtil; /** * Implementation of a JSpWriter */ public class CFMLWriterImpl extends CFMLWriter { private static final int BUFFER_SIZE = 100000; private static final String VERSION = Info.getVersionAsString(); private OutputStream out; private HttpServletResponse response; private boolean flushed; private String headData; private StringBuilder buffer=new StringBuilder(BUFFER_SIZE); private boolean closed=false; private boolean closeConn; private boolean showVersion; private boolean contentLength; private CacheItem cacheItem; private HttpServletRequest request; private boolean allowCompression; /** * constructor of the class * @param response Response Object * @param bufferSize buffer Size * @param autoFlush do auto flush Content */ public CFMLWriterImpl(HttpServletRequest request, HttpServletResponse response, int bufferSize, boolean autoFlush, boolean closeConn, boolean showVersion, boolean contentLength,boolean allowCompression) { super(bufferSize, autoFlush); this.request=request; this.response=response; this.autoFlush=autoFlush; this.bufferSize=bufferSize; this.closeConn=closeConn; this.showVersion=showVersion; this.contentLength=contentLength; this.allowCompression=allowCompression; } /* * * constructor of the class * @param response Response Object * / public JspWriterImpl(HttpServletResponse response) { this(response, BUFFER_SIZE, false); }*/ private void _check() throws IOException { if(autoFlush && buffer.length()>bufferSize) { _flush(true); } } /** * @throws IOException */ protected void initOut() throws IOException { if (out == null) { out=getOutputStream(false); //out=response.getWriter(); } } /** * @see javax.servlet.jsp.JspWriter#print(char[]) */ public void print(char[] arg) throws IOException { buffer.append(arg); _check(); } /** * reset configuration of buffer * @param bufferSize size of the buffer * @param autoFlush does the buffer autoflush * @throws IOException */ public void setBufferConfig(int bufferSize, boolean autoFlush) throws IOException { this.bufferSize=bufferSize; this.autoFlush=autoFlush; _check(); } /** * * @param headData * @throws IOException */ public void appendHTMLHead(String headData) throws IOException { if(!flushed) { if(this.headData==null)this.headData=headData; else this.headData+=headData; } else throw new IOException("page already flushed"); } public void writeHTMLHead(String headData) throws IOException { if(!flushed) { this.headData=headData; } else throw new IOException("page already flushed"); } /** * @see railo.runtime.writer.CFMLWriter#getHTMLHead() */ public String getHTMLHead() throws IOException { if(flushed) throw new IOException("page already flushed"); return headData==null?"":headData; } /** * @see railo.runtime.writer.CFMLWriter#resetHTMLHead() */ public void resetHTMLHead() throws IOException { if(flushed) throw new IOException("page already flushed"); headData=null; } /** * just a wrapper function for ACF * @throws IOException */ public void initHeaderBuffer() throws IOException{ resetHTMLHead(); } /** * @see java.io.Writer#write(char[], int, int) */ public void write(char[] cbuf, int off, int len) throws IOException { buffer.append(cbuf,off,len); _check(); } /** * @see javax.servlet.jsp.JspWriter#clear() */ public void clear() throws IOException { if (flushed) throw new IOException("respone buffer is already flushed"); clearBuffer(); } /** * @see javax.servlet.jsp.JspWriter#clearBuffer() */ public void clearBuffer() { buffer=new StringBuilder(BUFFER_SIZE); } /** * @see java.io.Writer#flush() */ public void flush() throws IOException { flushBuffer(true); // weil flushbuffer das out erstellt muss ich nicht mehr checken out.flush(); } /** * @see java.io.Writer#flush() */ private void _flush(boolean closeConn) throws IOException { flushBuffer(closeConn); // weil flushbuffer das out erstellt muss ich nicht mehr checken out.flush(); } /** * Flush the output buffer to the underlying character stream, without * flushing the stream itself. This method is non-private only so that it * may be invoked by PrintStream. * @throws IOException * @throws */ protected final void flushBuffer(boolean closeConn) throws IOException { if(!flushed && closeConn) { response.setHeader("connection", "close"); if(showVersion)response.setHeader("Railo-Version", VERSION); } initOut(); byte[] barr = _toString(true).getBytes(ReqRspUtil.getCharacterEncoding(null,response)); if(cacheItem!=null && cacheItem.isValid()) { cacheItem.store(barr, flushed); // writeCache(barr,flushed); } flushed = true; out.write(barr); buffer=new StringBuilder(BUFFER_SIZE); // to not change to clearBuffer, produce problem with CFMLWriterWhiteSpace.clearBuffer } private String _toString(boolean releaseHeadData) { if(headData==null) { return buffer.toString(); } String str=buffer.toString(); // /head int index=StringUtil.indexOfIgnoreCase(str,"</head>"); if(index!=-1){ str= str.substring(0,index).concat(headData).concat(str.substring(index)); if(releaseHeadData)headData=null; return str; } // head index=StringUtil.indexOfIgnoreCase(str,"<head>"); if(index!=-1){ str= str.substring(0,index+7).concat(headData).concat(str.substring(index+7)); if(releaseHeadData)headData=null; return str; } str= headData.concat(str); if(releaseHeadData)headData=null; return str; } /** * @see java.lang.Object#toString() */ public String toString() { return _toString(false); } /** * @see java.io.Writer#close() */ public void close() throws IOException { if (response == null || closed) return; //boolean closeConn=true; if(out==null) { if(response.isCommitted()) { closed=true; return; } //print.out(_toString()); byte[] barr = _toString(true).getBytes(ReqRspUtil.getCharacterEncoding(null,response)); if(cacheItem!=null) { cacheItem.store(barr, false); // writeCache(barr,false); } if(closeConn)response.setHeader("connection", "close"); if(showVersion)response.setHeader("Railo-Version", VERSION); if(barr.length<=512) allowCompression=false; out = getOutputStream(allowCompression); if(contentLength && !(out instanceof GZIPOutputStream))ReqRspUtil.setContentLength(response,barr.length); out.write(barr); out.flush(); out.close(); out = null; } else { _flush(closeConn); out.close(); out = null; } closed = true; } private OutputStream getOutputStream(boolean allowCompression) throws IOException { if (allowCompression){ String encodings = ReqRspUtil.getHeader(request, "Accept-Encoding", ""); if( encodings.indexOf("gzip")!=-1 ) { boolean inline=HttpServletResponseWrap.get(); if(!inline) { ServletOutputStream os = response.getOutputStream(); response.setHeader("Content-Encoding", "gzip"); return new GZIPOutputStream(os); } } } return response.getOutputStream(); } /*private void writeCache(byte[] barr,boolean append) throws IOException { cacheItem.store(barr, append); //IOUtil.copy(new ByteArrayInputStream(barr), cacheItem.getResource().getOutputStream(append),true,true); //MetaData.getInstance(cacheItem.getDirectory()).add(cacheItem.getName(), cacheItem.getRaw()); }*/ /** * @see javax.servlet.jsp.JspWriter#getRemaining() */ public int getRemaining() { return bufferSize - buffer.length(); } /** * @see javax.servlet.jsp.JspWriter#newLine() */ public void newLine() throws IOException { println(); } /** * @see javax.servlet.jsp.JspWriter#print(boolean) */ public void print(boolean arg) throws IOException { print(arg?new char[]{'t','r','u','e'}:new char[]{'f','a','l','s','e'}); } /** * @see javax.servlet.jsp.JspWriter#print(char) */ public void print(char arg) throws IOException { buffer.append(arg); _check(); } /** * @see javax.servlet.jsp.JspWriter#print(int) */ public void print(int arg) throws IOException { _print(String.valueOf(arg)); } /** * @see javax.servlet.jsp.JspWriter#print(long) */ public void print(long arg) throws IOException { _print(String.valueOf(arg)); } /** * @see javax.servlet.jsp.JspWriter#print(float) */ public void print(float arg) throws IOException { _print(String.valueOf(arg)); } /** * @see javax.servlet.jsp.JspWriter#print(double) */ public void print(double arg) throws IOException { _print(String.valueOf(arg)); } /** * @see javax.servlet.jsp.JspWriter#print(java.lang.String) */ public void print(String arg) throws IOException { buffer.append(arg); _check(); } /** * @see javax.servlet.jsp.JspWriter#print(java.lang.Object) */ public void print(Object arg) throws IOException { _print(String.valueOf(arg)); } /** * @see javax.servlet.jsp.JspWriter#println() */ public void println() throws IOException { _print("\n"); } /** * @see javax.servlet.jsp.JspWriter#println(boolean) */ public void println(boolean arg) throws IOException { print(arg?new char[]{'t','r','u','e','\n'}:new char[]{'f','a','l','s','e','\n'}); } /** * @see javax.servlet.jsp.JspWriter#println(char) */ public void println(char arg) throws IOException { print(new char[]{arg,'\n'}); } /** * @see javax.servlet.jsp.JspWriter#println(int) */ public void println(int arg) throws IOException { print(arg); println(); } /** * @see javax.servlet.jsp.JspWriter#println(long) */ public void println(long arg) throws IOException { print(arg); println(); } /** * @see javax.servlet.jsp.JspWriter#println(float) */ public void println(float arg) throws IOException { print(arg); println(); } /** * @see javax.servlet.jsp.JspWriter#println(double) */ public void println(double arg) throws IOException { print(arg); println(); } /** * @see javax.servlet.jsp.JspWriter#println(char[]) */ public void println(char[] arg) throws IOException { print(arg); println(); } /** * @see javax.servlet.jsp.JspWriter#println(java.lang.String) */ public void println(String arg) throws IOException { _print(arg); println(); } /** * @see javax.servlet.jsp.JspWriter#println(java.lang.Object) */ public void println(Object arg) throws IOException { print(arg); println(); } /** * @see java.io.Writer#write(char[]) */ public void write(char[] cbuf) throws IOException { print(cbuf); } /** * @see java.io.Writer#write(int) */ public void write(int c) throws IOException { print(c); } /** * @see java.io.Writer#write(java.lang.String, int, int) */ public void write(String str, int off, int len) throws IOException { write(str.toCharArray(),off,len); } /** * @see java.io.Writer#write(java.lang.String) */ public void write(String str) throws IOException { buffer.append(str); _check(); } /** * @see railo.runtime.writer.CFMLWriter#writeRaw(java.lang.String) */ public void writeRaw(String str) throws IOException { _print(str); } /** * @return Returns the flushed. */ public boolean isFlushed() { return flushed; } public void setClosed(boolean closed) { this.closed=closed; } private void _print(String arg) throws IOException { buffer.append(arg); _check(); } /** * @see railo.runtime.writer.CFMLWriter#getResponseStream() */ public OutputStream getResponseStream() throws IOException { initOut(); return out; } public void doCache(railo.runtime.cache.legacy.CacheItem ci) { this.cacheItem=ci; } /** * @return the cacheResource */ public CacheItem getCacheItem() { return cacheItem; } // only for compatibility to other vendors public String getString() { return toString(); } @Override public void setAllowCompression(boolean allowCompression) { this.allowCompression=allowCompression; } }