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                if(barr.length<=512) allowCompression=false;
278                
279                out = getOutputStream();
280                    
281                    
282                    if(contentLength && !(out instanceof GZIPOutputStream))ReqRspUtil.setContentLength(response,barr.length);
283                
284                    out.write(barr);
285                        out.flush();
286                        out.close();
287                
288                
289                out = null;
290            }
291            else {
292                _flush(closeConn);
293                out.close();
294                out = null;
295            }
296            closed = true;
297        } 
298        
299    
300        private OutputStream getOutputStream() throws IOException {
301            
302            if ( allowCompression && !response.isCommitted() ){
303                    
304                    String encodings = ReqRspUtil.getHeader(request,"Accept-Encoding",null);
305                if(!StringUtil.isEmpty(encodings) && encodings.indexOf("gzip")!=-1) {
306                    boolean inline=HttpServletResponseWrap.get();
307                    if(!inline) {
308                            ServletOutputStream os = response.getOutputStream();
309                            response.setHeader("Content-Encoding", "gzip");
310                                    return new GZIPOutputStream(os);
311                    }
312                }
313            }
314            return response.getOutputStream();
315            }
316        
317        
318    
319        /*private void writeCache(byte[] barr,boolean append) throws IOException {
320            cacheItem.store(barr, append);
321            //IOUtil.copy(new ByteArrayInputStream(barr), cacheItem.getResource().getOutputStream(append),true,true);
322            //MetaData.getInstance(cacheItem.getDirectory()).add(cacheItem.getName(), cacheItem.getRaw());
323            }*/
324    
325        /** 
326         * @see javax.servlet.jsp.JspWriter#getRemaining() 
327         */ 
328        public int getRemaining() { 
329            return bufferSize - buffer.length();
330        }
331    
332        /** 
333         * @see javax.servlet.jsp.JspWriter#newLine() 
334         */ 
335        public void newLine() throws IOException { 
336            println();
337        } 
338    
339        /** 
340         * @see javax.servlet.jsp.JspWriter#print(boolean) 
341         */ 
342        public void print(boolean arg) throws IOException { 
343            print(arg?new char[]{'t','r','u','e'}:new char[]{'f','a','l','s','e'}); 
344        }
345    
346        /** 
347         * @see javax.servlet.jsp.JspWriter#print(char) 
348         */ 
349        public void print(char arg) throws IOException { 
350            buffer.append(arg);
351            _check();
352        } 
353    
354        /** 
355         * @see javax.servlet.jsp.JspWriter#print(int) 
356         */ 
357        public void print(int arg) throws IOException { 
358            _print(String.valueOf(arg)); 
359        } 
360    
361        /** 
362         * @see javax.servlet.jsp.JspWriter#print(long) 
363         */ 
364        public void print(long arg) throws IOException { 
365            _print(String.valueOf(arg)); 
366    
367        } 
368    
369        /** 
370         * @see javax.servlet.jsp.JspWriter#print(float) 
371         */ 
372        public void print(float arg) throws IOException { 
373            _print(String.valueOf(arg)); 
374        } 
375    
376        /** 
377         * @see javax.servlet.jsp.JspWriter#print(double) 
378         */ 
379        public void print(double arg) throws IOException { 
380            _print(String.valueOf(arg)); 
381        } 
382    
383        /** 
384         * @see javax.servlet.jsp.JspWriter#print(java.lang.String) 
385         */ 
386        public void print(String arg) throws IOException { 
387            buffer.append(arg);
388            _check();
389        } 
390    
391        /** 
392         * @see javax.servlet.jsp.JspWriter#print(java.lang.Object) 
393         */ 
394        public void print(Object arg) throws IOException { 
395            _print(String.valueOf(arg)); 
396        } 
397    
398        /** 
399         * @see javax.servlet.jsp.JspWriter#println() 
400         */ 
401        public void println() throws IOException { 
402            _print("\n"); 
403    
404        } 
405    
406        /** 
407         * @see javax.servlet.jsp.JspWriter#println(boolean) 
408         */ 
409        public void println(boolean arg) throws IOException { 
410            print(arg?new char[]{'t','r','u','e','\n'}:new char[]{'f','a','l','s','e','\n'}); 
411        } 
412    
413        /** 
414         * @see javax.servlet.jsp.JspWriter#println(char) 
415         */ 
416        public void println(char arg) throws IOException { 
417            print(new char[]{arg,'\n'}); 
418        } 
419    
420        /** 
421         * @see javax.servlet.jsp.JspWriter#println(int) 
422         */ 
423        public void println(int arg) throws IOException { 
424            print(arg); 
425            println(); 
426        } 
427    
428        /** 
429         * @see javax.servlet.jsp.JspWriter#println(long) 
430         */ 
431        public void println(long arg) throws IOException { 
432            print(arg); 
433            println(); 
434        } 
435    
436        /** 
437         * @see javax.servlet.jsp.JspWriter#println(float) 
438         */ 
439        public void println(float arg) throws IOException { 
440            print(arg); 
441            println(); 
442        } 
443    
444        /** 
445         * @see javax.servlet.jsp.JspWriter#println(double) 
446         */ 
447        public void println(double arg) throws IOException { 
448            print(arg); 
449            println(); 
450        } 
451    
452        /** 
453         * @see javax.servlet.jsp.JspWriter#println(char[]) 
454         */ 
455        public void println(char[] arg) throws IOException { 
456            print(arg); 
457            println(); 
458        } 
459    
460        /** 
461         * @see javax.servlet.jsp.JspWriter#println(java.lang.String) 
462         */ 
463        public void println(String arg) throws IOException { 
464            _print(arg); 
465            println(); 
466        } 
467    
468        /** 
469         * @see javax.servlet.jsp.JspWriter#println(java.lang.Object) 
470         */ 
471        public void println(Object arg) throws IOException { 
472            print(arg); 
473            println(); 
474        }
475        
476        /** 
477         * @see java.io.Writer#write(char[]) 
478         */ 
479        public void write(char[] cbuf) throws IOException { 
480            print(cbuf); 
481        } 
482    
483        /** 
484         * @see java.io.Writer#write(int) 
485         */ 
486        public void write(int c) throws IOException { 
487            print(c); 
488        } 
489    
490        /** 
491         * @see java.io.Writer#write(java.lang.String, int, int) 
492         */ 
493        public void write(String str, int off, int len) throws IOException { 
494            write(str.toCharArray(),off,len);
495        } 
496    
497        /** 
498         * @see java.io.Writer#write(java.lang.String) 
499         */ 
500        public void write(String str) throws IOException { 
501            buffer.append(str);
502            _check();
503        }
504        
505        /**
506             * @see railo.runtime.writer.CFMLWriter#writeRaw(java.lang.String)
507             */
508            public void writeRaw(String str) throws IOException {
509                    _print(str);
510            }
511    
512        /**
513         * @return Returns the flushed.
514         */
515        public boolean isFlushed() {
516            return flushed;
517        }
518    
519        public void setClosed(boolean closed) {
520            this.closed=closed;
521        }
522    
523        private void _print(String arg) throws IOException { 
524            buffer.append(arg);
525            _check();
526        }
527    
528            /**
529             * @see railo.runtime.writer.CFMLWriter#getResponseStream()
530             */
531            public OutputStream getResponseStream() throws IOException {
532                    initOut();
533                    return out;
534            }
535    
536            public void doCache(railo.runtime.cache.legacy.CacheItem ci) {
537                    this.cacheItem=ci;
538            }
539    
540            /**
541             * @return the cacheResource
542             */
543            public CacheItem getCacheItem() {
544                    return cacheItem;
545            }
546            
547    
548            // only for compatibility to other vendors
549            public String getString() {
550                    return toString();
551            }
552    
553            @Override
554            public void setAllowCompression(boolean allowCompression) {
555                    this.allowCompression=allowCompression;
556            }
557            
558            
559    
560    }