|
38 | 38 | import java.time.Instant; |
39 | 39 | import java.time.ZoneOffset; |
40 | 40 | import java.time.format.DateTimeFormatter; |
| 41 | +import java.time.format.DateTimeParseException; |
41 | 42 | import java.time.temporal.ChronoUnit; |
42 | 43 | import java.util.Arrays; |
43 | 44 | import java.util.Base64; |
@@ -152,13 +153,7 @@ private InputStream getUrlInputStreamThroughCache(final URL url) throws IOExcept |
152 | 153 |
|
153 | 154 | if (cachedFile.exists() && cachedMetadataFile.exists()) { |
154 | 155 | try { |
155 | | - final HashMap<String,String> cachedMetadata = readMetadataFile(cachedMetadataFile); |
156 | | - |
157 | | - if (cachedMetadata != null) { |
158 | | - checkCache(cachedMetadataFile, cachedMetadata, url); |
159 | | - } else { |
160 | | - cacheMiss(url); |
161 | | - } |
| 156 | + checkCache(url); |
162 | 157 | } catch (IOException ioe) { |
163 | 158 | // We know we have a locally cached file here, so if we happen to get an exception we can safely ignore |
164 | 159 | // it and fall back on the (possibly stale) cached content file. This makes the code more robust in the |
@@ -234,31 +229,57 @@ private void writeContentFile(final InputStream urlInputStream, final File cache |
234 | 229 | } |
235 | 230 | } |
236 | 231 |
|
237 | | - private void checkCache(final File cachedMetadataFile, final HashMap<String,String> cachedMetadata, final URL url) throws IOException { |
238 | | - final Instant lastChecked = Instant.parse(cachedMetadata.get("lastChecked")); |
239 | | - final long difference = Math.abs(ChronoUnit.SECONDS.between(Instant.now(), lastChecked)); |
| 232 | + /** |
| 233 | + * Attempts to parse s as if it were an ISO8601 formatted String. |
| 234 | + * @param s The string to attempt to parse. |
| 235 | + * @return The Instant for that ISO8601 value if parsing succeeded, or null if it didn't. |
| 236 | + */ |
| 237 | + private final Instant parseISO8601String(final String s) { |
| 238 | + Instant result = null; |
| 239 | + if (s != null) { |
| 240 | + try { |
| 241 | + result = Instant.parse(s); |
| 242 | + } catch (final DateTimeParseException dtpe) { |
| 243 | + result = null; |
| 244 | + } |
| 245 | + } |
| 246 | + return result; |
| 247 | + } |
240 | 248 |
|
241 | | - if (difference > cacheCheckIntervalSecs) { |
242 | | - // It's been a while since we checked the cached download of this URL for staleness, so make an ETag request |
243 | | - logger.debug("Outside cache check interval; checking for updates to " + String.valueOf(url)); |
244 | | - final String eTag = cachedMetadata.get("eTag"); |
245 | | - final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); |
246 | | - connection.setReadTimeout(READ_TIMEOUT); |
247 | | - connection.setRequestProperty("If-None-Match", eTag); |
248 | | - final int status = connection.getResponseCode(); |
249 | | - if (status != HttpURLConnection.HTTP_NOT_MODIFIED) { |
250 | | - // The content of the URL has changed, which we handle the same as a cache miss (i.e. we re-download |
251 | | - // the content, and write a new metadata file from scratch) |
252 | | - cacheMiss(url, connection); |
| 249 | + private void checkCache(final URL url) throws IOException { |
| 250 | + final String cacheKey = base64Encode(url); |
| 251 | + final File cachedMetadataFile = new File(cacheDir, cacheKey + ".metadata.json"); |
| 252 | + final HashMap<String,String> cachedMetadata = readMetadataFile(cachedMetadataFile); |
| 253 | + |
| 254 | + if (cachedMetadata != null) { |
| 255 | + final Instant lastChecked = parseISO8601String(cachedMetadata.get("lastChecked")); |
| 256 | + final long difference = lastChecked != null ? Math.abs(ChronoUnit.SECONDS.between(Instant.now(), lastChecked)) : Long.MAX_VALUE; |
| 257 | + |
| 258 | + if (difference > cacheCheckIntervalSecs) { |
| 259 | + // It's been a while since we checked the cached download of this URL for staleness, so make an ETag request |
| 260 | + logger.debug("Cache check interval exceeded; checking for updates to " + String.valueOf(url)); |
| 261 | + final String eTag = cachedMetadata.get("eTag"); |
| 262 | + final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); |
| 263 | + connection.setReadTimeout(READ_TIMEOUT); |
| 264 | + connection.setRequestProperty("If-None-Match", eTag); |
| 265 | + final int status = connection.getResponseCode(); |
| 266 | + if (status != HttpURLConnection.HTTP_NOT_MODIFIED) { |
| 267 | + // The content of the URL has changed, which we handle the same as a cache miss (i.e. we re-download |
| 268 | + // the content, and write a new metadata file from scratch) |
| 269 | + cacheMiss(url, connection); |
| 270 | + } else { |
| 271 | + // The content hasn't changed, so just update the lastChecked metadata but otherwise do nothing |
| 272 | + logger.debug("Cache hit for " + String.valueOf(url)); |
| 273 | + cachedMetadata.put("lastChecked", iso8601.format(Instant.now())); |
| 274 | + writeMetadataFile(cachedMetadataFile, cachedMetadata); |
| 275 | + } |
253 | 276 | } else { |
254 | | - // The content hasn't changed, so just update the lastChecked metadata but otherwise do nothing |
255 | | - logger.debug("Cache hit for " + String.valueOf(url)); |
256 | | - cachedMetadata.put("lastChecked", iso8601.format(Instant.now())); |
257 | | - writeMetadataFile(cachedMetadataFile, cachedMetadata); |
| 277 | + // We checked recently, so don't need to do anything - the cached content will be used |
| 278 | + logger.debug("Within cache check interval; skipping check of updates to " + String.valueOf(url)); |
258 | 279 | } |
259 | 280 | } else { |
260 | | - // We checked recently, so don't need to do anything - the cached content will be used |
261 | | - logger.debug("Within cache check interval; skipping check of updates to " + String.valueOf(url)); |
| 281 | + // Metadata doesn't exist - treat it as a cache miss |
| 282 | + cacheMiss(url); |
262 | 283 | } |
263 | 284 | } |
264 | 285 |
|
|
0 commit comments