Skip to content

Commit

Permalink
Merge pull request #8550 from eclipse/fix/jetty-12-resource-1-lastmod…
Browse files Browse the repository at this point in the history
…ified-to-instant

Issue #8474 - Jetty 12 - Change signature of `Resource.lastModified()`
  • Loading branch information
joakime authored Sep 7, 2022
2 parents 8c7199c + 227c5a8 commit ec2bad1
Show file tree
Hide file tree
Showing 20 changed files with 130 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)));
Expand All @@ -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<CachedHttpContent> 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<CachedHttpContent> 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
Expand Down Expand Up @@ -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<CompressedContentFormat, CachedPrecompressedHttpContent> _precompressed;
private final AtomicReference<ByteBuffer> _indirectBuffer = new AtomicReference<>();
private final AtomicReference<ByteBuffer> _directBuffer = new AtomicReference<>();
private final AtomicReference<ByteBuffer> _mappedBuffer = new AtomicReference<>();
private volatile long _lastAccessed;
private volatile Instant _lastAccessed;

CachedHttpContent(String pathInContext, Resource resource, Map<CompressedContentFormat, CachedHttpContent> precompressedResources)
{
Expand All @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -460,7 +453,7 @@ boolean isValid()
{
if (_lastModifiedValue == _resource.lastModified() && _contentLengthValue == _resource.length())
{
_lastAccessed = System.currentTimeMillis();
_lastAccessed = Instant.now();
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -198,7 +200,9 @@ public static String getAsHTML(Resource resource, String base, boolean parent, S
buf.append("</tr>\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)
{
Expand All @@ -223,9 +227,8 @@ public static String getAsHTML(Resource resource, String base, boolean parent, S

// Last Modified
buf.append("<td class=\"lastmodified\">");
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("&nbsp;</td>");

// Size
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -76,7 +77,7 @@ public String getName()
}

@Override
public long lastModified()
public Instant lastModified()
{
return _created;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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('"');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public int compare(Resource o1, Resource o2)
Collections.reverseOrder(BY_NAME_ASCENDING);

private static Comparator<? super Resource> BY_LAST_MODIFIED_ASCENDING =
Comparator.comparingLong(Resource::lastModified);
Comparator.comparing(Resource::lastModified);

private static Comparator<? super Resource> BY_LAST_MODIFIED_DESCENDING =
Collections.reverseOrder(BY_LAST_MODIFIED_ASCENDING);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}

Expand Down
Loading

0 comments on commit ec2bad1

Please sign in to comment.