1 52 53 package com.go.teaservlet; 54 55 import java.io.IOException ; 56 import java.io.OutputStream ; 57 import java.io.Writer ; 58 import java.io.PrintWriter ; 59 import java.io.UnsupportedEncodingException ; 60 import java.util.Locale ; 61 import java.util.zip.CRC32 ; 62 import javax.servlet.ServletOutputStream ; 63 import javax.servlet.http.HttpServletResponse ; 64 import com.go.teaservlet.util.FilteredHttpServletResponse; 65 import com.go.teaservlet.io.CharToByteBuffer; 66 import com.go.teaservlet.io.InternedCharToByteBuffer; 67 import com.go.trove.io.ByteData; 68 import com.go.trove.io.ByteBuffer; 69 import com.go.trove.io.DefaultByteBuffer; 70 import com.go.trove.io.ByteBufferOutputStream; 71 import com.go.trove.io.CharToByteBufferWriter; 72 import com.go.trove.log.Log; 73 import com.go.trove.util.Deflater; 74 import com.go.trove.util.DeflaterPool; 75 import com.go.trove.util.DeflaterOutputStream; 76 import com.go.tea.runtime.OutputReceiver; 77 import com.go.tea.runtime.Substitution; 78 79 87 class ApplicationResponseImpl extends FilteredHttpServletResponse 88 implements ApplicationResponse 89 { 90 private static final byte[] GZIP_HEADER = { 91 (byte)0x1f, (byte)0x8b, (byte)Deflater.DEFLATED, 0, 0, 0, 0, 0, 0, 0, }; 98 99 private static final byte[] FINAL_BLANK_HEADER = { 100 1, 0, 0, (byte)0xff, (byte)0xff 101 }; 102 103 static void writeShort(OutputStream out, int i) throws IOException { 105 out.write((byte)i); 106 out.write((byte)(i >> 8)); 107 } 108 109 static void writeInt(OutputStream out, int i) throws IOException { 111 out.write((byte)i); 112 out.write((byte)(i >> 8)); 113 out.write((byte)(i >> 16)); 114 out.write((byte)(i >> 24)); 115 } 116 117 protected final Log mLog; 118 protected final CharToByteBuffer mBuffer; 119 120 protected int mState; 123 124 private HttpContext mHttpContext; 125 private MergedApplication mApp; 126 private ApplicationRequest mRequest; 127 private ServletOutputStream mOut; 128 private PrintWriter mWriter; 129 130 private int mCompressedSegments; 131 132 ApplicationResponseImpl(HttpServletResponse response, Log log) 133 throws IOException 134 { 135 this(response, log, new DefaultByteBuffer()); 136 } 137 138 ApplicationResponseImpl(HttpServletResponse response, Log log, 139 ByteBuffer bb) 140 throws IOException 141 { 142 super(response); 143 mLog = log; 144 String encoding = response.getCharacterEncoding(); 145 if (encoding == null) { 146 encoding = "ISO-8859-1"; 147 } 148 CharToByteBuffer buffer = new FastCharToByteBuffer(bb, encoding); 149 mBuffer = new InternedCharToByteBuffer(buffer); 150 } 151 152 public ServletOutputStream getOutputStream() { 153 if (mOut == null) { 154 final OutputStream out = new ByteBufferOutputStream(mBuffer); 155 mOut = new ServletOutputStream () { 156 public void write(int b) throws IOException { 157 out.write(b); 158 } 159 public void write(byte[] buf) throws IOException { 160 out.write(buf); 161 } 162 public void write(byte[] buf, int off, int len) 163 throws IOException 164 { 165 out.write(buf, off, len); 166 } 167 }; 168 } 169 return mOut; 170 } 171 172 public PrintWriter getWriter() throws IOException { 173 if (mWriter == null) { 174 Writer w = new CharToByteBufferWriter(mBuffer); 175 mWriter = new PrintWriter (w, false); 176 } 177 return mWriter; 178 } 179 180 public void setContentLength(int len) { 181 } 183 184 public void setContentType(String type) { 185 super.setContentType(type); 186 try { 187 mBuffer.setEncoding(getCharacterEncoding()); 188 } 189 catch (IOException e) { 190 Thread t = Thread.currentThread(); 191 t.getThreadGroup().uncaughtException(t, e); 192 } 193 } 194 195 public void setBufferSize(int size) { 196 } 198 199 public int getBufferSize() { 200 return Integer.MAX_VALUE; 201 } 202 203 public void flushBuffer() { 204 } 206 207 public void resetBuffer() { 208 } 210 211 public boolean isCommitted() { 212 return false; 213 } 214 215 public void reset() { 216 super.reset(); 217 resetBuffer(); 218 } 219 220 public void setLocale(Locale locale) { 221 super.setLocale(locale); 222 mHttpContext.setLocale(locale); 223 } 224 225 public void sendError(int statusCode, String msg) throws IOException { 226 mState |= 1; 227 super.sendError(statusCode, msg); 228 } 229 230 public void sendError(int statusCode) throws IOException { 231 mState |= 1; 232 super.sendError(statusCode); 233 } 234 235 public void sendRedirect(String location) throws IOException { 236 mState |= 1; 237 super.sendRedirect(location); 238 } 239 240 public boolean isRedirectOrError() { 241 return (mState & 1) != 0; 242 } 243 244 public CharToByteBuffer getResponseBuffer() { 245 return mBuffer; 246 } 247 248 public HttpContext getHttpContext() { 249 return mHttpContext; 250 } 251 252 public void stealOutput(Substitution s, OutputReceiver receiver) 253 throws Exception 254 { 255 ((HttpContextImpl)mHttpContext).stealOutput(s, receiver); 256 } 257 258 public DetachedData execDetached(Substitution s) throws Exception { 259 DetachedResponseImpl response = 260 new DetachedResponseImpl(mResponse, mLog); 261 HttpContext newContext = 262 (HttpContext)mApp.createContext(mRequest, response); 263 264 HttpContext thisContext = mHttpContext; 265 266 newContext.setLocale(thisContext.getLocale()); 267 newContext.nullFormat(thisContext.getNullFormat()); 268 newContext.dateFormat(thisContext.getDateFormat(), 269 thisContext.getDateFormatTimeZone()); 270 newContext.numberFormat(thisContext.getNumberFormat(), 271 thisContext.getNumberFormatInfinity(), 272 thisContext.getNumberFormatNaN()); 273 274 try { 275 s.detach().substitute(newContext); 276 } 277 catch (AbortTemplateException e) { 278 } 279 return response.getData(); 280 } 281 282 public DetachedData execDetached(Command command) throws Exception { 283 DetachedResponseImpl response = 284 new DetachedResponseImpl(mResponse, mLog); 285 HttpContext newContext = 286 (HttpContext)mApp.createContext(mRequest, response); 287 288 HttpContext thisContext = mHttpContext; 289 290 newContext.setLocale(thisContext.getLocale()); 291 newContext.nullFormat(thisContext.getNullFormat()); 292 newContext.dateFormat(thisContext.getDateFormat(), 293 thisContext.getDateFormatTimeZone()); 294 newContext.numberFormat(thisContext.getNumberFormat(), 295 thisContext.getNumberFormatInfinity(), 296 thisContext.getNumberFormatNaN()); 297 298 command.execute(mRequest, response); 299 return response.getData(); 300 } 301 302 public boolean insertCommand(Command command) throws Exception { 303 return false; 304 } 305 306 public void finish() throws IOException { 307 if (mState != 0) { 308 return; 309 } 310 311 mState |= 2; 312 313 ByteData bytes = mBuffer; 314 OutputStream out = super.getOutputStream(); 315 long length = bytes.getByteCount(); 316 317 try { 318 if (mCompressedSegments == 0 || length > 0xffffffffL) { 319 if (length <= Integer.MAX_VALUE) { 320 super.setContentLength((int)length); 321 } 322 bytes.writeTo(out); 323 return; 324 } 325 326 328 setHeader("Content-Encoding", "gzip"); 329 330 LengthComputer lc = 331 new LengthComputer(mCompressedSegments * 2 + 1); 332 bytes.writeTo(lc); 333 lc.nextSegment(false); 334 335 int contentLength = lc.getLength() + 10 + 5 + 8; 338 super.setContentLength(contentLength); 339 340 out.write(GZIP_HEADER); 342 343 bytes.writeTo(new FinalOut(out, lc.mSegments)); 345 346 out.write(FINAL_BLANK_HEADER); 348 349 writeInt(out, computeCRC(bytes)); 352 writeInt(out, (int)length); 354 } 355 finally { 356 try { 357 bytes.reset(); 358 } 359 catch (IOException e) { 360 mLog.warn(e); 361 } 362 } 363 } 364 365 void setAppAndRequest(MergedApplication app, ApplicationRequest request) { 368 mApp = app; 369 mRequest = request; 370 } 371 372 void setHttpContext(HttpContext context) { 375 mHttpContext = context; 376 } 377 378 void appendCompressed(ByteData compressed, ByteData original) 380 throws IOException 381 { 382 mCompressedSegments++; 383 mBuffer.appendSurrogate(new CompressedData(compressed, original)); 384 } 385 386 private int computeCRC(ByteData bytes) throws IOException { 387 final CRC32 crc = new CRC32 (); 388 389 OutputStream out = new OutputStream () { 390 public void write(int b) { 391 crc.update(b); 392 } 393 394 public void write(byte[] b) { 395 crc.update(b, 0, b.length); 396 } 397 398 public void write(byte[] b, int off, int len) { 399 crc.update(b, off, len); 400 } 401 }; 402 403 bytes.writeTo(out); 404 return (int)crc.getValue(); 405 } 406 407 private static class CompressedData implements ByteData { 408 private final ByteData mCompressed; 409 private final ByteData mOriginal; 410 411 CompressedData(ByteData compressed, ByteData original) { 412 mCompressed = compressed; 413 mOriginal = original; 414 } 415 416 public long getByteCount() throws IOException { 417 return mOriginal.getByteCount(); 418 } 419 420 public void writeTo(OutputStream out) throws IOException { 421 if (out instanceof Segmented) { 422 ((Segmented)out).nextSegment(true); 423 mCompressed.writeTo(out); 424 ((Segmented)out).nextSegment(false); 425 } 426 else { 427 mOriginal.writeTo(out); 428 } 429 } 430 431 public void reset() throws IOException { 432 mOriginal.reset(); 433 } 434 } 435 436 private interface Segmented { 437 void nextSegment(boolean preCompressed); 438 } 439 440 private static class LengthComputer extends OutputStream 443 implements Segmented 444 { 445 int[] mSegments; 446 int mSegCount; 447 448 private int mCurrentSegment; 450 private int mSign = 1; 452 453 LengthComputer(int segmentCount) { 454 mSegments = new int[segmentCount]; 455 } 456 457 public void write(int b) { 458 mCurrentSegment += mSign; 459 } 460 461 public void write(byte[] b) { 462 mCurrentSegment += b.length * mSign; 463 } 464 465 public void write(byte[] b, int off, int len) { 466 mCurrentSegment += len * mSign; 467 } 468 469 public void nextSegment(boolean preCompressed) { 470 mSegments[mSegCount++] = mCurrentSegment; 471 mSign = preCompressed ? -1 : 1; 472 mCurrentSegment = 0; 473 } 474 475 int getLength() { 476 int length = 0; 477 for (int i=0; i<mSegCount; i++) { 478 int segLen = mSegments[i]; 479 if (segLen < 0) { 480 length += -segLen; 481 } 482 else { 483 length += segLen + ((65534 + segLen) / 65535) * 5; 486 } 487 } 488 return length; 489 } 490 } 491 492 private static class FinalOut extends OutputStream implements Segmented { 495 private OutputStream mOut; 496 private int[] mSegments; 497 private int mCursor; 498 499 private int mBlockLen; 502 503 FinalOut(OutputStream out, int[] segments) { 504 mOut = out; 505 mSegments = segments; 506 } 507 508 public void write(int b) throws IOException { 509 if (mBlockLen < 0) { 510 mOut.write(b); 511 return; 512 } 513 if (nextBlock(1) > 0) { 514 mOut.write(b); 515 mBlockLen--; 516 } 517 } 518 519 public void write(byte[] b) throws IOException { 520 write(b, 0, b.length); 521 } 522 523 public void write(byte[] b, int off, int len) throws IOException { 524 if (mBlockLen < 0) { 525 mOut.write(b, off, len); 526 return; 527 } 528 while (len > 0) { 529 int amt = nextBlock(len); 530 if (amt <= 0) { 531 break; 532 } 533 mOut.write(b, off, amt); 534 len -= amt; 535 off += amt; 536 mBlockLen -= amt; 537 } 538 } 539 540 public void nextSegment(boolean preCompressed) { 541 mCursor++; 542 mBlockLen = preCompressed ? -1 : 0; 543 } 544 545 private int nextBlock(int needed) throws IOException { 547 if (mBlockLen > 0) { 548 return mBlockLen <= needed ? mBlockLen : needed; 549 } 550 551 int blockLen = mSegments[mCursor]; 553 554 if (blockLen > 65535) { 555 blockLen = 65535; 556 } 557 558 mSegments[mCursor] -= blockLen; 559 560 mOut.write(0); 562 writeShort(mOut, blockLen); 563 writeShort(mOut, 65535 - blockLen); 564 565 mBlockLen = blockLen; 566 567 return blockLen < needed ? blockLen : needed; 568 } 569 } 570 } 571 | Popular Tags |