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 }