Ticket #19549: 19549.patch
File 19549.patch, 9.7 KB (added by , 5 years ago) |
---|
-
src/org/openstreetmap/josm/io/CachedFile.java
16 16 import java.nio.file.Files; 17 17 import java.nio.file.InvalidPathException; 18 18 import java.nio.file.StandardCopyOption; 19 import java.nio.file.attribute.BasicFileAttributes; 19 20 import java.security.MessageDigest; 20 21 import java.security.NoSuchAlgorithmException; 21 22 import java.util.ArrayList; 22 import java.util.Arrays; 23 import java.util.Collection; 24 import java.util.Collections; 25 import java.util.EnumSet; 23 26 import java.util.Enumeration; 24 27 import java.util.List; 25 28 import java.util.Map; 26 29 import java.util.Optional; 30 import java.util.Set; 27 31 import java.util.concurrent.ConcurrentHashMap; 28 32 import java.util.concurrent.TimeUnit; 29 33 import java.util.zip.ZipEntry; … … 71 75 IfModifiedSince 72 76 } 73 77 78 /** 79 * Options that may be useful to more accurately control cached files. 80 * 81 * @author Taylor Smock 82 * @since xxx 83 */ 84 public enum Options { 85 /** 86 * Fail quickly (~1 second) 87 */ 88 FastFail, 89 /** 90 * Delete the files on graceful exit 91 */ 92 DeleteOnExit, 93 } 94 74 95 protected String name; 75 96 protected long maxAge; 76 97 protected String destDir; … … 77 98 protected String httpAccept; 78 99 protected CachingStrategy cachingStrategy; 79 100 80 private boolean fastFail;81 101 private HttpClient activeConnection; 82 102 protected File cacheFile; 83 103 protected boolean initialized; 84 104 protected String parameter; 85 105 106 protected final Set<Options> options = EnumSet.noneOf(Options.class); 107 86 108 public static final long DEFAULT_MAXTIME = -1L; 87 109 public static final long DAYS = TimeUnit.DAYS.toSeconds(1); // factor to get caching time in days 88 110 … … 177 199 * @param fastFail whether opening HTTP connections should fail fast 178 200 */ 179 201 public void setFastFail(boolean fastFail) { 180 this.fastFail = fastFail; 202 if (fastFail) { 203 options.add(Options.FastFail); 204 } else { 205 options.remove(Options.FastFail); 206 } 181 207 } 182 208 183 209 /** … … 189 215 this.parameter = parameter; 190 216 } 191 217 218 /** 219 * Get the name of the file. If a URL parameter is set, 220 * some characters will be deleted. 221 * 222 * @return The name of the file 223 */ 192 224 public String getName() { 193 225 if (parameter != null) 194 226 return name.replaceAll("%<(.*)>", ""); … … 205 237 return maxAge; 206 238 } 207 239 240 /** 241 * The destination directory of the downloaded file 242 * @return The destination directory. If {@code null}, a default directory 243 * will be selected on download. 244 */ 208 245 public String getDestDir() { 209 246 return destDir; 210 247 } … … 425 462 } 426 463 427 464 private File checkLocal(URL url) throws IOException { 428 String prefKey = getPrefKey(url, destDir);429 465 String urlStr = url.toExternalForm(); 430 466 if (parameter != null) 431 467 urlStr = urlStr.replaceAll("%<(.*)>", ""); … … 432 468 long age = 0L; 433 469 long maxAgeMillis = TimeUnit.SECONDS.toMillis(maxAge); 434 470 Long ifModifiedSince = null; 435 File localFile = null;436 List<String> localPathEntry = new ArrayList<>(Config.getPref().getList(prefKey));437 471 boolean offline = NetworkManager.isOffline(urlStr); 438 if (localPathEntry.size() == 2) { 439 localFile = new File(localPathEntry.get(1)); 440 if (!localFile.exists()) { 441 localFile = null; 442 } else { 443 if (maxAge == DEFAULT_MAXTIME 444 || maxAge <= 0 // arbitrary value <= 0 is deprecated 445 ) { 446 maxAgeMillis = TimeUnit.SECONDS.toMillis(Config.getPref().getLong("mirror.maxtime", TimeUnit.DAYS.toSeconds(7))); 447 } 448 age = System.currentTimeMillis() - Long.parseLong(localPathEntry.get(0)); 449 if (offline || age < maxAgeMillis) { 450 return localFile; 451 } else if (NetworkManager.isOffline(OnlineResource.CACHE_UPDATES)) { 452 Logging.warn(OfflineAccessException.forResource(tr("Cache update for {0}", urlStr)).getMessage()); 453 return localFile; 454 } 455 if (cachingStrategy == CachingStrategy.IfModifiedSince) { 456 ifModifiedSince = Long.valueOf(localPathEntry.get(0)); 457 } 472 473 /** Start check for local file */ 474 boolean nullDestDir = destDir == null; 475 if (nullDestDir) { 476 destDir = Config.getDirs().getCacheDirectory(true).getPath(); 477 } 478 String localPath = getFilePath(urlStr, destDir); 479 File localFile = new File(destDir, localPath); 480 if (!localFile.exists()) { 481 localFile = null; 482 } 483 if (nullDestDir) { 484 destDir = null; 485 } 486 /** End check for local file */ 487 488 if (localFile != null && !localFile.exists()) { 489 localFile = null; 490 } else if (localFile != null) { 491 if (maxAge == DEFAULT_MAXTIME 492 || maxAge <= 0 // arbitrary value <= 0 is deprecated 493 ) { 494 maxAgeMillis = TimeUnit.SECONDS.toMillis(Config.getPref().getLong("mirror.maxtime", TimeUnit.DAYS.toSeconds(7))); 458 495 } 496 497 BasicFileAttributes attributes = Files.readAttributes(localFile.toPath(), BasicFileAttributes.class); 498 Long fileAge = attributes.lastModifiedTime().toMillis(); 499 age = System.currentTimeMillis() - fileAge; 500 if (offline || age < maxAgeMillis) { 501 return localFile; 502 } else if (NetworkManager.isOffline(OnlineResource.CACHE_UPDATES)) { 503 Logging.warn(OfflineAccessException.forResource(tr("Cache update for {0}", urlStr)).getMessage()); 504 return localFile; 505 } 506 if (cachingStrategy == CachingStrategy.IfModifiedSince) { 507 ifModifiedSince = fileAge; 508 } 459 509 } 460 510 if (destDir == null) { 461 511 destDir = Config.getDirs().getCacheDirectory(true).getPath(); … … 483 533 url = new URL(uc); 484 534 } 485 535 486 String a = urlStr.replaceAll("[^A-Za-z0-9_.-]", "_"); 487 String localPath = "mirror_" + a; 488 localPath = truncatePath(destDir, localPath); 536 localPath = getFilePath(urlStr, destDir); 489 537 destDirFile = new File(destDir, localPath + ".tmp"); 490 538 try { 491 539 activeConnection = HttpClient.create(url) … … 492 540 .setAccept(httpAccept) 493 541 .setIfModifiedSince(ifModifiedSince == null ? 0L : ifModifiedSince) 494 542 .setHeaders(httpHeaders); 495 if ( fastFail) {543 if (options.contains(Options.FastFail)) { 496 544 activeConnection.setReadTimeout(1000); 497 545 } 498 546 final HttpClient.Response con = activeConnection.connect(); 499 547 if (ifModifiedSince != null && con.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) { 500 548 Logging.debug("304 Not Modified ({0})", urlStr); 501 Config.getPref().putList(prefKey,502 Arrays.asList(Long.toString(System.currentTimeMillis()), localPathEntry.get(1)));503 549 return localFile; 504 550 } else if (con.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) { 505 551 throw new IOException(tr("The requested URL {0} was not found", urlStr)); … … 510 556 activeConnection = null; 511 557 localFile = new File(destDir, localPath); 512 558 if (PlatformManager.getPlatform().rename(destDirFile, localFile)) { 513 Config.getPref().putList(prefKey, 514 Arrays.asList(Long.toString(System.currentTimeMillis()), localFile.toString())); 559 if (options.contains(Options.DeleteOnExit)) { 560 localFile.deleteOnExit(); 561 } 515 562 } else { 516 563 Logging.warn(tr("Failed to rename file {0} to {1}.", 517 564 destDirFile.getPath(), localFile.getPath())); … … 528 575 return localFile; 529 576 } 530 577 578 private static String getFilePath(String urlStr, String destDir) { 579 String a = urlStr.replaceAll("[^A-Za-z0-9_.-]", "_"); 580 String localPath = "mirror_" + a; 581 return truncatePath(destDir, localPath); 582 } 583 531 584 private static String truncatePath(String directory, String fileName) { 532 585 if (directory.length() + fileName.length() > 255) { 533 586 // Windows doesn't support paths longer than 260, leave 5 chars as safe buffer, 4 will be used by ".tmp" … … 586 639 Utils.deleteFile(f); 587 640 } 588 641 } 642 643 /** 644 * Add an option to be used prior to getting the file 645 * 646 * @param option The option to add 647 * @since xxx 648 */ 649 public void addOption(Options option) { 650 options.add(option); 651 } 652 653 /** 654 * Remove an option to be used prior to getting the file 655 * 656 * @param option The option to remove 657 * @since xxx 658 */ 659 public void removeOption(Options option) { 660 options.remove(option); 661 } 662 663 /** 664 * CachedFile uses options when getting the actual file. 665 * These should generally be set prior to any action which may get a file. 666 * 667 * @return The options used by this CachedFile in an unmodifiable collection 668 * @since xxx 669 */ 670 public Collection<Options> getOptions() { 671 return Collections.unmodifiableSet(options); 672 } 589 673 }