diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/DateGenerator.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/DateGenerator.java index f5e54e4da960..1c6e8f310d4c 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/DateGenerator.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/DateGenerator.java @@ -13,6 +13,7 @@ package org.eclipse.jetty.http; +import java.time.Instant; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.TimeZone; @@ -58,6 +59,17 @@ public static String formatDate(long date) return __dateGenerator.get().doFormatDate(date); } + /** + * Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'" + * + * @param instant the date/time instant + * @return the formatted date + */ + public static String formatDate(Instant instant) + { + return formatDate(instant.toEpochMilli()); + } + /** * Format "EEE, dd-MMM-yyyy HH:mm:ss 'GMT'" for cookies * diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/ResourceHttpContent.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/ResourceHttpContent.java index 0c9b74fe92eb..f3d1ea901ad3 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/ResourceHttpContent.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/ResourceHttpContent.java @@ -15,6 +15,7 @@ import java.nio.ByteBuffer; import java.nio.file.Path; +import java.time.Instant; import java.util.HashMap; import java.util.Map; @@ -98,15 +99,15 @@ public Type getMimeType() @Override public HttpField getLastModified() { - long lm = _resource.lastModified(); - return lm >= 0 ? new HttpField(HttpHeader.LAST_MODIFIED, DateGenerator.formatDate(lm)) : null; + Instant lm = _resource.lastModified(); + return new HttpField(HttpHeader.LAST_MODIFIED, DateGenerator.formatDate(lm)); } @Override public String getLastModifiedValue() { - long lm = _resource.lastModified(); - return lm >= 0 ? DateGenerator.formatDate(lm) : null; + Instant lm = _resource.lastModified(); + return DateGenerator.formatDate(lm); } @Override diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/CachedContentFactory.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/CachedContentFactory.java index 7cec8988acf8..70386686e4d9 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/CachedContentFactory.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/CachedContentFactory.java @@ -16,7 +16,9 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.file.Files; +import java.time.Instant; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.SortedSet; @@ -227,7 +229,7 @@ private HttpContent load(String pathInContext, Resource resource) throws IOExcep { compressedContent = null; Resource compressedResource = _factory.newResource(compressedPathInContext); - if (compressedResource.exists() && compressedResource.lastModified() >= resource.lastModified() && + if (compressedResource.exists() && compressedResource.lastModified().isAfter(resource.lastModified()) && compressedResource.length() < resource.length()) { compressedContent = new CachedHttpContent(compressedPathInContext, compressedResource, null); @@ -268,12 +270,12 @@ private HttpContent load(String pathInContext, Resource resource) throws IOExcep { String compressedPathInContext = pathInContext + format.getExtension(); CachedHttpContent compressedContent = _cache.get(compressedPathInContext); - if (compressedContent != null && compressedContent.isValid() && Files.getLastModifiedTime(compressedContent.getResource().getPath()).toMillis() >= resource.lastModified()) + if (compressedContent != null && compressedContent.isValid() && Files.getLastModifiedTime(compressedContent.getResource().getPath()).toInstant().isAfter(resource.lastModified())) compressedContents.put(format, compressedContent); // Is there a precompressed resource? Resource compressedResource = _factory.newResource(compressedPathInContext); - if (compressedResource.exists() && compressedResource.lastModified() >= resource.lastModified() && + if (compressedResource.exists() && compressedResource.lastModified().isAfter(resource.lastModified()) && compressedResource.length() < resource.length()) compressedContents.put(format, new ResourceHttpContent(compressedResource, _mimeTypes.getMimeByExtension(compressedPathInContext))); @@ -291,19 +293,10 @@ private void shrinkCache() while (_cache.size() > 0 && (_cachedFiles.get() > _maxCachedFiles || _cachedSize.get() > _maxCacheSize)) { // Scan the entire cache and generate an ordered list by last accessed time. - SortedSet sorted = new TreeSet<>((c1, c2) -> - { - if (c1._lastAccessed < c2._lastAccessed) - return -1; - - if (c1._lastAccessed > c2._lastAccessed) - return 1; - - if (c1._contentLengthValue < c2._contentLengthValue) - return -1; - - return c1._key.compareTo(c2._key); - }); + SortedSet sorted = new TreeSet<>( + Comparator.comparing((CachedHttpContent c) -> c._lastAccessed) + .thenComparingLong(c -> c._contentLengthValue) + .thenComparing(c -> c._key)); sorted.addAll(_cache.values()); // Invalidate least recently used first @@ -381,13 +374,13 @@ public class CachedHttpContent implements HttpContent private final MimeTypes.Type _mimeType; private final HttpField _contentLength; private final HttpField _lastModified; - private final long _lastModifiedValue; + private final Instant _lastModifiedValue; private final HttpField _etag; private final Map _precompressed; private final AtomicReference _indirectBuffer = new AtomicReference<>(); private final AtomicReference _directBuffer = new AtomicReference<>(); private final AtomicReference _mappedBuffer = new AtomicReference<>(); - private volatile long _lastAccessed; + private volatile Instant _lastAccessed; CachedHttpContent(String pathInContext, Resource resource, Map precompressedResources) { @@ -400,8 +393,8 @@ public class CachedHttpContent implements HttpContent _mimeType = _contentType == null ? null : MimeTypes.CACHE.get(MimeTypes.getContentTypeWithoutCharset(contentType)); boolean exists = resource.exists(); - _lastModifiedValue = exists ? resource.lastModified() : -1L; - _lastModified = _lastModifiedValue == -1 ? null + _lastModifiedValue = exists ? resource.lastModified() : null; + _lastModified = _lastModifiedValue == null ? null : new PreEncodedHttpField(HttpHeader.LAST_MODIFIED, DateGenerator.formatDate(_lastModifiedValue)); _contentLengthValue = exists ? resource.length() : 0; @@ -410,7 +403,7 @@ public class CachedHttpContent implements HttpContent if (_cachedFiles.incrementAndGet() > _maxCachedFiles) shrinkCache(); - _lastAccessed = System.currentTimeMillis(); + _lastAccessed = Instant.now(); _etag = CachedContentFactory.this._etags ? new PreEncodedHttpField(HttpHeader.ETAG, resource.getWeakETag()) : null; @@ -460,7 +453,7 @@ boolean isValid() { if (_lastModifiedValue == _resource.lastModified() && _contentLengthValue == _resource.length()) { - _lastAccessed = System.currentTimeMillis(); + _lastAccessed = Instant.now(); return true; } diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceContentFactory.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceContentFactory.java index 4bdec9e368f9..4dfbadbf56a0 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceContentFactory.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceContentFactory.java @@ -91,7 +91,7 @@ private HttpContent load(String pathInContext, Resource resource) { String compressedPathInContext = pathInContext + format.getExtension(); Resource compressedResource = this._factory.newResource(compressedPathInContext); - if (compressedResource != null && compressedResource.exists() && compressedResource.lastModified() >= resource.lastModified() && + if (compressedResource != null && compressedResource.exists() && compressedResource.lastModified().isAfter(resource.lastModified()) && compressedResource.length() < resource.length()) compressedContents.put(format, new ResourceHttpContent(compressedResource, _mimeTypes.getMimeByExtension(compressedPathInContext))); diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceListing.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceListing.java index 4378a2ac7f44..676f62a876a8 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceListing.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceListing.java @@ -13,10 +13,12 @@ package org.eclipse.jetty.server; -import java.text.DateFormat; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; import java.util.ArrayList; import java.util.Comparator; -import java.util.Date; import java.util.List; import org.eclipse.jetty.util.Fields; @@ -198,7 +200,9 @@ public static String getAsHTML(Resource resource, String base, boolean parent, S buf.append("\n"); } - DateFormat dfmt = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM); + // TODO: Use Locale and/or ZoneId from Request? + DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.MEDIUM) + .withZone(ZoneId.systemDefault()); for (Resource item : listing) { @@ -223,9 +227,8 @@ public static String getAsHTML(Resource resource, String base, boolean parent, S // Last Modified buf.append(""); - long lastModified = item.lastModified(); - if (lastModified > 0) - buf.append(dfmt.format(new Date(item.lastModified()))); + Instant lastModified = item.lastModified(); + buf.append(formatter.format(lastModified)); buf.append(" "); // Size diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/MemoryResource.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/MemoryResource.java index b134fdf5ce65..f82fb8b5f09f 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/MemoryResource.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/MemoryResource.java @@ -22,6 +22,7 @@ import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.file.Path; +import java.time.Instant; import java.util.Objects; import org.eclipse.jetty.util.IO; @@ -32,7 +33,7 @@ public class MemoryResource extends Resource { private final URI _uri; - private final long _created = System.currentTimeMillis(); + private final Instant _created = Instant.now(); private final byte[] _bytes; MemoryResource(URL url) @@ -76,7 +77,7 @@ public String getName() } @Override - public long lastModified() + public Instant lastModified() { return _created; } diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java index c1d3eb8009f5..81d7e28d2cb5 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java @@ -29,6 +29,7 @@ import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.FileTime; +import java.time.Instant; import java.util.ArrayList; import java.util.Base64; import java.util.Collection; @@ -178,24 +179,31 @@ public boolean isDirectory() } /** - * Time resource was last modified. + * The time the resource was last modified. + * * Equivalent to {@link Files#getLastModifiedTime(Path, LinkOption...)} with the following parameter: - * {@link #getPath()} then returning {@link FileTime#toMillis()}. + * {@link #getPath()} then returning {@link FileTime#toInstant()}. * - * @return the last modified time as milliseconds since unix epoch or - * 0 if {@link Files#getLastModifiedTime(Path, LinkOption...)} throws {@link IOException}. + * @return the last modified time instant, or {@link Instant#EPOCH} if unable to obtain last modified. */ - public long lastModified() + public Instant lastModified() { + Path path = getPath(); + if (path == null) + return Instant.EPOCH; + + if (!Files.exists(path)) + return Instant.EPOCH; + try { - FileTime ft = Files.getLastModifiedTime(getPath(), FOLLOW_LINKS); - return ft.toMillis(); + FileTime ft = Files.getLastModifiedTime(path, FOLLOW_LINKS); + return ft.toInstant(); } catch (IOException e) { LOG.trace("IGNORED", e); - return 0; + return Instant.EPOCH; } } @@ -426,7 +434,7 @@ public String getWeakETag(String suffix) } Base64.Encoder encoder = Base64.getEncoder().withoutPadding(); - b.append(encoder.encodeToString(longToBytes(lastModified() ^ lhash))); + b.append(encoder.encodeToString(longToBytes(lastModified().toEpochMilli() ^ lhash))); b.append(encoder.encodeToString(longToBytes(length() ^ lhash))); b.append(suffix); b.append('"'); diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollators.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollators.java index f922a406a690..3a8a8c432e3d 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollators.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollators.java @@ -37,7 +37,7 @@ public int compare(Resource o1, Resource o2) Collections.reverseOrder(BY_NAME_ASCENDING); private static Comparator BY_LAST_MODIFIED_ASCENDING = - Comparator.comparingLong(Resource::lastModified); + Comparator.comparing(Resource::lastModified); private static Comparator BY_LAST_MODIFIED_DESCENDING = Collections.reverseOrder(BY_LAST_MODIFIED_ASCENDING); diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollection.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollection.java index 5b7e2527ec4b..be925cd0e2e9 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollection.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollection.java @@ -19,6 +19,7 @@ import java.net.URI; import java.nio.channels.ReadableByteChannel; import java.nio.file.Path; +import java.time.Instant; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -240,17 +241,18 @@ public boolean isDirectory() } @Override - public long lastModified() + public Instant lastModified() { + Instant instant = null; for (Resource r : _resources) { - long lm = r.lastModified(); - if (lm != -1) + Instant lm = r.lastModified(); + if (instant == null || lm.isAfter(instant)) { - return lm; + instant = lm; } } - return -1; + return instant; } @Override diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileSystemResourceTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileSystemResourceTest.java index 657ea198619a..8e8da2211b02 100644 --- a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileSystemResourceTest.java +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileSystemResourceTest.java @@ -30,6 +30,7 @@ import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; +import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -55,7 +56,6 @@ import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.startsWith; @@ -352,21 +352,21 @@ public void testLastModified() throws Exception Path file = workDir.getPathFile("foo"); Files.createFile(file); - long expected = Files.getLastModifiedTime(file).toMillis(); + Instant expected = Files.getLastModifiedTime(file).toInstant(); Resource base = ResourceFactory.root().newResource(dir); Resource res = base.resolve("foo"); - assertThat("foo.lastModified", res.lastModified() / 1000 * 1000, lessThanOrEqualTo(expected)); + assertThat("foo.lastModified", res.lastModified(), is(expected)); } @Test - public void testLastModifiedNotExists() throws Exception + public void testLastModifiedNotExists() { Path dir = workDir.getEmptyPathDir(); Resource base = ResourceFactory.root().newResource(dir); Resource res = base.resolve("foo"); - assertThat("foo.lastModified", res.lastModified(), is(0L)); + assertThat("foo.lastModified", res.lastModified(), is(Instant.EPOCH)); } @Test diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/JarResourceTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/JarResourceTest.java index a07ad13d0df1..4f4bee7f86a2 100644 --- a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/JarResourceTest.java +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/JarResourceTest.java @@ -213,7 +213,7 @@ public void testJarFileLastModified() long last = zf.getEntry("subdir/numbers").getTime(); Resource r = resourceFactory.newResource(uri); - assertEquals(last, r.lastModified()); + assertEquals(last, r.lastModified().toEpochMilli()); } } diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/Overlay.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/Overlay.java index f5af225968e2..6084f13a5289 100644 --- a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/Overlay.java +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/Overlay.java @@ -15,6 +15,9 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Instant; import org.eclipse.jetty.util.resource.Resource; @@ -78,12 +81,21 @@ public String toString() * @param dir the directory into which to unpack the overlay * @throws IOException */ - public void unpackTo(File dir) throws IOException + public void unpackTo(File dir) throws IOException // TODO: change to Path { if (dir == null) throw new IllegalStateException("No overly unpack directory"); - //only unpack if the overlay is newer - if (!dir.exists() || (getResource().lastModified() > dir.lastModified())) - getResource().copyTo(dir.toPath()); + Path pathDir = dir.toPath(); + // only unpack if the overlay is newer + if (!Files.exists(pathDir)) + { + getResource().copyTo(pathDir); + } + else + { + Instant dirLastModified = Files.getLastModifiedTime(pathDir).toInstant(); + if (getResource().lastModified().isAfter(dirLastModified)) + getResource().copyTo(pathDir); + } } } diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebInfConfiguration.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebInfConfiguration.java index 6fc08556f5f6..316ee00aaa84 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebInfConfiguration.java +++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebInfConfiguration.java @@ -381,7 +381,7 @@ public void unpack(WebAppContext context) throws IOException { // Only extract if the war file is newer, or a .extract_lock file is left behind meaning a possible partial extraction // Use the original War Resource to obtain lastModified to avoid filesystem locks on MS Windows. - if (originalWarResource.lastModified() > Files.getLastModifiedTime(extractedWebAppDir).toMillis() || extractionLock.exists()) + if (originalWarResource.lastModified().isAfter(Files.getLastModifiedTime(extractedWebAppDir).toInstant()) || extractionLock.exists()) { extractionLock.createNewFile(); IO.delete(extractedWebAppDir); diff --git a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/OrderingTest.java b/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/OrderingTest.java index 4f202e71e974..32a9e02a6d64 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/OrderingTest.java +++ b/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/OrderingTest.java @@ -102,12 +102,6 @@ public boolean isDirectory() return false; } - @Override - public long lastModified() - { - return 0; - } - @Override public long length() { diff --git a/jetty-ee9/jetty-ee9-maven-plugin/src/main/java/org/eclipse/jetty/ee9/maven/plugin/Overlay.java b/jetty-ee9/jetty-ee9-maven-plugin/src/main/java/org/eclipse/jetty/ee9/maven/plugin/Overlay.java index 23e746797520..7ee61c2c57d5 100644 --- a/jetty-ee9/jetty-ee9-maven-plugin/src/main/java/org/eclipse/jetty/ee9/maven/plugin/Overlay.java +++ b/jetty-ee9/jetty-ee9-maven-plugin/src/main/java/org/eclipse/jetty/ee9/maven/plugin/Overlay.java @@ -15,6 +15,9 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Instant; import org.eclipse.jetty.util.resource.Resource; @@ -78,12 +81,22 @@ public String toString() * @param dir the directory into which to unpack the overlay * @throws IOException */ - public void unpackTo(File dir) throws IOException + public void unpackTo(File dir) throws IOException // TODO: change to Path { if (dir == null) throw new IllegalStateException("No overly unpack directory"); - //only unpack if the overlay is newer - if (!dir.exists() || (getResource().lastModified() > dir.lastModified())) - getResource().copyTo(dir.toPath()); + + Path pathDir = dir.toPath(); + // only unpack if the overlay is newer + if (!Files.exists(pathDir)) + { + getResource().copyTo(pathDir); + } + else + { + Instant dirLastModified = Files.getLastModifiedTime(pathDir).toInstant(); + if (getResource().lastModified().isAfter(dirLastModified)) + getResource().copyTo(pathDir); + } } } diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/CachedContentFactory.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/CachedContentFactory.java index 12f3a8d26420..c11d486fd4bb 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/CachedContentFactory.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/CachedContentFactory.java @@ -16,7 +16,9 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; +import java.time.Instant; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.SortedSet; @@ -227,7 +229,7 @@ private HttpContent load(String pathInContext, Resource resource) throws IOExcep { compressedContent = null; Resource compressedResource = _factory.newResource(compressedPathInContext); - if (compressedResource.exists() && compressedResource.lastModified() >= resource.lastModified() && + if (compressedResource.exists() && compressedResource.lastModified().isAfter(resource.lastModified()) && compressedResource.length() < resource.length()) { compressedContent = new CachedHttpContent(compressedPathInContext, compressedResource, null); @@ -268,12 +270,12 @@ private HttpContent load(String pathInContext, Resource resource) throws IOExcep { String compressedPathInContext = pathInContext + format.getExtension(); CachedHttpContent compressedContent = _cache.get(compressedPathInContext); - if (compressedContent != null && compressedContent.isValid() && compressedContent.getResource().lastModified() >= resource.lastModified()) + if (compressedContent != null && compressedContent.isValid() && compressedContent.getResource().lastModified().isAfter(resource.lastModified())) compressedContents.put(format, compressedContent); // Is there a precompressed resource? Resource compressedResource = _factory.newResource(compressedPathInContext); - if (compressedResource.exists() && compressedResource.lastModified() >= resource.lastModified() && + if (compressedResource.exists() && compressedResource.lastModified().isAfter(resource.lastModified()) && compressedResource.length() < resource.length()) compressedContents.put(format, new ResourceHttpContent(compressedResource, _mimeTypes.getMimeByExtension(compressedPathInContext))); @@ -291,19 +293,10 @@ private void shrinkCache() while (_cache.size() > 0 && (_cachedFiles.get() > _maxCachedFiles || _cachedSize.get() > _maxCacheSize)) { // Scan the entire cache and generate an ordered list by last accessed time. - SortedSet sorted = new TreeSet<>((c1, c2) -> - { - if (c1._lastAccessed < c2._lastAccessed) - return -1; - - if (c1._lastAccessed > c2._lastAccessed) - return 1; - - if (c1._contentLengthValue < c2._contentLengthValue) - return -1; - - return c1._key.compareTo(c2._key); - }); + SortedSet sorted = new TreeSet<>( + Comparator.comparing((CachedHttpContent c) -> c._lastAccessed) + .thenComparingLong(c -> c._contentLengthValue) + .thenComparing(c -> c._key)); sorted.addAll(_cache.values()); // Invalidate least recently used first @@ -381,11 +374,11 @@ public class CachedHttpContent implements HttpContent private final MimeTypes.Type _mimeType; private final HttpField _contentLength; private final HttpField _lastModified; - private final long _lastModifiedValue; + private final Instant _lastModifiedValue; private final HttpField _etag; private final Map _precompressed; private final AtomicReference _buffer = new AtomicReference<>(); - private volatile long _lastAccessed; + private volatile Instant _lastAccessed; CachedHttpContent(String pathInContext, Resource resource, Map precompressedResources) { @@ -398,8 +391,8 @@ public class CachedHttpContent implements HttpContent _mimeType = _contentType == null ? null : MimeTypes.CACHE.get(MimeTypes.getContentTypeWithoutCharset(contentType)); boolean exists = resource.exists(); - _lastModifiedValue = exists ? resource.lastModified() : -1L; - _lastModified = _lastModifiedValue == -1 ? null + _lastModifiedValue = exists ? resource.lastModified() : null; + _lastModified = _lastModifiedValue == null ? null : new PreEncodedHttpField(HttpHeader.LAST_MODIFIED, DateGenerator.formatDate(_lastModifiedValue)); _contentLengthValue = exists ? resource.length() : 0; @@ -408,7 +401,7 @@ public class CachedHttpContent implements HttpContent if (_cachedFiles.incrementAndGet() > _maxCachedFiles) shrinkCache(); - _lastAccessed = System.currentTimeMillis(); + _lastAccessed = Instant.now(); _etag = CachedContentFactory.this._etags ? new PreEncodedHttpField(HttpHeader.ETAG, resource.getWeakETag()) : null; @@ -458,7 +451,7 @@ boolean isValid() { if (_lastModifiedValue == _resource.lastModified() && _contentLengthValue == _resource.length()) { - _lastAccessed = System.currentTimeMillis(); + _lastAccessed = Instant.now(); return true; } @@ -615,7 +608,7 @@ public class CachedPrecompressedHttpContent extends PrecompressedHttpContent public boolean isValid() { - return _precompressedContent.isValid() && _content.isValid() && _content.getResource().lastModified() <= _precompressedContent.getResource().lastModified(); + return _precompressedContent.isValid() && _content.isValid() && _content.getResource().lastModified().isBefore(_precompressedContent.getResource().lastModified()); } @Override diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceContentFactory.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceContentFactory.java index f8ce2a6bc082..571825debf27 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceContentFactory.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceContentFactory.java @@ -87,7 +87,7 @@ private HttpContent load(String pathInContext, Resource resource) { String compressedPathInContext = pathInContext + format.getExtension(); Resource compressedResource = _factory.newResource(compressedPathInContext); - if (compressedResource != null && compressedResource.exists() && compressedResource.lastModified() >= resource.lastModified() && + if (compressedResource != null && compressedResource.exists() && compressedResource.lastModified().isAfter(resource.lastModified()) && compressedResource.length() < resource.length()) compressedContents.put(format, new ResourceHttpContent(compressedResource, _mimeTypes.getMimeByExtension(compressedPathInContext))); diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceService.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceService.java index d36ee1340d2b..fdbb88a75d99 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceService.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceService.java @@ -597,7 +597,7 @@ protected boolean passConditionalHeaders(HttpServletRequest request, HttpServlet } long ifmsl = request.getDateHeader(HttpHeader.IF_MODIFIED_SINCE.asString()); - if (ifmsl != -1 && content.getResource().lastModified() / 1000 <= ifmsl / 1000) + if (ifmsl != -1 && content.getResource().lastModified().toEpochMilli() <= ifmsl) { sendStatus(response, HttpServletResponse.SC_NOT_MODIFIED, content::getETagValue); return false; @@ -605,7 +605,7 @@ protected boolean passConditionalHeaders(HttpServletRequest request, HttpServlet } // Parse the if[un]modified dates and compare to resource - if (ifums != -1 && content.getResource().lastModified() / 1000 > ifums / 1000) + if (ifums != -1 && content.getResource().lastModified().toEpochMilli() > ifums) { response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); return false; diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebInfConfiguration.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebInfConfiguration.java index 9d43b41603f0..679373e04fb3 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebInfConfiguration.java +++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebInfConfiguration.java @@ -379,7 +379,7 @@ public void unpack(WebAppContext context) throws IOException { // Only extract if the war file is newer, or a .extract_lock file is left behind meaning a possible partial extraction // Use the original War Resource to obtain lastModified to avoid filesystem locks on MS Windows. - if (originalWarResource.lastModified() > Files.getLastModifiedTime(extractedWebAppDir).toMillis() || extractionLock.exists()) + if (originalWarResource.lastModified().isAfter(Files.getLastModifiedTime(extractedWebAppDir).toInstant()) || extractionLock.exists()) { extractionLock.createNewFile(); IO.delete(extractedWebAppDir); diff --git a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/OrderingTest.java b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/OrderingTest.java index ef7c9296778c..4dbd47785335 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/OrderingTest.java +++ b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/OrderingTest.java @@ -102,12 +102,6 @@ public boolean isDirectory() return false; } - @Override - public long lastModified() - { - return 0; - } - @Override public long length() {