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 }