1 29 30 package com.caucho.vfs; 31 32 import com.caucho.loader.EnvironmentLocal; 33 import com.caucho.make.CachedDependency; 34 import com.caucho.util.Alarm; 35 import com.caucho.util.CacheListener; 36 import com.caucho.util.Log; 37 import com.caucho.util.LruCache; 38 39 import java.io.FileNotFoundException ; 40 import java.io.IOException ; 41 import java.io.InputStream ; 42 import java.lang.ref.SoftReference ; 43 import java.security.cert.Certificate ; 44 import java.util.ArrayList ; 45 import java.util.Enumeration ; 46 import java.util.Iterator ; 47 import java.util.jar.JarEntry ; 48 import java.util.jar.JarFile ; 49 import java.util.jar.Manifest ; 50 import java.util.logging.Level ; 51 import java.util.logging.Logger ; 52 import java.util.zip.ZipEntry ; 53 import java.util.zip.ZipFile ; 54 import java.util.zip.ZipInputStream ; 55 56 63 public class Jar implements CacheListener { 64 private static final Logger log = Log.open(Jar.class); 65 66 private static LruCache<Path,Jar> _jarCache; 67 68 private static EnvironmentLocal<Integer > _jarSize 69 = new EnvironmentLocal<Integer >("caucho.vfs.jar-size"); 70 71 private Path _backing; 72 73 private JarDepend _depend; 74 75 private long _lastModified; 77 private long _length; 79 private long _lastTime; 81 82 private SoftReference <JarFile > _jarFileRef; 84 private long _jarLastModified; 86 87 private SoftReference <JarFile > _closeJarFileRef; 89 90 95 private Jar(Path backing) 96 { 97 _backing = backing; 98 } 99 100 104 static Jar create(Path backing) 105 { 106 if (_jarCache == null) { 107 int size = 256; 108 109 Integer iSize = _jarSize.get(); 110 111 if (iSize != null) 112 size = iSize.intValue(); 113 114 _jarCache = new LruCache<Path,Jar>(size); 115 } 116 117 Jar jar = _jarCache.get(backing); 118 if (jar == null) { 119 jar = new Jar(backing); 120 jar = _jarCache.putIfNew(backing, jar); 121 } 122 123 return jar; 124 } 125 126 130 static Jar getJar(Path backing) 131 { 132 if (_jarCache != null) 133 return _jarCache.get(backing); 134 else 135 return null; 136 } 137 138 142 public static PersistentDependency createDepend(Path backing) 143 { 144 Jar jar = create(backing); 145 146 return jar.getDepend(); 147 } 148 149 153 public static PersistentDependency createDepend(Path backing, long digest) 154 { 155 Jar jar = create(backing); 156 157 return new JarDigestDepend(jar.getJarDepend(), digest); 158 } 159 160 163 Path getBacking() 164 { 165 return _backing; 166 } 167 168 171 public PersistentDependency getDepend() 172 { 173 return getJarDepend(); 174 } 175 176 179 private JarDepend getJarDepend() 180 { 181 if (_depend == null || _depend.isModified()) 182 _depend = new JarDepend(new Depend(getBacking())); 183 184 return _depend; 185 } 186 187 192 public Manifest getManifest() 193 throws IOException 194 { 195 Manifest manifest; 196 197 synchronized (this) { 198 JarFile jarFile = getJarFile(); 199 200 if (jarFile == null) 201 manifest = null; 202 else 203 manifest = jarFile.getManifest(); 204 } 205 206 closeJarFile(); 207 208 return manifest; 209 } 210 211 214 public Certificate []getCertificates(String path) 215 { 216 if (path.length() > 0 && path.charAt(0) == '/') 217 path = path.substring(1); 218 219 try { 220 if (! _backing.canRead()) 221 return null; 222 223 JarFile jarFile = new JarFile (_backing.getNativePath()); 224 JarEntry entry; 225 InputStream is = null; 226 227 try { 228 entry = (JarEntry ) jarFile.getEntry(path); 229 230 if (entry != null) { 231 is = jarFile.getInputStream(entry); 232 233 while (is.skip(65536) > 0) { 234 } 235 236 is.close(); 237 238 return entry.getCertificates(); 239 } 240 } finally { 241 jarFile.close(); 242 } 243 } catch (IOException e) { 244 log.log(Level.FINE, e.toString(), e); 245 246 return null; 247 } 248 249 return null; 250 } 251 252 257 public boolean exists(String path) 258 { 259 boolean exists; 260 261 synchronized (this) { 262 exists = getSafeJarEntry(path) != null; 263 } 264 265 closeJarFile(); 266 267 return exists; 268 } 269 270 275 public boolean isDirectory(String path) 276 { 277 boolean isDirectory; 278 279 synchronized (this) { 280 if (! path.endsWith("/")) 281 path = path + '/'; 282 283 ZipEntry entry = getSafeJarEntry(path); 284 285 if (entry == null && (path.equals("/") || path.equals(""))) 286 isDirectory = true; 287 else 288 isDirectory = entry != null && entry.isDirectory(); 289 } 290 291 closeJarFile(); 292 293 return isDirectory; 294 } 295 296 301 public boolean isFile(String path) 302 { 303 boolean isFile; 304 305 synchronized (this) { 306 ZipEntry entry = getSafeJarEntry(path); 307 308 isFile = entry != null && ! entry.isDirectory(); 309 } 310 311 closeJarFile(); 312 313 return isFile; 314 } 315 316 322 public long getLastModified(String path) 323 { 324 long lastModified; 325 326 synchronized (this) { 327 ZipEntry entry = getSafeJarEntry(path); 328 329 if (entry == null) 330 lastModified = 0; 331 else 332 lastModified = _lastModified; 333 } 334 335 closeJarFile(); 336 337 return lastModified; 338 } 339 340 346 public long getLength(Path path) 347 { 348 long length; 349 350 synchronized (this) { 351 ZipEntry entry = getSafeJarEntry(path.getPath()); 352 353 if (entry == null) 354 length = -1; 355 else 356 length = entry.getSize(); 357 } 358 359 closeJarFile(); 360 361 return length; 362 } 363 364 367 public boolean canRead(String path) 368 { 369 boolean canRead; 370 371 synchronized (this) { 372 canRead = _backing.canRead() && isFile(path); 373 } 374 375 closeJarFile(); 376 377 return canRead; 378 } 379 380 383 public boolean canWrite(String path) 384 { 385 return false; 386 } 387 388 391 public String []list(String pathName) throws IOException 392 { 393 if (pathName.length() > 0 && ! pathName.endsWith("/")) 394 pathName = pathName + "/"; 395 if (pathName.startsWith("/")) 396 pathName = pathName.substring(1); 397 398 ArrayList <String > list = new ArrayList <String >(); 399 400 String []result = null; 401 402 synchronized (this) { 403 JarFile jarFile = getJarFile(); 404 405 if (jarFile != null) { 406 Enumeration e = jarFile.entries(); 407 while (e.hasMoreElements()) { 408 ZipEntry entry = (ZipEntry ) e.nextElement(); 409 String name = entry.getName(); 410 411 if (name.startsWith(pathName) && ! name.equals(pathName)) { 412 String subName = name.substring(pathName.length()); 413 414 int p = subName.indexOf('/'); 415 416 if (p < 0) 417 list.add(subName); 418 else if (p == subName.length() - 1) 419 list.add(subName.substring(0, p)); 420 } 421 } 422 423 result = (String []) list.toArray(new String [list.size()]); 424 } 425 } 426 427 closeJarFile(); 428 429 if (result != null) { 430 return result; 431 } 432 433 ReadStream backingIs = _backing.openRead(); 434 ZipInputStream is = new ZipInputStream (backingIs); 435 436 try { 437 ZipEntry entry; 438 439 while ((entry = is.getNextEntry()) != null) { 440 String name = entry.getName(); 441 442 if (name.startsWith(pathName) && ! name.equals(pathName)) { 443 String subName = name.substring(pathName.length()); 444 445 int p = subName.indexOf('/'); 446 447 if (p < 0) 448 list.add(subName); 449 else if (p == subName.length() - 1) 450 list.add(subName.substring(0, p)); 451 } 452 } 453 } finally { 454 is.close(); 455 backingIs.close(); 456 } 457 458 return (String []) list.toArray(new String [list.size()]); 459 } 460 461 466 public StreamImpl openReadImpl(Path path) throws IOException 467 { 468 String pathName = path.getPath(); 469 470 if (pathName.length() > 0 && pathName.charAt(0) == '/') 471 pathName = pathName.substring(1); 472 473 ZipFile zipFile = new ZipFile (_backing.getNativePath()); 474 ZipEntry entry; 475 InputStream is = null; 476 477 try { 478 entry = zipFile.getEntry(pathName); 479 if (entry != null) { 480 is = zipFile.getInputStream(entry); 481 482 return new ZipStreamImpl(zipFile, is, null, path); 483 } 484 else { 485 throw new FileNotFoundException (path.toString()); 486 } 487 } finally { 488 if (is == null) { 489 zipFile.close(); 490 } 491 } 492 } 493 494 public String toString() 495 { 496 return _backing.toString(); 497 } 498 499 503 public static void clearJarCache() 504 { 505 LruCache<Path,Jar> jarCache = _jarCache; 506 507 if (jarCache == null) 508 return; 509 510 ArrayList <Jar> jars = new ArrayList <Jar>(); 511 512 synchronized (jarCache) { 513 Iterator <Jar> iter = jarCache.values(); 514 515 while (iter.hasNext()) 516 jars.add(iter.next()); 517 } 518 519 for (int i = 0; i < jars.size(); i++) { 520 Jar jar = jars.get(i); 521 522 if (jar != null) 523 jar.clearCache(); 524 } 525 } 526 527 530 public void clearCache() 531 { 532 JarFile jarFile = null; 533 534 synchronized (this) { 535 SoftReference <JarFile > jarFileRef = _jarFileRef; 536 _jarFileRef = null; 537 538 if (jarFileRef != null) 539 jarFile = jarFileRef.get(); 540 } 541 542 try { 543 if (jarFile != null) 544 jarFile.close(); 545 } catch (Exception e) { 546 } 547 } 548 549 556 private ZipEntry getSafeJarEntry(String path) 557 { 558 try { 559 return getJarEntry(path); 560 } catch (Throwable e) { 561 _jarLastModified = 0; 562 563 try { 564 closeJarFile(); 565 } catch (Throwable e1) { 566 } 567 568 try { 569 return getJarEntry(path); 570 } catch (Throwable e1) { 571 log.log(Level.INFO, e1.toString(), e1); 572 573 return null; 574 } 575 } 576 } 577 578 584 private ZipEntry getJarEntry(String path) 585 throws IOException 586 { 587 if (path.startsWith("/")) 588 path = path.substring(1); 589 590 JarFile jarFile = getJarFile(); 591 592 if (jarFile != null) 593 return jarFile.getEntry(path); 594 else 595 return null; 596 } 597 598 604 private JarFile getJarFile() 605 throws IOException 606 { 607 JarFile jarFile = null; 608 609 if (getLastModifiedImpl() == _jarLastModified) { 610 SoftReference <JarFile > jarFileRef = _jarFileRef; 611 612 if (jarFileRef != null) { 613 jarFile = jarFileRef.get(); 614 615 if (jarFile != null) 616 return jarFile; 617 } 618 } 619 620 SoftReference <JarFile > oldJarRef = _jarFileRef; 621 _jarFileRef = null; 622 623 JarFile oldFile = null; 624 if (oldJarRef == null) { 625 } 626 else if (_closeJarFileRef == null) 627 _closeJarFileRef = oldJarRef; 628 else 629 oldFile = oldJarRef.get(); 630 631 if (oldFile != null) { 632 try { 633 oldFile.close(); 634 } catch (Throwable e) { 635 e.printStackTrace(); 636 } 637 } 638 639 if (_backing.getScheme().equals("file") && _backing.canRead()) { 640 jarFile = new JarFile (_backing.getNativePath()); 641 642 _jarFileRef = new SoftReference <JarFile >(jarFile); 643 _jarLastModified = getLastModifiedImpl(); 644 } 645 646 return jarFile; 647 } 648 649 656 private long getLastModifiedImpl() 657 { 658 long now = Alarm.getCurrentTime(); 659 660 if (now == _lastTime) 661 return _lastModified; 662 663 long oldLastModified = _lastModified; 664 long oldLength = _length; 665 666 _lastModified = _backing.getLastModified(); 667 _length = _backing.getLength(); 668 _lastTime = now; 669 670 if (_lastModified != oldLastModified || _length != oldLength) { 672 SoftReference <JarFile > oldFileRef = _jarFileRef; 673 674 _jarFileRef = null; 675 _jarLastModified = 0; 676 677 JarFile oldCloseFile = null; 678 if (_closeJarFileRef != null) 679 oldCloseFile = _closeJarFileRef.get(); 680 681 _closeJarFileRef = oldFileRef; 682 683 if (oldCloseFile != null) { 684 try { 685 oldCloseFile.close(); 686 } catch (Throwable e) { 687 } 688 } 689 } 690 691 return _lastModified; 692 } 693 694 697 private void closeJarFile() 698 { 699 JarFile jarFile = null; 700 701 synchronized (this) { 702 if (_closeJarFileRef != null) 703 jarFile = _closeJarFileRef.get(); 704 _closeJarFileRef = null; 705 } 706 707 if (jarFile != null) { 708 try { 709 jarFile.close(); 710 } catch (IOException e) { 711 log.log(Level.WARNING, e.toString(), e); 712 } 713 } 714 } 715 716 public void close() 717 { 718 removeEvent(); 719 } 720 721 public void removeEvent() 722 { 723 JarFile jarFile = null; 724 725 synchronized (this) { 726 if (_jarFileRef != null) 727 jarFile = _jarFileRef.get(); 728 729 _jarFileRef = null; 730 } 731 732 try { 733 if (jarFile != null) 734 jarFile.close(); 735 } catch (Throwable e) { 736 log.log(Level.FINE, e.toString(), e); 737 } 738 739 closeJarFile(); 740 } 741 742 public boolean equals(Object o) 743 { 744 if (this == o) 745 return true; 746 else if (o == null || ! getClass().equals(o.getClass())) 747 return false; 748 749 Jar jar = (Jar) o; 750 751 return _backing.equals(jar._backing); 752 } 753 754 757 static class ZipStreamImpl extends StreamImpl { 758 private ZipFile _zipFile; 759 private InputStream _zis; 760 private InputStream _is; 761 762 769 ZipStreamImpl(ZipFile file, InputStream zis, InputStream is, Path path) 770 { 771 _zipFile = file; 772 _zis = zis; 773 _is = is; 774 775 setPath(path); 776 } 777 778 781 public boolean canRead() { return true; } 782 783 public int getAvailable() throws IOException 784 { 785 if (_zis == null) 786 return -1; 787 else 788 return _zis.available(); 789 } 790 791 public int read(byte []buf, int off, int len) throws IOException 792 { 793 int readLen = _zis.read(buf, off, len); 794 795 return readLen; 796 } 797 798 public void close() throws IOException 799 { 800 ZipFile zipFile = _zipFile; 801 _zipFile = null; 802 803 InputStream zis = _zis; 804 _zis = null; 805 806 InputStream is = _is; 807 _is = null; 808 809 try { 810 if (zis != null) 811 zis.close(); 812 } catch (Throwable e) { 813 } 814 815 try { 816 if (zipFile != null) 817 zipFile.close(); 818 } catch (Throwable e) { 819 } 820 821 if (is != null) 822 is.close(); 823 } 824 825 protected void finalize() 826 throws IOException 827 { 828 close(); 829 } 830 } 831 832 static class JarDepend extends CachedDependency 833 implements PersistentDependency { 834 private Depend _depend; 835 private boolean _isDigestModified; 836 837 842 JarDepend(Depend depend) 843 { 844 _depend = depend; 845 } 846 847 852 JarDepend(Depend depend, long digest) 853 { 854 _depend = depend; 855 856 _isDigestModified = _depend.getDigest() != digest; 857 } 858 859 862 Depend getDepend() 863 { 864 return _depend; 865 } 866 867 870 public boolean isModifiedImpl() 871 { 872 return _isDigestModified || _depend.isModified(); 873 } 874 875 878 public String getJavaCreateString() 879 { 880 String sourcePath = _depend.getPath().getPath(); 881 long digest = _depend.getDigest(); 882 883 return ("new com.caucho.vfs.Jar.createDepend(" + 884 "com.caucho.vfs.Vfs.lookup(\"" + sourcePath + "\"), " + 885 digest + "L)"); 886 } 887 888 public String toString() 889 { 890 return "Jar$JarDepend[" + _depend.getPath() + "]"; 891 } 892 } 893 894 static class JarDigestDepend implements PersistentDependency { 895 private JarDepend _jarDepend; 896 private Depend _depend; 897 private boolean _isDigestModified; 898 899 904 JarDigestDepend(JarDepend jarDepend, long digest) 905 { 906 _jarDepend = jarDepend; 907 _depend = jarDepend.getDepend(); 908 909 _isDigestModified = _depend.getDigest() != digest; 910 } 911 912 915 public boolean isModified() 916 { 917 return _isDigestModified || _jarDepend.isModified(); 918 } 919 920 923 public String getJavaCreateString() 924 { 925 String sourcePath = _depend.getPath().getPath(); 926 long digest = _depend.getDigest(); 927 928 return ("new com.caucho.vfs.Jar.createDepend(" + 929 "com.caucho.vfs.Vfs.lookup(\"" + sourcePath + "\"), " + 930 digest + "L)"); 931 } 932 } 933 } 934 | Popular Tags |