1 6 21 22 package de.schlichtherle.io; 23 24 import de.schlichtherle.io.ArchiveFileSystem.Delta; 25 import de.schlichtherle.io.archive.*; 26 import de.schlichtherle.io.archive.spi.*; 27 import de.schlichtherle.io.rof.*; 28 import de.schlichtherle.key.*; 29 30 import java.io.*; 31 import java.lang.ref.*; 32 import java.util.*; 33 import java.util.logging.*; 34 35 import javax.swing.Icon ; 36 37 92 abstract class ArchiveController implements Archive, FileConstants { 93 94 98 private static final String CLASS_NAME 99 = "de/schlichtherle/io/ArchiveController".replace('/', '.'); private static final Logger logger = Logger.getLogger(CLASS_NAME, CLASS_NAME); 101 102 109 private static final Map controllers = new WeakHashMap(); 110 111 private static final LiveStatistics liveStats = new LiveStatistics(); 112 113 118 private static final CopyLock copyLock = new CopyLock(); 119 120 private static final Comparator REVERSE_CONTROLLERS = new Comparator() { 121 public int compare(Object o1, Object o2) { 122 return ((ArchiveController) o2).getTarget().compareTo( 123 ((ArchiveController) o1).getTarget()); 124 } 125 }; 126 127 131 135 private final WeakReference weakThis = new WeakReference(this); 136 137 141 private final java.io.File target; 142 143 146 private final ArchiveController enclController; 147 148 151 private final String enclEntryName; 152 153 156 private ArchiveDriver driver; 157 158 private final ReentrantLock readLock; 159 private final ReentrantLock writeLock; 160 161 165 static { 166 Runtime.getRuntime().addShutdownHook(ShutdownHook.singleton); 167 } 168 169 173 186 protected ArchiveController( 187 final java.io.File target, 188 final ArchiveController enclController, 189 final String enclEntryName, 190 final ArchiveDriver driver) { 191 assert target != null; 192 assert target.isAbsolute(); 193 assert (enclController != null) == (enclEntryName != null); 194 assert driver != null; 195 196 this.target = target; 197 this.enclController = enclController; 198 this.enclEntryName = enclEntryName; 199 this.driver = driver; 200 201 ReadWriteLock rwl = new ReentrantReadWriteLock(); 202 this.readLock = rwl.readLock(); 203 this.writeLock = rwl.writeLock(); 204 205 setScheduled(false); 206 } 207 208 212 public final ReentrantLock readLock() { 213 return readLock; 214 } 215 216 public final ReentrantLock writeLock() { 217 return writeLock; 218 } 219 220 235 protected final void runWriteLocked(IORunnable runnable) 236 throws IOException { 237 final int lockCount = readLock().lockCount(); 244 for (int c = lockCount; c > 0; c--) 245 readLock().unlock(); 246 247 writeLock().lock(); 249 try { 250 try { 251 runnable.run(); 252 } finally { 253 for (int c = lockCount; c > 0; c--) 255 readLock().lock(); 256 } 257 } finally { 258 writeLock().unlock(); 259 } 260 } 261 262 277 static ArchiveController getInstance(final File file) { 278 assert file != null; 279 assert file.isArchive(); 280 281 java.io.File target = file.getDelegate(); 282 try { 283 target = target.getCanonicalFile(); 284 } catch (IOException failure) { 285 target = File.normalize(target.getAbsoluteFile()); 286 } 287 288 final ArchiveDriver driver = file.getArchiveDetector() 289 .getArchiveDriver(target.getPath()); 290 291 ArchiveController controller = null; 292 boolean reconfigure = false; 293 try { 294 synchronized (controllers) { 295 final Object value = controllers.get(target); 296 if (value instanceof Reference) { 297 controller = (ArchiveController) ((Reference) value).get(); 298 if (controller != null) { 301 reconfigure = controller.getDriver() != driver; 305 return controller; 306 } 307 } else if (value != null) { 308 return (ArchiveController) value; 322 } 323 324 final File enclArchive = file.getEnclArchive(); 325 final ArchiveController enclController; 326 final String enclEntryName; 327 if (enclArchive != null) { 328 enclController = enclArchive.getArchiveController(); 329 enclEntryName = file.getEnclEntryName(); 330 } else { 331 enclController = null; 332 enclEntryName = null; 333 } 334 335 controller = new UpdatingArchiveController( 338 target, enclController, enclEntryName, driver); 339 } 340 } finally { 341 if (reconfigure) { 342 controller.writeLock().lock(); 343 try { 344 controller.setDriver(driver); 345 } finally { 346 controller.writeLock().unlock(); 347 } 348 } 349 } 350 351 return controller; 352 } 353 354 358 public final java.io.File getTarget() { 359 return target; 360 } 361 362 public final String getPath() { 363 return target.getPath(); 364 } 365 366 370 public final ArchiveController getEnclController() { 371 return enclController; 372 } 373 374 378 public final String getEnclEntryName() { 379 return enclEntryName; 380 } 381 382 public final String enclEntryName(final String entryName) { 383 return EMPTY != entryName 384 ? enclEntryName + ENTRY_SEPARATOR + entryName 385 : enclEntryName; 386 } 387 388 396 public final ArchiveDriver getDriver() { 397 return driver; 398 } 399 400 408 protected final void setDriver(ArchiveDriver driver) { 409 this.driver = driver; 415 } 416 417 static final ArchiveStatistics getLiveStatistics() { 418 return liveStats; 419 } 420 421 426 protected final boolean usesNativeTargetFile() { 427 430 return enclController == null 433 || enclController.getTarget().isDirectory(); 434 } 435 436 440 protected abstract boolean isTouched(); 441 442 462 protected final void setScheduled(final boolean scheduled) { 463 assert weakThis.get() != null || !scheduled; 465 synchronized (controllers) { 466 controllers.put(getTarget(), scheduled ? (Object ) this : weakThis); 467 } 468 } 469 470 481 public final InputStream getInputStream(final String entryName) 482 throws FileNotFoundException { 483 assert entryName != null; 484 assert EMPTY != entryName; 486 try { 487 return getInputStream0(entryName); 488 } catch (FalsePositiveEntryException failure) { return enclController.getInputStream(enclEntryName(entryName)); 490 } catch (FileNotFoundException failure) { 491 throw failure; 492 } catch (ArchiveBusyException failure) { 493 throw new FileBusyException(failure); 494 } catch (IOException failure) { 495 final FileNotFoundException fnfe 496 = new FileNotFoundException(failure.toString()); 497 fnfe.initCause(failure); 498 throw fnfe; 499 } 500 } 501 502 private InputStream getInputStream0(final String entryName) 503 throws IOException { 504 readLock().lock(); 506 try { 507 if (hasNewData(entryName)) { 508 runWriteLocked(new IORunnable() { 509 public void run() throws IOException { 510 if (hasNewData(entryName)) update(); 512 } 513 }); 514 } 515 516 final ArchiveEntry entry = getFileSystem(false).get(entryName); 517 if (entry == null) 518 throw new ArchiveEntryNotFoundException(entryName, "no such entry"); 519 520 return getInputStream(entry, null); 521 } finally { 522 readLock().unlock(); 523 } 524 } 525 526 private final InputStream getInputStream( 527 final ArchiveEntry entry, 528 final ArchiveEntry dstEntry) 529 throws FileNotFoundException { 530 assert readLock().isLocked() || writeLock().isLocked(); 531 assert !hasNewData(entry.getName()); 532 533 try { 534 return getInputStreamImpl(entry, dstEntry); 535 } catch (FileNotFoundException failure) { 536 throw failure; 537 } catch (IOException failure) { 538 final FileNotFoundException fnfe 539 = new FileNotFoundException(failure.toString()); 540 fnfe.initCause(failure); 541 throw fnfe; 542 } 543 } 544 545 553 protected abstract InputStream getInputStreamImpl( 554 ArchiveEntry entry, 555 ArchiveEntry dstEntry) 556 throws IOException; 557 558 569 public final OutputStream getOutputStream( 570 final String entryName, 571 final boolean append) 572 throws FileNotFoundException { 573 assert entryName != null; 574 assert EMPTY != entryName; 576 try { 577 InputStream in = null; 580 if (append && isFile(entryName)) 581 in = getInputStream0(entryName); 582 final OutputStream out = getOutputStream0(entryName); 583 if (in != null) { 584 try { 585 File.cat(in, out); 586 } finally { 587 in.close(); 588 } 589 } 590 return out; 591 } catch (FalsePositiveEntryException failure) { return enclController.getOutputStream(enclEntryName(entryName), 593 append); 594 } catch (FileNotFoundException failure) { 595 throw failure; 596 } catch (ArchiveBusyException failure) { 597 throw new FileBusyException(failure); 598 } catch (IOException failure) { 599 final FileNotFoundException exc 600 = new FileNotFoundException(failure.toString()); 601 exc.initCause(failure); 602 throw exc; 603 } 604 } 605 606 private OutputStream getOutputStream0(final String entryName) 607 throws IOException { 608 assert entryName != null; 609 610 class OutputStreamCreator implements IORunnable { 611 OutputStream out; 612 613 public void run() throws IOException { 614 if (hasNewData(entryName)) 615 update(); 616 617 final boolean lenient = File.isLenient(); 618 final ArchiveFileSystem fileSystem = getFileSystem(lenient); 619 620 final Delta delta = fileSystem.beginCreateAndLink( 622 entryName, lenient); 623 624 out = getOutputStream(delta.getEntry(), null); 626 627 delta.commit(); 630 } 631 } 633 final OutputStreamCreator creator = new OutputStreamCreator(); 634 runWriteLocked(creator); 635 return creator.out; 636 } 637 638 private final OutputStream getOutputStream( 639 final ArchiveEntry entry, 640 final ArchiveEntry srcEntry) 641 throws FileNotFoundException { 642 try { 643 return getOutputStreamImpl(entry, srcEntry); 644 } catch (FileNotFoundException failure) { 645 throw failure; 646 } catch (IOException failure) { 647 final FileNotFoundException fnfe 648 = new FileNotFoundException(failure.toString()); 649 fnfe.initCause(failure); 650 throw fnfe; 651 } 652 } 653 654 662 protected abstract OutputStream getOutputStreamImpl( 663 ArchiveEntry entry, 664 ArchiveEntry srcEntry) 665 throws IOException; 666 667 675 protected abstract boolean hasNewData(String entryName); 676 677 763 static void updateAll( 764 final String prefix, 765 final boolean waitInputStreams, 766 final boolean closeInputStreams, 767 final boolean waitOutputStreams, 768 final boolean closeOutputStreams, 769 final boolean umount, 770 final boolean resetCounters) 771 throws ArchiveException { 772 if (prefix == null) 773 throw new NullPointerException (); 774 if (!closeInputStreams && closeOutputStreams) 775 throw new IllegalArgumentException (); 776 777 int controllersTotal = 0, controllersTouched = 0; 778 logger.log(Level.FINE, "updateAll.entering", new Object [] { 780 prefix, 781 Boolean.valueOf(waitInputStreams), 782 Boolean.valueOf(closeInputStreams), 783 Boolean.valueOf(waitOutputStreams), 784 Boolean.valueOf(closeOutputStreams), 785 Boolean.valueOf(umount), 786 Boolean.valueOf(resetCounters), 787 }); 788 try { 789 if (resetCounters) { 790 CountingReadOnlyFile.resetOnReuse(); 795 CountingOutputStream.resetOnReuse(); 796 } 797 try { 798 if (umount) { 808 System.runFinalization(); 809 817 } 818 819 ArchiveException exceptionChain = null; 821 822 final Enumeration e = new ControllerEnumeration( 829 prefix, REVERSE_CONTROLLERS); 830 while (e.hasMoreElements()) { 831 final ArchiveController controller 832 = (ArchiveController) e.nextElement(); 833 controller.writeLock().lock(); 834 try { 835 if (controller.isTouched()) 836 controllersTouched++; 837 try { 838 controller.update(exceptionChain, 842 waitInputStreams, closeInputStreams, 843 waitOutputStreams, closeOutputStreams, 844 umount, true); 845 } catch (ArchiveException exception) { 846 exceptionChain = exception; 852 } 853 } finally { 854 controller.writeLock().unlock(); 855 } 856 controllersTotal++; 857 } 858 859 if (exceptionChain != null) 862 throw (ArchiveException) exceptionChain.sortPriority(); 863 } finally { 864 CountingReadOnlyFile.setResetOnReuse(); 865 CountingOutputStream.setResetOnReuse(); 866 } 867 } catch (ArchiveException failure) { 868 logger.log(Level.FINE, "updateAll.throwing", failure); throw failure; 870 } 871 logger.log(Level.FINE, "updateAll.exiting", new Object [] { 873 new Integer (controllersTotal), 874 new Integer (controllersTouched) 875 }); 876 } 877 878 892 public final void update() throws ArchiveException { 893 assert writeLock().isLocked(); 894 update(null, false, false, false, false, false, false); 895 } 896 897 934 protected abstract void update( 935 ArchiveException exceptionChain, 936 final boolean waitInputStreams, 937 final boolean closeInputStreams, 938 final boolean waitOutputStreams, 939 final boolean closeOutputStreams, 940 final boolean umount, 941 final boolean reassemble) 942 throws ArchiveException; 943 944 protected abstract int waitAllInputStreamsByOtherThreads(long timeout); 946 947 protected abstract int waitAllOutputStreamsByOtherThreads(long timeout); 949 950 968 protected abstract ArchiveFileSystem getFileSystem(boolean autoCreate) 969 throws IOException; 970 971 981 protected abstract void reset() 982 throws IOException; 983 984 public String toString() { 985 return getClass().getName() + "@" + System.identityHashCode(this) + "(" + getPath() + ")"; 986 } 987 988 993 public final boolean exists(final String entryName) 994 throws FalsePositiveNativeException { 995 try { 996 return exists0(entryName); 997 } catch (FalsePositiveEntryException failure) { 998 return enclController.exists(enclEntryName(entryName)); 999 } catch (FalsePositiveNativeException failure) { 1000 throw failure; 1001 } catch (IOException failure) { 1002 return false; 1003 } 1004 } 1005 1006 private final boolean exists0(final String entryName) 1007 throws IOException { 1008 assert entryName != EMPTY; 1009 1010 readLock().lock(); 1011 try { 1012 1014 final ArchiveFileSystem fileSystem = getFileSystem(false); 1015 return fileSystem.exists(entryName); 1016 } finally { 1017 readLock().unlock(); 1018 } 1019 } 1020 1021 public final boolean isFile(final String entryName) 1022 throws FalsePositiveNativeException { 1023 try { 1024 return isFile0(entryName); 1025 } catch (ArchiveController.FalsePositiveEntryException failure) { 1026 if (EMPTY == entryName 1027 && failure.getCause() instanceof FileNotFoundException) { 1028 return false; 1040 } else { 1041 return enclController.isFile(enclEntryName(entryName)); 1042 } 1043 } catch (ArchiveController.FalsePositiveNativeException failure) { 1044 throw failure; 1045 } catch (IOException failure) { 1046 return false; 1047 } 1048 } 1049 1050 private final boolean isFile0(final String entryName) 1051 throws IOException { 1052 readLock().lock(); 1053 try { 1054 final ArchiveFileSystem fileSystem = getFileSystem(false); 1055 return fileSystem.isFile(entryName); 1056 } finally { 1057 readLock().unlock(); 1058 } 1059 } 1060 1061 public final boolean isDirectory(final String entryName) 1062 throws FalsePositiveNativeException { 1063 try { 1064 return isDirectory0(entryName); 1065 } catch (ArchiveController.FalsePositiveEntryException failure) { 1066 return enclController.isDirectory(enclEntryName(entryName)); 1067 } catch (ArchiveController.FalsePositiveNativeException failure) { 1068 throw failure; 1069 } catch (IOException failure) { 1070 return false; 1074 } 1075 } 1076 1077 private final boolean isDirectory0(final String entryName) 1078 throws IOException { 1079 readLock().lock(); 1080 try { 1081 final ArchiveFileSystem fileSystem = getFileSystem(false); 1082 return fileSystem.isDirectory(entryName); 1083 } finally { 1084 readLock().unlock(); 1085 } 1086 } 1087 1088 public final Icon getOpenIcon(final String entryName) 1089 throws FalsePositiveNativeException { 1090 try { 1091 return getOpenIcon0(entryName); 1092 } catch (ArchiveController.FalsePositiveEntryException failure) { 1093 return enclController.getOpenIcon(enclEntryName(entryName)); 1094 } catch (ArchiveController.FalsePositiveNativeException failure) { 1095 throw failure; 1096 } catch (IOException failure) { 1097 return null; 1098 } 1099 } 1100 1101 private final Icon getOpenIcon0(final String entryName) 1102 throws IOException { 1103 readLock().lock(); 1104 try { 1105 final ArchiveFileSystem fileSystem = getFileSystem(false); if (EMPTY != entryName) return fileSystem.getOpenIcon(entryName); 1108 else 1109 return getDriver().getOpenIcon(this); 1110 } finally { 1111 readLock().unlock(); 1112 } 1113 } 1114 1115 public final Icon getClosedIcon(final String entryName) 1116 throws FalsePositiveNativeException { 1117 try { 1118 return getClosedIcon0(entryName); 1119 } catch (ArchiveController.FalsePositiveEntryException failure) { 1120 return enclController.getOpenIcon(enclEntryName(entryName)); 1121 } catch (ArchiveController.FalsePositiveNativeException failure) { 1122 throw failure; 1123 } catch (IOException failure) { 1124 return null; 1125 } 1126 } 1127 1128 private final Icon getClosedIcon0(final String entryName) 1129 throws IOException { 1130 readLock().lock(); 1131 try { 1132 final ArchiveFileSystem fileSystem = getFileSystem(false); if (EMPTY != entryName) return fileSystem.getClosedIcon(entryName); 1135 else 1136 return getDriver().getClosedIcon(this); 1137 } finally { 1138 readLock().unlock(); 1139 } 1140 } 1141 1142 public final boolean canRead(final String entryName) 1143 throws FalsePositiveNativeException { 1144 try { 1145 return canRead0(entryName); 1146 } catch (ArchiveController.FalsePositiveEntryException failure) { 1147 return enclController.canRead(enclEntryName(entryName)); 1148 } catch (ArchiveController.FalsePositiveNativeException failure) { 1149 throw failure; 1150 } catch (IOException failure) { 1151 return false; 1152 } 1153 } 1154 1155 private final boolean canRead0(final String entryName) 1156 throws IOException { 1157 readLock().lock(); 1158 try { 1159 final ArchiveFileSystem fileSystem = getFileSystem(false); 1160 return fileSystem.exists(entryName); 1161 } finally { 1162 readLock().unlock(); 1163 } 1164 } 1165 1166 public final boolean canWrite(final String entryName) 1167 throws FalsePositiveNativeException { 1168 try { 1169 return canWrite0(entryName); 1170 } catch (ArchiveController.FalsePositiveEntryException failure) { 1171 return enclController.canWrite(enclEntryName(entryName)); 1172 } catch (ArchiveController.FalsePositiveNativeException failure) { 1173 throw failure; 1174 } catch (IOException failure) { 1175 return false; 1176 } 1177 } 1178 1179 private final boolean canWrite0(final String entryName) 1180 throws IOException { 1181 readLock().lock(); 1182 try { 1183 final ArchiveFileSystem fileSystem = getFileSystem(false); 1184 return fileSystem.canWrite(entryName); 1185 } finally { 1186 readLock().unlock(); 1187 } 1188 } 1189 1190 public final long length(final String entryName) 1191 throws FalsePositiveNativeException { 1192 try { 1193 return length0(entryName); 1194 } catch (ArchiveController.FalsePositiveEntryException failure) { 1195 return enclController.length(enclEntryName(entryName)); 1196 } catch (ArchiveController.FalsePositiveNativeException failure) { 1197 throw failure; 1198 } catch (IOException failure) { 1199 return 0; 1200 } 1201 } 1202 1203 private final long length0(final String entryName) 1204 throws IOException { 1205 readLock().lock(); 1206 try { 1207 final ArchiveFileSystem fileSystem = getFileSystem(false); 1208 return fileSystem.length(entryName); 1209 } finally { 1210 readLock().unlock(); 1211 } 1212 } 1213 1214 public final long lastModified(final String entryName) 1215 throws FalsePositiveNativeException { 1216 try { 1217 return lastModified0(entryName); 1218 } catch (ArchiveController.FalsePositiveEntryException failure) { 1219 return enclController.lastModified(enclEntryName(entryName)); 1220 } catch (ArchiveController.FalsePositiveNativeException failure) { 1221 throw failure; 1222 } catch (IOException failure) { 1223 return 0; 1224 } 1225 } 1226 1227 private final long lastModified0(final String entryName) 1228 throws IOException { 1229 readLock().lock(); 1230 try { 1231 final ArchiveFileSystem fileSystem = getFileSystem(false); 1232 return fileSystem.lastModified(entryName); 1233 } finally { 1234 readLock().unlock(); 1235 } 1236 } 1237 1238 public final String [] list(final String entryName) 1239 throws FalsePositiveNativeException { 1240 try { 1241 return list0(entryName); 1242 } catch (ArchiveController.FalsePositiveEntryException failure) { 1243 return enclController.list(enclEntryName(entryName)); 1244 } catch (ArchiveController.FalsePositiveNativeException failure) { 1245 throw failure; 1246 } catch (IOException failure) { 1247 return null; 1248 } 1249 } 1250 1251 private final String [] list0(final String entryName) 1252 throws IOException { 1253 readLock().lock(); 1254 try { 1255 final ArchiveFileSystem fileSystem = getFileSystem(false); 1256 return fileSystem.list(entryName); 1257 } finally { 1258 readLock().unlock(); 1259 } 1260 } 1261 1262 public final String [] list( 1263 final String entryName, 1264 final FilenameFilter filenameFilter, 1265 final File dir) 1266 throws FalsePositiveNativeException { 1267 try { 1268 return list0(entryName, filenameFilter, dir); 1269 } catch (ArchiveController.FalsePositiveEntryException failure) { 1270 return enclController.list(enclEntryName(entryName), 1271 filenameFilter, dir); 1272 } catch (ArchiveController.FalsePositiveNativeException failure) { 1273 throw failure; 1274 } catch (IOException failure) { 1275 return null; 1276 } 1277 } 1278 1279 private final String [] list0( 1280 final String entryName, 1281 final FilenameFilter filenameFilter, 1282 final File dir) 1283 throws IOException { 1284 readLock().lock(); 1285 try { 1286 final ArchiveFileSystem fileSystem = getFileSystem(false); 1287 return fileSystem.list(entryName, filenameFilter, dir); 1288 } finally { 1289 readLock().unlock(); 1290 } 1291 } 1292 1293 public final File[] listFiles( 1294 final String entryName, 1295 final FilenameFilter filenameFilter, 1296 final File dir, 1297 final FileFactory factory) 1298 throws FalsePositiveNativeException { 1299 try { 1300 return listFiles0(entryName, filenameFilter, dir, factory); 1301 } catch (ArchiveController.FalsePositiveEntryException failure) { 1302 return enclController.listFiles(enclEntryName(entryName), 1303 filenameFilter, dir, factory); 1304 } catch (ArchiveController.FalsePositiveNativeException failure) { 1305 throw failure; 1306 } catch (IOException failure) { 1307 return null; 1308 } 1309 } 1310 1311 private final File[] listFiles0( 1312 final String entryName, 1313 final FilenameFilter filenameFilter, 1314 final File dir, 1315 final FileFactory factory) 1316 throws IOException { 1317 readLock().lock(); 1318 try { 1319 final ArchiveFileSystem fileSystem = getFileSystem(false); 1320 return fileSystem.listFiles(entryName, filenameFilter, dir, factory); 1321 } finally { 1322 readLock().unlock(); 1323 } 1324 } 1325 1326 public final File[] listFiles( 1327 final String entryName, 1328 final FileFilter fileFilter, 1329 final File dir, 1330 final FileFactory factory) 1331 throws FalsePositiveNativeException { 1332 try { 1333 return listFiles0(entryName, fileFilter, dir, factory); 1334 } catch (ArchiveController.FalsePositiveEntryException failure) { 1335 return enclController.listFiles(enclEntryName(entryName), 1336 fileFilter, dir, factory); 1337 } catch (ArchiveController.FalsePositiveNativeException failure) { 1338 throw failure; 1339 } catch (IOException failure) { 1340 return null; 1341 } 1342 } 1343 1344 private final File[] listFiles0( 1345 final String entryName, 1346 final FileFilter fileFilter, 1347 final File dir, 1348 final FileFactory factory) 1349 throws IOException { 1350 readLock().lock(); 1351 try { 1352 final ArchiveFileSystem fileSystem = getFileSystem(false); 1353 return fileSystem.listFiles(entryName, fileFilter, dir, factory); 1354 } finally { 1355 readLock().unlock(); 1356 } 1357 } 1358 1359 1364 public final boolean setReadOnly(final String entryName) 1365 throws FalsePositiveNativeException { 1366 try { 1367 return setReadOnly0(entryName); 1368 } catch (ArchiveController.FalsePositiveEntryException failure) { 1369 return enclController.setReadOnly(enclEntryName(entryName)); 1370 } catch (ArchiveController.FalsePositiveNativeException failure) { 1371 throw failure; 1372 } catch (IOException failure) { 1373 return false; 1374 } 1375 } 1376 1377 private final boolean setReadOnly0(final String entryName) 1378 throws IOException { 1379 writeLock().lock(); 1380 try { 1381 final ArchiveFileSystem fileSystem = getFileSystem(false); 1382 return fileSystem.setReadOnly(entryName); 1383 } finally { 1384 writeLock().unlock(); 1385 } 1386 } 1387 1388 public final boolean setLastModified( 1389 final String entryName, 1390 final long time) 1391 throws FalsePositiveNativeException { 1392 try { 1393 return setLastModified0(entryName, time); 1394 } catch (ArchiveController.FalsePositiveEntryException failure) { 1395 return enclController.setLastModified(enclEntryName(entryName), 1396 time); 1397 } catch (ArchiveController.FalsePositiveNativeException failure) { 1398 throw failure; 1399 } catch (IOException failure) { 1400 return false; 1401 } 1402 } 1403 1404 private final boolean setLastModified0( 1405 final String entryName, 1406 final long time) 1407 throws IOException { 1408 writeLock().lock(); 1409 try { 1410 ArchiveFileSystem fileSystem = getFileSystem(false); 1411 if (fileSystem.isReadOnly()) return false; 1413 if (hasNewData(entryName)) { 1414 update(); 1415 fileSystem = getFileSystem(false); } 1417 return fileSystem.setLastModified(entryName, time); 1418 } finally { 1419 writeLock().unlock(); 1420 } 1421 } 1422 1423 public final boolean createNewFile( 1424 final String entryName, 1425 final boolean autoCreate) 1426 throws IOException { 1427 try { 1428 return createNewFile0(entryName, autoCreate); 1429 } catch (ArchiveController.FalsePositiveEntryException failure) { 1430 return enclController.createNewFile(enclEntryName(entryName), 1431 autoCreate); 1432 } 1433 } 1434 1435 private final boolean createNewFile0( 1436 final String entryName, 1437 final boolean autoCreate) 1438 throws IOException { 1439 assert entryName != EMPTY; 1440 1441 writeLock().lock(); 1442 try { 1443 final ArchiveFileSystem fileSystem = getFileSystem(autoCreate); 1444 if (fileSystem.exists(entryName)) 1445 return false; 1446 1447 getOutputStream0(entryName).close(); 1450 1451 return true; 1452 } finally { 1453 writeLock().unlock(); 1454 } 1455 } 1456 1457 public final boolean mkdir( 1458 final String entryName, 1459 final boolean autoCreate) 1460 throws FalsePositiveNativeException { 1461 try { 1462 mkdir0(entryName, autoCreate); 1463 return true; 1464 } catch (ArchiveController.FalsePositiveEntryException failure) { 1465 return enclController.mkdir(enclEntryName(entryName), autoCreate); 1466 } catch (ArchiveController.FalsePositiveNativeException failure) { 1467 throw failure; 1468 } catch (IOException failure) { 1469 return false; 1470 } 1471 } 1472 1473 private final void mkdir0(final String entryName, final boolean autoCreate) 1474 throws IOException { 1475 writeLock().lock(); 1476 try { 1477 if (EMPTY != entryName) { final ArchiveFileSystem fileSystem = getFileSystem(autoCreate); 1480 fileSystem.mkdir(entryName, autoCreate); 1481 } else { if (usesNativeTargetFile()) { 1485 if (target.exists()) 1486 throw new IOException("target file exists already!"); 1487 } else { 1488 if (enclController.exists(enclEntryName)) 1489 throw new IOException("target file exists already!"); 1490 } 1491 getFileSystem(true); 1493 } 1494 } finally { 1495 writeLock().unlock(); 1496 } 1497 } 1498 1499 public final boolean delete(final String entryName) 1500 throws FalsePositiveNativeException { 1501 try { 1502 delete0(entryName); 1503 return true; 1504 } catch (ArchiveController.FalsePositiveDirectoryEntryException failure) { 1505 return enclController.delete(enclEntryName(entryName)); 1506 } catch (ArchiveController.FalsePositiveFileEntryException failure) { 1507 if (EMPTY == entryName 1508 && failure.getCause() instanceof FileNotFoundException) { 1509 return false; 1519 } else { 1520 return enclController.delete(enclEntryName(entryName)); 1521 } 1522 } catch (ArchiveController.FalsePositiveNativeException failure) { 1523 throw failure; 1524 } catch (IOException failure) { 1525 return false; 1526 } 1527 } 1528 1529 private final void delete0(final String entryName) 1530 throws IOException { 1531 writeLock().lock(); 1532 try { 1533 if (hasNewData(entryName)) 1543 update(); 1544 1545 if (EMPTY != entryName) { final ArchiveFileSystem fileSystem = getFileSystem(false); 1547 fileSystem.delete(entryName); 1548 } else { final ArchiveFileSystem fileSystem; 1551 try { 1552 fileSystem = getFileSystem(false); 1553 } catch (FalsePositiveException falsePositive) { 1554 try { 1557 reset(); 1558 } catch (IOException cannotHappen) { 1559 throw new AssertionError (cannotHappen); 1560 } 1561 throw falsePositive; 1562 } 1563 1564 final String [] members = fileSystem.list(entryName); 1569 if (members != null && members.length != 0) 1570 throw new IOException("archive file system not empty!"); 1571 final int outputStreams = waitAllOutputStreamsByOtherThreads(50); 1572 assert outputStreams <= 0 1574 : "Entries for open output streams should not be deletable!"; 1575 final int inputStreams = waitAllInputStreamsByOtherThreads(50); 1577 if (inputStreams > 0 || outputStreams > 0) 1578 throw new IOException("archive file has open streams!"); 1579 reset(); 1580 PromptingKeyManager.resetKeyProvider(getPath()); 1586 if (usesNativeTargetFile()) { 1589 if (!target.delete()) 1592 throw new IOException("couldn't delete archive file!"); 1593 } else { 1594 enclController.delete0(enclEntryName(entryName)); 1597 } 1598 } 1599 } finally { 1600 writeLock().unlock(); 1601 } 1602 } 1603 1604 1608 1628 protected static void cp( 1629 final java.io.File src, 1630 final java.io.File dst, 1631 final boolean preserve) 1632 throws IOException { 1633 assert src != null; 1634 assert dst != null; 1635 1636 try { 1637 if (src instanceof File) { 1638 final File srcFile = (File) src; 1639 srcFile.ensureNotVirtualDirectory("cannot read"); 1640 final String srcEntryName = srcFile.getEnclEntryName(); 1641 if (srcEntryName != null) { 1642 cp( srcFile, 1643 srcFile.getEnclArchive().getArchiveController(), 1644 srcEntryName, dst, 1645 preserve); 1646 return; 1647 } 1648 } 1649 1650 final InputStream in = new java.io.FileInputStream (src); 1652 try { 1653 cp(src, in, dst, preserve); 1654 } finally { 1655 try { 1656 in.close(); 1657 } catch (IOException failure) { 1658 throw new InputIOException(failure); 1659 } 1660 } 1661 } catch (ArchiveBusyException failure) { 1662 throw new FileBusyException(failure); 1663 } 1664 } 1665 1666 1680 protected static void cp( 1681 final java.io.File src, 1682 final InputStream in, 1683 final java.io.File dst, 1684 final boolean preserve) 1685 throws IOException { 1686 if (dst instanceof File) { 1687 final File dstFile = (File) dst; 1688 dstFile.ensureNotVirtualDirectory("cannot write"); 1689 final String dstEntryName = dstFile.getEnclEntryName(); 1690 if (dstEntryName != null) { 1691 cp( src, in, dstFile, 1692 dstFile.getEnclArchive().getArchiveController(), 1693 dstEntryName, preserve); 1694 return; 1695 } 1696 } 1697 1698 final OutputStream out = new java.io.FileOutputStream (dst); 1700 try { 1701 File.cat(in, out); 1702 } finally { 1703 out.close(); 1704 } 1705 if (preserve && !dst.setLastModified(src.lastModified())) 1706 throw new IOException(dst.getPath() + 1707 " (couldn't preserve last modification time)"); 1708 } 1709 1710 1728 protected static void cp( 1729 final File srcFile, 1730 final ArchiveController srcController, 1731 final String srcEntryName, 1732 final java.io.File dst, 1733 final boolean preserve) 1734 throws IOException { 1735 1740 try { 1741 try { 1742 if (dst instanceof File) { 1743 final File dstFile = (File) dst; 1744 dstFile.ensureNotVirtualDirectory("cannot write"); 1745 final String dstEntryName = dstFile.getEnclEntryName(); 1746 if (dstEntryName != null) { 1747 cp( srcFile, srcController, srcEntryName, 1748 dstFile, 1749 dstFile.getEnclArchive().getArchiveController(), 1750 dstEntryName, 1751 preserve); 1752 return; 1753 } 1754 } 1755 1756 final InputStream in; 1757 final long time; 1758 srcController.readLock().lock(); 1759 try { 1760 in = srcController.getInputStream(srcEntryName); time = srcController.lastModified0(srcEntryName); 1762 } finally { 1763 srcController.readLock().unlock(); 1764 } 1765 1766 final OutputStream out; 1768 try { 1769 out = new java.io.FileOutputStream (dst); 1770 } catch (IOException failure) { 1771 try { 1772 in.close(); 1773 } catch (IOException inFailure) { 1774 throw new InputIOException(inFailure); 1775 } 1776 throw failure; 1777 } 1778 1779 File.cp(in, out); 1780 if (preserve && !dst.setLastModified(time)) 1781 throw new IOException(dst.getPath() + 1782 " (couldn't preserve last modification time)"); 1783 } catch (FalsePositiveDirectoryEntryException failure) { 1784 assert srcController == failure.getSource(); 1785 cp( srcFile, srcController.getEnclController(), 1787 srcController.enclEntryName(srcEntryName), 1788 dst, preserve); 1789 } 1790 } catch (FalsePositiveNativeException failure) { 1791 assert srcController == failure.getSource(); 1792 cp(srcFile.getDelegate(), dst, preserve); 1794 } 1795 } 1796 1797 1811 protected static void cp( 1812 final File srcFile, 1813 final ArchiveController srcController, 1814 final String srcEntryName, 1815 final File dstFile, 1816 final ArchiveController dstController, 1817 final String dstEntryName, 1818 final boolean preserve) 1819 throws IOException { 1820 1827 try { 1828 class IOStreamCreator implements IORunnable { 1829 InputStream in; 1830 OutputStream out; 1831 1832 public void run() throws IOException { 1833 class SrcControllerUpdater implements IORunnable { 1838 public void run() throws IOException { 1839 if (srcController.hasNewData(srcEntryName)) 1840 srcController.update(); 1841 srcController.readLock().lock(); } 1843 } 1845 final ArchiveEntry srcEntry, dstEntry; 1846 final Delta delta; 1847 srcController.runWriteLocked(new SrcControllerUpdater()); 1848 try { 1849 if (dstController.hasNewData(dstEntryName)) 1850 dstController.update(); 1851 1852 final ArchiveFileSystem srcFileSystem 1854 = srcController.getFileSystem(false); 1855 srcEntry = srcFileSystem.get(srcEntryName); 1856 1857 final boolean isLenient = File.isLenient(); 1859 final ArchiveFileSystem dstFileSystem 1860 = dstController.getFileSystem(isLenient); 1861 delta = dstFileSystem.beginCreateAndLink( 1862 dstEntryName, isLenient, preserve ? srcEntry : null); 1863 dstEntry = delta.getEntry(); 1864 1865 in = srcController.getInputStream(srcEntry, dstEntry); 1867 } finally { 1868 srcController.readLock().unlock(); 1869 } 1870 1871 try { 1872 out = dstController.getOutputStream(dstEntry, srcEntry); 1874 1875 try { 1876 delta.commit(); 1878 } catch (IOException failure) { 1879 out.close(); 1880 throw failure; 1881 } 1882 } catch (IOException failure) { 1883 try { 1884 in.close(); 1885 } catch (IOException inFailure) { 1886 throw new InputIOException(inFailure); 1887 } 1888 throw failure; 1889 } 1890 } 1891 } 1893 final IOStreamCreator streams = new IOStreamCreator(); 1894 synchronized (copyLock) { 1895 dstController.runWriteLocked(streams); 1896 } 1897 1898 File.cp(streams.in, streams.out); 1900 } catch (FalsePositiveEntryException failure) { 1901 if (dstController != failure.getSource()) 1905 throw failure; 1907 cp( srcFile, srcController, srcEntryName, 1909 dstFile, dstController.getEnclController(), 1910 dstController.enclEntryName(dstEntryName), 1911 preserve); 1912 } catch (FalsePositiveNativeException failure) { 1913 if (dstController != failure.getSource()) 1917 throw failure; 1919 cp( srcFile, srcController, srcEntryName, 1921 dstFile.getDelegate(), 1922 preserve); 1923 } 1924 } 1925 1926 1945 protected static void cp( 1946 final java.io.File src, 1947 final InputStream in, 1948 final File dstFile, 1949 final ArchiveController dstController, 1950 final String dstEntryName, 1951 final boolean preserve) 1952 throws IOException { 1953 1958 try { 1959 class OStreamCreator implements IORunnable { 1960 OutputStream out; 1961 1962 public void run() throws IOException { 1963 if (dstController.hasNewData(dstEntryName)) 1968 dstController.update(); 1969 1970 final boolean isLenient = File.isLenient(); 1971 1972 final ArchiveEntry srcEntry 1974 = new File2ArchiveEntryAdapter(src); 1975 1976 final ArchiveFileSystem dstFileSystem 1978 = dstController.getFileSystem(isLenient); 1979 final Delta transaction = dstFileSystem.beginCreateAndLink( 1980 dstEntryName, isLenient, preserve ? srcEntry : null); 1981 final ArchiveEntry dstEntry = transaction.getEntry(); 1982 1983 out = dstController.getOutputStream(dstEntry, srcEntry); 1985 1986 transaction.commit(); 1988 } 1989 } 1990 1991 final OStreamCreator stream = new OStreamCreator(); 1994 dstController.runWriteLocked(stream); 1995 final OutputStream out = stream.out; 1996 1997 try { 1999 File.cat(in, out); 2000 } finally { 2001 out.close(); 2002 } 2003 } catch (FalsePositiveEntryException failure) { 2004 assert dstController == failure.getSource(); 2005 cp( src, in, 2007 dstFile, dstController.getEnclController(), 2008 dstController.enclEntryName(dstEntryName), 2009 preserve); 2010 } catch (FalsePositiveNativeException failure) { 2011 assert dstController == failure.getSource(); 2012 cp(src, in, dstFile.getDelegate(), preserve); 2014 } 2015 } 2016 2017 2021 2024 protected interface IORunnable { 2025 void run() throws IOException; 2026 } 2027 2028 2033 private static final class CopyLock { } 2034 2035 static final class ShutdownHook extends Thread { 2036 private static final ShutdownHook singleton = new ShutdownHook(); 2037 2038 2042 static final Set deleteOnExit 2043 = Collections.synchronizedSet(new LinkedHashSet()); 2044 2045 private ShutdownHook() { 2046 super("TrueZIP ArchiveController Shutdown Hook"); 2047 setPriority(Thread.MAX_PRIORITY); 2048 } 2049 2050 2066 public void run() { 2067 synchronized (PromptingKeyManager.class) { 2068 try { PromptingKeyManager.setPrompting(false); 2070 logger.setLevel(Level.OFF); 2071 2072 for (Iterator i = deleteOnExit.iterator(); i.hasNext(); ) { 2073 final File file = (File) i.next(); 2074 if (file.exists() && !file.delete()) { 2075 System.err.println( 2076 file.getPath() + ": failed to deleteOnExit()!"); 2077 } 2078 } 2079 } finally { 2080 try { 2081 updateAll("", false, true, false, true, true, false); 2082 } catch (ArchiveException oops) { 2083 oops.printStackTrace(); 2084 } 2085 } 2086 } 2087 } 2088 } 2090 private static final class LiveStatistics implements ArchiveStatistics { 2091 public long getUpdateTotalByteCountRead() { 2092 return CountingReadOnlyFile.getTotal(); 2093 } 2094 2095 public long getUpdateTotalByteCountWritten() { 2096 return CountingOutputStream.getTotal(); 2097 } 2098 2099 public int getArchivesTotal() { 2100 return controllers.size(); 2106 } 2107 2108 public int getArchivesTouched() { 2109 int result = 0; 2110 2111 final Enumeration e = new ControllerEnumeration(); 2112 while (e.hasMoreElements()) { 2113 final ArchiveController c = (ArchiveController) e.nextElement(); 2114 c.readLock().lock(); 2115 try { 2116 if (c.isTouched()) 2117 result++; 2118 } finally { 2119 c.readLock().unlock(); 2120 } 2121 } 2122 2123 return result; 2124 } 2125 2126 public int getTopLevelArchivesTotal() { 2127 int result = 0; 2128 2129 final Enumeration e = new ControllerEnumeration(); 2130 while (e.hasMoreElements()) { 2131 final ArchiveController c = (ArchiveController) e.nextElement(); 2132 if (c.getEnclController() == null) 2133 result++; 2134 } 2135 2136 return result; 2137 } 2138 2139 public int getTopLevelArchivesTouched() { 2140 int result = 0; 2141 2142 final Enumeration e = new ControllerEnumeration(); 2143 while (e.hasMoreElements()) { 2144 final ArchiveController c = (ArchiveController) e.nextElement(); 2145 c.readLock().lock(); 2146 try { 2147 if (c.getEnclController() == null && c.isTouched()) 2148 result++; 2149 } finally { 2150 c.readLock().unlock(); 2151 } 2152 } 2153 2154 return result; 2155 } 2156 } 2158 private static final class ControllerEnumeration implements Enumeration { 2159 private final Iterator it; 2160 2161 public ControllerEnumeration() { 2162 this("", null); 2163 } 2164 2165 public ControllerEnumeration(final String prefix, final Comparator c) { 2166 assert prefix != null; 2167 2168 final Set snapshot; 2169 synchronized (controllers) { 2170 if (c != null) { 2171 snapshot = new TreeSet(c); 2172 } else { 2173 snapshot = new HashSet((int) (controllers.size() / 0.75f)); 2174 } 2175 2176 final Iterator it = controllers.values().iterator(); 2177 while (it.hasNext()) { 2178 Object value = it.next(); 2179 if (value instanceof Reference) { 2180 value = ((Reference) value).get(); if (value != null) { 2182 assert value instanceof ArchiveController; 2183 if (((ArchiveController) value).getPath().startsWith(prefix)) 2184 snapshot.add(value); 2185 } 2192 } else { 2193 assert value != null; 2194 assert value instanceof ArchiveController; 2195 if (((ArchiveController) value).getPath().startsWith(prefix)) 2196 snapshot.add(value); 2197 } 2198 } 2199 } 2200 2201 it = snapshot.iterator(); 2202 } 2203 2204 public boolean hasMoreElements() { 2205 return it.hasNext(); 2206 } 2207 2208 public Object nextElement() { 2209 return it.next(); 2210 } 2211 } 2213 static final class CountingReadOnlyFile extends SimpleReadOnlyFile { 2215 private static volatile long _total; 2216 2218 CountingReadOnlyFile(java.io.File file) 2219 throws FileNotFoundException { 2220 super(file); 2221 resetOnReuse(); 2222 } 2223 2224 public static long getTotal() { 2225 return _total; 2226 } 2227 2228 private static void setResetOnReuse() { 2229 } 2231 2232 private static void resetOnReuse() { 2233 2235 _total = 0; 2236 } 2238 2239 public int read() throws IOException { 2240 int ret = super.read(); 2241 if (ret != -1) 2242 _total++; 2243 return ret; 2244 } 2245 2246 public int read(byte[] b) throws IOException { 2247 int ret = super.read(b); 2248 if (ret != -1) 2249 _total += ret; 2250 return ret; 2251 } 2252 2253 public int read(byte[] b, int off, int len) throws IOException { 2254 int ret = super.read(b, off, len); 2255 if (ret != -1) 2256 _total += ret; 2257 return ret; 2258 } 2259 2260 public int skipBytes(int n) throws IOException { 2261 int ret = super.skipBytes(n); 2262 _total += ret; 2263 return ret; 2264 } 2265 } 2267 2315 2316 static final class CountingOutputStream extends FilterOutputStream { 2317 private static volatile long _total; 2318 private static volatile boolean _resetOnReuse; 2319 2320 CountingOutputStream(OutputStream out) { 2321 super(out); 2322 resetOnReuse(); 2323 } 2324 2325 public static long getTotal() { 2326 return _total; 2327 } 2328 2329 private static void setResetOnReuse() { 2330 _resetOnReuse = true; 2331 } 2332 2333 private static void resetOnReuse() { 2334 if (_resetOnReuse) { 2335 _resetOnReuse = false; 2336 _total = 0; 2337 } 2338 } 2339 2340 public void write(final int b) throws IOException { 2341 out.write(b); 2342 _total++; 2343 } 2344 2345 public void write(byte b[], int off, int len) throws IOException { 2346 out.write(b, off, len); 2347 _total += len; 2348 } 2349 } 2351 private static final class File2ArchiveEntryAdapter implements ArchiveEntry { 2352 private final java.io.File file; 2353 2354 private File2ArchiveEntryAdapter(final java.io.File file) { 2355 assert file != null; 2356 this.file = file; 2357 } 2358 2359 public String getName() { 2360 assert false : "Drivers should never call this method!"; 2361 if (file.isDirectory()) 2364 return file.getName() + "/"; 2365 else 2366 return file.getName(); 2367 } 2368 2369 public boolean isDirectory() { 2370 return file.isDirectory(); 2371 } 2372 2373 public long getSize() { 2374 return file.length(); 2375 } 2376 2377 public long getTime() { 2378 return file.lastModified(); 2379 } 2380 2381 public void setTime(long time) { 2382 assert false : "Drivers should never call this method!"; 2383 file.setLastModified(time); } 2385 2386 public Icon getOpenIcon() { 2387 return null; 2388 } 2389 2390 public Icon getClosedIcon() { 2391 return null; 2392 } 2393 2394 public ArchiveEntryMetaData getMetaData() { 2395 throw new AssertionError ("Drivers should never call this method!"); 2396 } 2397 2398 public void setMetaData(ArchiveEntryMetaData metaData) { 2399 throw new AssertionError ("Drivers should never call this method!"); 2400 } 2401 } 2403 2408 2413 abstract class FalsePositiveException extends FileNotFoundException { 2414 private boolean cacheable = true; 2415 2416 FalsePositiveException(String path) { 2417 super(path); 2418 } 2419 2420 2430 public Throwable initCause(Throwable cause) { 2431 final boolean trans = cause instanceof TransientIOException; 2436 super.initCause(trans ? cause.getCause() : cause); 2437 cacheable = !trans; 2438 return this; 2439 } 2440 2441 2446 public ArchiveController getSource() { 2447 return ArchiveController.this; 2448 } 2449 2450 2454 public boolean isCacheable() { 2455 return cacheable; 2456 } 2457 } 2459 2467 class FalsePositiveNativeException extends FalsePositiveException { 2468 2469 2478 FalsePositiveNativeException(IOException cause) { 2479 super(getPath()); 2480 initCause(cause); 2481 } 2482 } 2484 2489 abstract class FalsePositiveEntryException extends FalsePositiveException { 2490 2491 private final ArchiveController target; 2492 2493 2499 FalsePositiveEntryException( 2500 ArchiveController target, 2501 String entryName) { 2502 this(target, entryName, null); 2503 } 2504 2505 2518 FalsePositiveEntryException( 2519 ArchiveController target, 2520 String entryName, 2521 IOException cause) { 2522 super(target.getPath() + File.separator + entryName); 2523 assert target != null; 2524 assert entryName != null; 2525 initCause(cause); 2526 this.target = target; 2527 this.entryName = entryName; 2528 } 2529 2530 2535 public ArchiveController getTarget() { 2536 return target; 2537 } 2538 2539 private final String entryName; 2540 2541 2545 public String getEntryName() { 2546 return entryName; 2547 } 2548 } 2550 2558 class FalsePositiveFileEntryException extends FalsePositiveEntryException { 2559 2560 2569 FalsePositiveFileEntryException( 2570 ArchiveController enclController, 2571 String enclEntryName, 2572 IOException cause) { 2573 super(enclController, enclEntryName, cause); 2574 assert cause != null; 2575 } 2576 } 2578 2583 class FalsePositiveDirectoryEntryException extends FalsePositiveEntryException { 2584 2585 2588 FalsePositiveDirectoryEntryException( 2589 ArchiveController enclController, 2590 String enclEntryName) { 2591 super(enclController, enclEntryName); 2592 } 2593 } 2595 2601 class ArchiveNotFoundException extends FileNotFoundException { 2602 ArchiveNotFoundException() { 2603 } 2604 2605 ArchiveNotFoundException(String msg) { 2606 super(msg); 2607 } 2608 2609 public String getMessage() { 2610 String msg = super.getMessage(); 2611 if (msg != null) 2612 return getPath() + " (" + msg + ")"; 2613 else 2614 return getPath(); 2615 } 2616 } 2618 2623 class ArchiveEntryNotFoundException extends FileNotFoundException { 2624 private final String name; 2625 2626 ArchiveEntryNotFoundException(String name, String msg) { 2627 super(msg); 2628 assert name != null; 2629 assert msg != null; 2630 this.name = name; 2631 } 2632 2633 public String getMessage() { 2634 String msg = super.getMessage(); 2635 if (msg != null) 2636 return getPath() + ENTRY_SEPARATOR + name + " (" + msg + ")"; 2637 else 2638 return getPath() + ENTRY_SEPARATOR + name; 2639 } 2640 } } 2642
| Popular Tags
|