001    package railo.runtime.writer;
002    
003    import java.io.IOException;
004    import java.io.OutputStream;
005    import java.util.zip.GZIPOutputStream;
006    
007    import javax.servlet.ServletOutputStream;
008    import javax.servlet.http.HttpServletRequest;
009    import javax.servlet.http.HttpServletResponse;
010    
011    import railo.commons.lang.StringUtil;
012    import railo.runtime.Info;
013    import railo.runtime.cache.legacy.CacheItem;
014    import railo.runtime.net.http.HttpServletResponseWrap;
015    import railo.runtime.net.http.ReqRspUtil;
016    
017    /**
018     * Implementation of a JSpWriter
019     */
020    public class CFMLWriterImpl extends CFMLWriter { 
021         
022            private static final int BUFFER_SIZE = 100000;
023            private static final String VERSION = Info.getVersionAsString();  
024        private OutputStream out;
025            private HttpServletResponse response;
026        private boolean flushed;
027        private String headData;
028        private StringBuilder buffer=new StringBuilder(BUFFER_SIZE);
029        private boolean closed=false;
030        private boolean closeConn;
031        private boolean showVersion;
032        private boolean contentLength;
033        private CacheItem cacheItem;
034            private HttpServletRequest request;
035            private boolean allowCompression; 
036        
037        /**
038         * constructor of the class
039         * @param response Response Object
040         * @param bufferSize buffer Size
041         * @param autoFlush do auto flush Content
042         */
043        public CFMLWriterImpl(HttpServletRequest request, HttpServletResponse response, int bufferSize, boolean autoFlush, boolean closeConn, boolean showVersion, boolean contentLength,boolean allowCompression) {
044            super(bufferSize, autoFlush);
045            this.request=request;
046            this.response=response;
047            this.autoFlush=autoFlush;
048            this.bufferSize=bufferSize;
049            this.closeConn=closeConn;
050            this.showVersion=showVersion;
051            this.contentLength=contentLength;
052            this.allowCompression=allowCompression;
053        }
054    
055        /* *
056         * constructor of the class
057         * @param response Response Object
058         * /
059        public JspWriterImpl(HttpServletResponse response) {
060            this(response, BUFFER_SIZE, false);
061        }*/
062        
063        private void _check() throws IOException {
064            if(autoFlush && buffer.length()>bufferSize)  {
065                _flush(true);
066            }
067        }
068    
069        /**
070         * @throws IOException
071         */
072        protected void initOut() throws IOException {
073            if (out == null) {
074                    out=getOutputStream();
075                //out=response.getWriter();
076            }
077        }
078    
079    
080            /**
081         * @see javax.servlet.jsp.JspWriter#print(char[]) 
082         */ 
083        public void print(char[] arg) throws IOException { 
084            buffer.append(arg);
085            _check();
086        }
087        
088        /**
089         * reset configuration of buffer
090         * @param bufferSize size of the buffer
091         * @param autoFlush does the buffer autoflush
092         * @throws IOException
093         */
094        public void setBufferConfig(int bufferSize, boolean autoFlush) throws IOException {
095            this.bufferSize=bufferSize;
096            this.autoFlush=autoFlush;
097            _check();
098        }
099        
100        /**
101         * 
102         * @param headData
103         * @throws IOException
104         */
105        public void appendHTMLHead(String headData) throws IOException {
106            if(!flushed) {
107                if(this.headData==null)this.headData=headData;
108                else this.headData+=headData;
109            }
110            else throw new IOException("page already flushed");
111        }
112        
113        public void writeHTMLHead(String headData) throws IOException {
114            if(!flushed) {
115                this.headData=headData;
116            }
117            else throw new IOException("page already flushed");
118        }
119        
120        /** 
121         * @see railo.runtime.writer.CFMLWriter#getHTMLHead()
122         */
123        public String getHTMLHead() throws IOException {
124            if(flushed) throw new IOException("page already flushed");
125            return headData==null?"":headData;
126        }
127        
128        /** 
129         * @see railo.runtime.writer.CFMLWriter#resetHTMLHead()
130         */
131        public void resetHTMLHead() throws IOException {
132            if(flushed) throw new IOException("page already flushed");
133            headData=null;
134        }
135        
136        /**
137         * just a wrapper function for ACF
138         * @throws IOException 
139         */
140        public void initHeaderBuffer() throws IOException{
141            resetHTMLHead();
142        }
143    
144    
145        /** 
146         * @see java.io.Writer#write(char[], int, int) 
147         */ 
148        public void write(char[] cbuf, int off, int len) throws IOException { 
149            buffer.append(cbuf,off,len);
150            _check();
151        }
152    
153        /** 
154         * @see javax.servlet.jsp.JspWriter#clear() 
155         */ 
156        public void clear() throws IOException { 
157            if (flushed)  throw new IOException("respone buffer is already flushed");
158            clearBuffer();
159        } 
160    
161        /** 
162         * @see javax.servlet.jsp.JspWriter#clearBuffer() 
163         */ 
164        public void clearBuffer() { 
165            buffer=new StringBuilder(BUFFER_SIZE);
166        } 
167    
168        /** 
169         * @see java.io.Writer#flush() 
170         */ 
171        public void flush() throws IOException { 
172            flushBuffer(true);
173            // weil flushbuffer das out erstellt muss ich nicht mehr checken
174            out.flush();
175        } 
176        
177        /** 
178         * @see java.io.Writer#flush() 
179         */ 
180        private void _flush(boolean closeConn) throws IOException { 
181            flushBuffer(closeConn);
182            // weil flushbuffer das out erstellt muss ich nicht mehr checken
183            out.flush();
184            
185        } 
186    
187        /**
188         * Flush the output buffer to the underlying character stream, without
189         * flushing the stream itself.  This method is non-private only so that it
190         * may be invoked by PrintStream.
191         * @throws IOException
192         * @throws  
193         */
194        protected final void flushBuffer(boolean closeConn) throws IOException {
195            if(!flushed && closeConn) {
196                    response.setHeader("connection", "close");
197                    if(showVersion)response.setHeader("Railo-Version", VERSION);
198                    
199            }
200            initOut();
201            byte[] barr = _toString(true).getBytes(response.getCharacterEncoding());
202            
203            if(cacheItem!=null && cacheItem.isValid()) {
204                    cacheItem.store(barr, flushed);
205                    // writeCache(barr,flushed);
206            }
207            flushed = true;
208            out.write(barr);
209            
210            buffer=new StringBuilder(BUFFER_SIZE); // to not change to clearBuffer, produce problem with CFMLWriterWhiteSpace.clearBuffer 
211        } 
212        
213        
214    
215        private String _toString(boolean releaseHeadData) {
216            if(headData==null) {
217                return buffer.toString();
218            }
219            String str=buffer.toString();
220            
221        // /head
222            int index=StringUtil.indexOfIgnoreCase(str,"</head>");
223            if(index!=-1){
224                    str= str.substring(0,index).concat(headData).concat(str.substring(index));
225                if(releaseHeadData)headData=null;
226                return str;
227            }
228            
229         // head
230            index=StringUtil.indexOfIgnoreCase(str,"<head>");
231            if(index!=-1){
232                    str= str.substring(0,index+7).concat(headData).concat(str.substring(index+7));
233                if(releaseHeadData)headData=null;
234                return str;
235            }
236            
237            
238            str= headData.concat(str);
239                if(releaseHeadData)headData=null;
240                return str;
241            
242            
243        }
244        
245            /**
246             * @see java.lang.Object#toString()
247             */
248            public String toString() {
249            return _toString(false);
250        }
251        
252        
253        
254        
255    
256        /** 
257         * @see java.io.Writer#close() 
258         */ 
259        public void close() throws IOException {
260            if (response == null || closed) return;
261            //boolean closeConn=true;
262            if(out==null) { 
263                    if(response.isCommitted()) {
264                            closed=true;
265                            return;
266                    }
267                    //print.out(_toString());
268                    byte[] barr = _toString(true).getBytes(response.getCharacterEncoding());
269                
270                    if(cacheItem!=null)     {
271                            cacheItem.store(barr, false);
272                    // writeCache(barr,false);
273                    }
274                    
275                    if(closeConn)response.setHeader("connection", "close");
276                    if(showVersion)response.setHeader("Railo-Version", VERSION);
277                
278                out = getOutputStream();
279                    
280                    
281                    if(contentLength && !(out instanceof GZIPOutputStream))ReqRspUtil.setContentLength(response,barr.length);
282                
283                    out.write(barr);
284                        out.flush();
285                        out.close();
286                
287                
288                out = null;
289            }
290            else {
291                _flush(closeConn);
292                out.close();
293                out = null;
294            }
295            closed = true;
296        } 
297        
298    
299        private OutputStream getOutputStream() throws IOException {
300            
301            if(allowCompression){
302                    
303                    String encodings = ReqRspUtil.getHeader(request,"Accept-Encoding",null);
304                if(!StringUtil.isEmpty(encodings) && encodings.indexOf("gzip")!=-1) {
305                    boolean inline=HttpServletResponseWrap.get();
306                    if(!inline) {
307                            ServletOutputStream os = response.getOutputStream();
308                            response.setHeader("Content-Encoding", "gzip");
309                                    return new GZIPOutputStream(os);
310                    }
311                }
312            }
313            return response.getOutputStream();
314            }
315        
316        
317    
318        /*private void writeCache(byte[] barr,boolean append) throws IOException {
319            cacheItem.store(barr, append);
320            //IOUtil.copy(new ByteArrayInputStream(barr), cacheItem.getResource().getOutputStream(append),true,true);
321            //MetaData.getInstance(cacheItem.getDirectory()).add(cacheItem.getName(), cacheItem.getRaw());
322            }*/
323    
324        /** 
325         * @see javax.servlet.jsp.JspWriter#getRemaining() 
326         */ 
327        public int getRemaining() { 
328            return bufferSize - buffer.length();
329        }
330    
331        /** 
332         * @see javax.servlet.jsp.JspWriter#newLine() 
333         */ 
334        public void newLine() throws IOException { 
335            println();
336        } 
337    
338        /** 
339         * @see javax.servlet.jsp.JspWriter#print(boolean) 
340         */ 
341        public void print(boolean arg) throws IOException { 
342            print(arg?new char[]{'t','r','u','e'}:new char[]{'f','a','l','s','e'}); 
343        }
344    
345        /** 
346         * @see javax.servlet.jsp.JspWriter#print(char) 
347         */ 
348        public void print(char arg) throws IOException { 
349            buffer.append(arg);
350            _check();
351        } 
352    
353        /** 
354         * @see javax.servlet.jsp.JspWriter#print(int) 
355         */ 
356        public void print(int arg) throws IOException { 
357            _print(String.valueOf(arg)); 
358        } 
359    
360        /** 
361         * @see javax.servlet.jsp.JspWriter#print(long) 
362         */ 
363        public void print(long arg) throws IOException { 
364            _print(String.valueOf(arg)); 
365    
366        } 
367    
368        /** 
369         * @see javax.servlet.jsp.JspWriter#print(float) 
370         */ 
371        public void print(float arg) throws IOException { 
372            _print(String.valueOf(arg)); 
373        } 
374    
375        /** 
376         * @see javax.servlet.jsp.JspWriter#print(double) 
377         */ 
378        public void print(double arg) throws IOException { 
379            _print(String.valueOf(arg)); 
380        } 
381    
382        /** 
383         * @see javax.servlet.jsp.JspWriter#print(java.lang.String) 
384         */ 
385        public void print(String arg) throws IOException { 
386            buffer.append(arg);
387            _check();
388        } 
389    
390        /** 
391         * @see javax.servlet.jsp.JspWriter#print(java.lang.Object) 
392         */ 
393        public void print(Object arg) throws IOException { 
394            _print(String.valueOf(arg)); 
395        } 
396    
397        /** 
398         * @see javax.servlet.jsp.JspWriter#println() 
399         */ 
400        public void println() throws IOException { 
401            _print("\n"); 
402    
403        } 
404    
405        /** 
406         * @see javax.servlet.jsp.JspWriter#println(boolean) 
407         */ 
408        public void println(boolean arg) throws IOException { 
409            print(arg?new char[]{'t','r','u','e','\n'}:new char[]{'f','a','l','s','e','\n'}); 
410        } 
411    
412        /** 
413         * @see javax.servlet.jsp.JspWriter#println(char) 
414         */ 
415        public void println(char arg) throws IOException { 
416            print(new char[]{arg,'\n'}); 
417        } 
418    
419        /** 
420         * @see javax.servlet.jsp.JspWriter#println(int) 
421         */ 
422        public void println(int arg) throws IOException { 
423            print(arg); 
424            println(); 
425        } 
426    
427        /** 
428         * @see javax.servlet.jsp.JspWriter#println(long) 
429         */ 
430        public void println(long arg) throws IOException { 
431            print(arg); 
432            println(); 
433        } 
434    
435        /** 
436         * @see javax.servlet.jsp.JspWriter#println(float) 
437         */ 
438        public void println(float arg) throws IOException { 
439            print(arg); 
440            println(); 
441        } 
442    
443        /** 
444         * @see javax.servlet.jsp.JspWriter#println(double) 
445         */ 
446        public void println(double arg) throws IOException { 
447            print(arg); 
448            println(); 
449        } 
450    
451        /** 
452         * @see javax.servlet.jsp.JspWriter#println(char[]) 
453         */ 
454        public void println(char[] arg) throws IOException { 
455            print(arg); 
456            println(); 
457        } 
458    
459        /** 
460         * @see javax.servlet.jsp.JspWriter#println(java.lang.String) 
461         */ 
462        public void println(String arg) throws IOException { 
463            _print(arg); 
464            println(); 
465        } 
466    
467        /** 
468         * @see javax.servlet.jsp.JspWriter#println(java.lang.Object) 
469         */ 
470        public void println(Object arg) throws IOException { 
471            print(arg); 
472            println(); 
473        }
474        
475        /** 
476         * @see java.io.Writer#write(char[]) 
477         */ 
478        public void write(char[] cbuf) throws IOException { 
479            print(cbuf); 
480        } 
481    
482        /** 
483         * @see java.io.Writer#write(int) 
484         */ 
485        public void write(int c) throws IOException { 
486            print(c); 
487        } 
488    
489        /** 
490         * @see java.io.Writer#write(java.lang.String, int, int) 
491         */ 
492        public void write(String str, int off, int len) throws IOException { 
493            write(str.toCharArray(),off,len);
494        } 
495    
496        /** 
497         * @see java.io.Writer#write(java.lang.String) 
498         */ 
499        public void write(String str) throws IOException { 
500            _print(str);
501        }
502        
503        /**
504             * @see railo.runtime.writer.CFMLWriter#writeRaw(java.lang.String)
505             */
506            public void writeRaw(String str) throws IOException {
507                    _print(str);
508            }
509    
510        /**
511         * @return Returns the flushed.
512         */
513        public boolean isFlushed() {
514            return flushed;
515        }
516    
517        public void setClosed(boolean closed) {
518            this.closed=closed;
519        }
520    
521        private void _print(String arg) throws IOException { 
522            buffer.append(arg);
523            _check();
524        }
525    
526            /**
527             * @see railo.runtime.writer.CFMLWriter#getResponseStream()
528             */
529            public OutputStream getResponseStream() throws IOException {
530                    initOut();
531                    return out;
532            }
533    
534            public void doCache(railo.runtime.cache.legacy.CacheItem ci) {
535                    this.cacheItem=ci;
536            }
537    
538            /**
539             * @return the cacheResource
540             */
541            public CacheItem getCacheItem() {
542                    return cacheItem;
543            }
544            
545    
546            // only for compatibility to other vendors
547            public String getString() {
548                    return toString();
549            }
550    
551    }