1 23 24 package com.sun.enterprise.web.connector.grizzly; 25 26 import java.io.File ; 27 import java.io.FileInputStream ; 28 import java.io.IOException ; 29 import java.nio.ByteBuffer ; 30 import java.nio.MappedByteBuffer ; 31 import java.nio.channels.FileChannel ; 32 import java.nio.channels.SocketChannel ; 33 import java.util.ArrayList ; 34 import java.util.concurrent.ConcurrentHashMap ; 35 import java.util.concurrent.ConcurrentLinkedQueue ; 36 import java.util.concurrent.Future ; 37 import java.util.concurrent.ScheduledThreadPoolExecutor ; 38 import java.util.concurrent.TimeUnit ; 39 import org.apache.catalina.util.ServerInfo; 40 import org.apache.tomcat.util.http.MimeHeaders; 41 42 43 49 public final class FileCache{ 50 51 public final static String DEFAULT_SERVLET_NAME = "default"; 52 53 54 57 private final ConcurrentHashMap <String ,FileCacheEntry> fileCache = 58 new ConcurrentHashMap <String ,FileCacheEntry>(); 59 60 61 64 private final static ByteBuffer nullByteBuffer = 65 (ByteBuffer ) ByteBuffer.allocate(0); 66 67 68 71 private final static String NEWLINE = "\r\n"; 72 73 74 77 public final static String OK = "HTTP/1.1 200 OK" + NEWLINE; 78 79 82 private int port = 8080; 83 84 85 88 private ScheduledThreadPoolExecutor cacheResourcesThread 89 = new ScheduledThreadPoolExecutor (1, 90 new GrizzlyThreadFactory("FileCacheThread-" + port, 91 1,Thread.NORM_PRIORITY)); 92 93 94 97 private ConcurrentLinkedQueue <FileCacheEntry> cacheManager; 98 99 100 103 private int secondsMaxAge = -1; 104 105 106 109 private int maxCacheEntries = 1024; 110 111 112 115 private long minEntrySize = 2048; 116 117 118 121 private long maxEntrySize = 537600; 122 123 124 127 private long maxLargeFileCacheSize = 10485760; 128 129 130 133 private long maxSmallFileCacheSize = 1048576; 134 135 136 139 private static long mappedMemorySize = 0; 140 141 142 145 private static long heapSize = 0; 146 147 148 151 private boolean isEnabled = true; 152 153 154 157 private boolean isLargeFileCacheEnabled = true; 158 159 160 163 private static boolean isMonitoringEnabled = false; 164 165 166 169 private int openCacheEntries = 0; 170 171 172 175 private int maxOpenCacheEntries = 0; 176 177 178 181 private long maxHeapCacheSize = 0; 182 183 184 187 private long maxMappedMemory = 0; 188 189 190 193 private int countHits = 0; 194 195 196 199 private int countMisses = 0; 200 201 202 205 private int countCacheHits; 206 207 208 211 private int countCacheMisses; 212 213 214 217 private int countMappedHits; 218 219 220 223 private int countMappedMisses; 224 225 227 228 232 public synchronized void add(String mappedServlet, String baseDir, 233 String requestURI, MimeHeaders headers, boolean xPoweredBy){ 234 235 if ( fileCache.get(requestURI) != null) return; 236 237 if ( fileCache.size() > maxCacheEntries) { 239 return; 240 } 241 242 if ( mappedServlet.equals(DEFAULT_SERVLET_NAME) ){ 243 File file = new File (baseDir + requestURI); 244 ByteBuffer bb = mapFile(file); 245 246 if (bb == null) 250 bb = nullByteBuffer; 251 252 FileCacheEntry entry = cacheManager.poll(); 253 if ( entry == null){ 254 entry = new FileCacheEntry(); 255 } 256 entry.bb = bb; 257 entry.requestURI = requestURI; 258 259 if ( bb != nullByteBuffer){ 260 entry.lastModified = headers.getHeader("Last-Modified"); 261 entry.contentType = headers.getHeader("content-type"); 262 entry.xPoweredBy = xPoweredBy; 263 entry.isInHeap = (file.length() < minEntrySize); 264 entry.date = headers.getHeader("Date"); 265 entry.Etag = headers.getHeader("Etag"); 266 entry.keepAlive = "Keep-Alive".equalsIgnoreCase( 267 headers.getHeader("Connection"))? true: false; 268 269 configHeaders(entry); 270 271 if ( isMonitoringEnabled ) { 272 openCacheEntries++; 273 274 if ( openCacheEntries > maxOpenCacheEntries){ 275 maxOpenCacheEntries = openCacheEntries; 276 } 277 278 if ( heapSize > maxHeapCacheSize){ 279 maxHeapCacheSize = heapSize; 280 } 281 282 if ( mappedMemorySize > maxMappedMemory){ 283 maxMappedMemory = mappedMemorySize; 284 } 285 } 286 287 if ( secondsMaxAge > 0 ) { 288 entry.future = cacheResourcesThread.schedule(entry, 289 secondsMaxAge, TimeUnit.SECONDS); 290 } 291 } 292 fileCache.put(requestURI,entry); 293 } 294 } 295 296 297 301 private final ByteBuffer mapFile(File file){ 302 FileChannel fileChannel = null; 303 FileInputStream stream = null; 304 try { 305 stream = new FileInputStream (file); 306 fileChannel = stream.getChannel(); 307 308 long size = fileChannel.size(); 309 310 if ( !isLargeFileCacheEnabled ) { 311 if ( size > minEntrySize ) { 313 return null; 314 } 315 } else if ( size > maxEntrySize){ 316 return null; 317 } 318 319 if ( size > minEntrySize ) 320 mappedMemorySize+= size; 321 else 322 heapSize+= size; 323 324 if ( mappedMemorySize > maxLargeFileCacheSize ) { 326 mappedMemorySize-= size; 327 return null; 328 } else if ( heapSize > maxSmallFileCacheSize ) { 329 heapSize-= size; 330 return null; 331 } 332 333 ByteBuffer bb = 334 fileChannel.map(FileChannel.MapMode.READ_ONLY,0,size); 335 336 if ( size < minEntrySize) { 337 ((MappedByteBuffer )bb).load(); 338 } 339 return bb; 340 } catch (IOException ioe) { 341 return null; 342 } finally { 343 if (stream != null) { 344 try { 345 stream.close(); 346 } catch (IOException ioe) { 347 } 348 } 349 if (fileChannel != null) { 350 try { 351 fileChannel.close(); 352 } catch (IOException ioe) { 353 } 354 } 355 } 356 } 357 358 359 362 private final FileCacheEntry map(byte[] requestBytes,int start, int length){ 363 String uri = ""; 364 FileCacheEntry entry = null; 365 366 if ( fileCache.size() != 0 ){ 367 uri = new String (requestBytes,start,length); 368 entry = fileCache.get(uri); 369 370 if ( isMonitoringEnabled) { 371 if (entry != null && entry.bb != null 372 && entry.bb != nullByteBuffer){ 373 if ( entry.isInHeap ) 374 countCacheHits++; 375 else 376 countMappedHits++; 377 378 countHits++; 379 380 } else { 381 countMisses++; 382 } 383 } 384 } 385 return entry; 386 } 387 388 389 392 public boolean sendCache(byte[] req, int start, int length, 393 SocketChannel socketChannel, boolean keepAlive){ 394 395 try{ 396 FileCacheEntry entry = map(req,start,length); 397 if ( entry != null && entry.bb != nullByteBuffer){ 398 sendCache(socketChannel,entry); 399 return true; 400 } 401 } catch (IOException ex){ 402 SelectorThread.logger() 403 .fine("File Cache: " + ex.getMessage()); 404 return true; 405 } catch (Throwable t){ 406 SelectorThread.logger() 409 .fine("File Cache thread race: " + t.getMessage()); 410 } 411 return false; 412 } 413 414 415 418 public void setCacheManager(ConcurrentLinkedQueue cacheManager){ 419 this.cacheManager = cacheManager; 420 } 421 422 423 425 426 429 private void sendCache(SocketChannel socketChannel, FileCacheEntry entry) 430 throws IOException { 431 432 OutputWriter.flushChannel(socketChannel, entry.headerBuffer.slice()); 433 OutputWriter.flushChannel(socketChannel, entry.bb.slice()); 434 } 435 436 437 440 private void configHeaders(FileCacheEntry entry) { 441 if ( entry.headerBuffer == null ) { 442 entry.headerBuffer = 443 ByteBuffer.allocate(Constants.CHANNEL_BYTE_SIZE); 444 } 445 446 StringBuffer sb = new StringBuffer (); 447 sb.append(OK); 448 if ( entry.xPoweredBy){ 449 appendHeaderValue(sb,"X-Powered-By", "Servlet/2.5"); 450 } 451 appendHeaderValue(sb, "ETag", entry.Etag); 452 appendHeaderValue(sb,"Last-Modified", entry.lastModified); 453 appendHeaderValue(sb,"Content-Type", entry.contentType); 454 appendHeaderValue(sb,"Content-Length", entry.bb.capacity() + ""); 455 appendHeaderValue(sb,"Date", entry.date); 456 appendHeaderValue(sb,"Server", ServerInfo.getServerInfo()); 457 appendHeaderValue(sb,"Connection", 458 (entry.keepAlive ? "Keep-Alive":"close")); 459 sb.append(NEWLINE); 460 entry.headerBuffer.put(sb.toString().getBytes()); 461 entry.headerBuffer.flip(); 462 } 463 464 465 468 private void appendHeaderValue(StringBuffer sb,String name, String value) { 469 sb.append(name); 470 sb.append(": "); 471 sb.append(value); 472 sb.append(NEWLINE); 473 } 474 475 476 protected final class FileCacheEntry implements Runnable { 477 public String requestURI; 478 public String lastModified; 479 public String contentType; 480 public boolean keepAlive; 481 public ByteBuffer bb; 482 public ByteBuffer headerBuffer; 483 public boolean xPoweredBy; 484 public boolean isInHeap = false; 485 public String date; 486 public String Etag; 487 public Future future; 488 489 public void run(){ 490 fileCache.remove(requestURI); 491 492 if (requestURI == null) return; 493 494 if (headerBuffer != null) { 495 496 501 if ( headerBuffer.position() !=0 || bb.position() != 0 ){ 502 future = cacheResourcesThread 503 .schedule(this, 10, TimeUnit.SECONDS); 504 return; 505 } 506 507 if ( !isInHeap ) 508 mappedMemorySize -= bb.limit(); 509 else 510 heapSize -= bb.limit(); 511 512 bb = null; 513 headerBuffer = null; 514 openCacheEntries--; 515 } 516 517 if ( future != null ) { 518 future.cancel(false); 519 future = null; 520 } 521 requestURI = null; 522 cacheManager.offer(this); 523 } 524 } 525 526 527 529 530 534 public int getFlagEnabled() { 535 return (isEnabled == true?1:0); 536 } 537 538 539 543 public int getSecondsMaxAge() { 544 return secondsMaxAge; 545 } 546 547 548 552 public long getCountEntries() { 553 return fileCache.size(); 554 } 555 556 557 561 public long getMaxEntries() { 562 return maxCacheEntries; 563 } 564 565 566 570 public long getCountOpenEntries() { 571 return openCacheEntries; 572 } 573 574 575 579 public long getMaxOpenEntries() { 580 return maxOpenCacheEntries; 581 } 582 583 584 588 public long getSizeHeapCache() { 589 return heapSize; 590 } 591 592 593 597 public long getMaxHeapCacheSize() { 598 return maxHeapCacheSize; 599 } 600 601 602 606 public static long getSizeMmapCache() { 607 return mappedMemorySize; 608 } 609 610 611 615 public long getMaxMmapCacheSize() { 616 return maxMappedMemory; 617 } 618 619 620 624 public long getCountHits() { 625 return countHits; 626 } 627 628 629 633 public long getCountMisses() { 634 return countMisses; 635 } 636 637 638 642 public long getCountInfoHits() { 643 return countCacheHits; 644 } 645 646 647 651 public long getCountInfoMisses() { 652 return countCacheMisses; 653 } 654 655 656 660 public long getCountContentHits() { 661 return countMappedHits; 662 } 663 664 665 669 public int getCountContentMisses() { 670 return countMappedMisses; 671 } 672 673 675 676 679 public static void setIsMonitoringEnabled(boolean isMe){ 680 isMonitoringEnabled = isMe; 681 } 682 683 684 685 689 public void setSecondsMaxAge(int sMaxAges){ 690 secondsMaxAge = sMaxAges; 691 } 692 693 694 697 public void setMaxCacheEntries(int mEntries){ 698 maxCacheEntries = mEntries; 699 } 700 701 702 705 public int getMaxCacheEntries(){ 706 return maxCacheEntries; 707 } 708 709 710 713 public void setMinEntrySize(long mSize){ 714 minEntrySize = mSize; 715 } 716 717 718 721 public long getMinEntrySize(){ 722 return minEntrySize; 723 } 724 725 726 729 public void setMaxEntrySize(long mEntrySize){ 730 maxEntrySize = mEntrySize; 731 } 732 733 734 737 public long getMaxEntrySize(){ 738 return maxEntrySize; 739 } 740 741 742 745 public void setMaxLargeCacheSize(long mCacheSize){ 746 maxLargeFileCacheSize = mCacheSize; 747 } 748 749 750 753 public long getMaxLargeCacheSize(){ 754 return maxLargeFileCacheSize; 755 } 756 757 758 761 public void setMaxSmallCacheSize(long mCacheSize){ 762 maxSmallFileCacheSize = mCacheSize; 763 } 764 765 766 769 public long getMaxSmallCacheSize(){ 770 return maxSmallFileCacheSize; 771 } 772 773 774 777 public boolean isEnabled(){ 778 return isEnabled; 779 } 780 781 782 785 public void setIsEnabled(boolean isEnabled){ 786 this.isEnabled = isEnabled; 787 } 788 789 790 793 public void setLargeFileCacheEnabled(boolean isLargeEnabled){ 794 this.isLargeFileCacheEnabled = isLargeEnabled; 795 } 796 797 798 801 public boolean getLargeFileCacheEnabled(){ 802 return isLargeFileCacheEnabled; 803 } 804 805 806 809 public ConcurrentHashMap getCache(){ 810 return fileCache; 811 } 812 813 } 814 | Popular Tags |