changes from review

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2022-11-02 17:21:44 +11:00
parent af19607f1c
commit e28a528165
7 changed files with 84 additions and 47 deletions

View File

@ -34,9 +34,16 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>
* {@link HttpContent.Factory} implementation that wraps any other {@link HttpContent.Factory} instance
* using it as a caching authority.
* Only HttpContent instances whose path is not a directory are cached.
* using it as a caching authority. Only HttpContent instances whose path is not a directory are cached.
* </p>
* <p>
* No eviction is done by this {@link HttpContent.Factory}, once an entry is in the cache it is always
* assumed to be valid. This class can be extended to implement the validation behaviours on
* {@link CachingHttpContent} which allow entries to be evicted once they become invalid.
* </p>
* @see ValidatingCachingContentFactory
*/
public class CachingHttpContentFactory implements HttpContent.Factory
{
@ -118,7 +125,8 @@ public class CachingHttpContentFactory implements HttpContent.Factory
private void shrinkCache()
{
// While we need to shrink
while (_cache.size() > 0 && (_cache.size() > _maxCachedFiles || _cachedSize.get() > _maxCacheSize))
int numCacheEntries = _cache.size();
while (numCacheEntries > 0 && (numCacheEntries > _maxCachedFiles || _cachedSize.get() > _maxCacheSize))
{
// Scan the entire cache and generate an ordered list by last accessed time.
SortedSet<CachingHttpContent> sorted = new TreeSet<>((c1, c2) ->
@ -143,6 +151,8 @@ public class CachingHttpContentFactory implements HttpContent.Factory
break;
removeFromCache(content);
}
numCacheEntries = _cache.size();
}
}
@ -272,21 +282,24 @@ public class CachingHttpContentFactory implements HttpContent.Factory
}
_etagField = etagField;
// Map the content into memory if possible.
RetainableByteBuffer buffer = null;
try
// Read the content into memory if the HttpContent does not already have a buffer.
RetainableByteBuffer buffer = httpContent.getBuffer();
if (buffer != null)
{
long contentLengthValue = getContentLengthValue();
if (contentLengthValue > _maxCachedFileSize)
try
{
buffer = _byteBufferPool.acquire((int)contentLengthValue, false);
BufferUtil.readFrom(httpContent.getResource().newReadableByteChannel(), contentLengthValue, buffer.getBuffer());
long contentLengthValue = getContentLengthValue();
if (contentLengthValue > _maxCachedFileSize)
{
buffer = _byteBufferPool.acquire((int)contentLengthValue, false);
BufferUtil.readFrom(httpContent.getResource().newReadableByteChannel(), contentLengthValue, buffer.getBuffer());
}
}
catch (Throwable t)
{
buffer = null;
LOG.warn("Failed to read Resource", t);
}
}
catch (Throwable t)
{
buffer = null;
LOG.warn("Failed to read Resource", t);
}
_buffer = buffer;
@ -335,6 +348,7 @@ public class CachingHttpContentFactory implements HttpContent.Factory
@Override
public void release()
{
super.release();
if (_buffer != null)
_buffer.release();
}

View File

@ -41,24 +41,41 @@ import org.eclipse.jetty.util.thread.Scheduler;
* elapsed the entry will be invalid and will be evicted from the cache at the next access.
* </p>
*/
public class EvictingCachingContentFactory extends CachingHttpContentFactory implements Runnable
public class ValidatingCachingContentFactory extends CachingHttpContentFactory implements Runnable
{
private final Scheduler _scheduler;
private final long _sweepDelay;
private final long _validationTime;
private final long _maxCacheIdleTime;
public EvictingCachingContentFactory(@Name("authority") HttpContent.Factory authority,
@Name("validationTime") long validationTime)
/**
* Construct a {@link ValidatingCachingContentFactory} which validates entries upon use to check if they
* are still valid.
*
* @param authority the wrapped {@link HttpContent.Factory} to use.
* @param validationTime time between filesystem checks in ms to see if an {@link HttpContent} is still valid (-1 never validate, 0 always validate).
*/
public ValidatingCachingContentFactory(@Name("authority") HttpContent.Factory authority,
@Name("validationTime") long validationTime)
{
this(authority, validationTime, null, -1, -1);
}
public EvictingCachingContentFactory(@Name("authority") HttpContent.Factory authority,
@Name("validationTime") long validationTime,
@Name("scheduler") Scheduler scheduler,
@Name("sweepDelay") long sweepDelay,
@Name("maxCacheIdleTime") long maxCacheIdleTime)
/**
* Construct a {@link ValidatingCachingContentFactory} which validates entries upon use to check if they
* are still valid and an optional period sweeper of the cache to find invalid and old entries to evict.
*
* @param authority the wrapped {@link HttpContent.Factory} to use.
* @param validationTime time between filesystem checks in ms to see if an {@link HttpContent} is still valid (-1 never validate, 0 always validate).
* @param scheduler scheduler to use for the sweeper, can be null to not use sweeper.
* @param sweepDelay time between runs of the sweeper in ms (if < 0 never sweep for invalid entries).
* @param maxCacheIdleTime amount of time in ms an entry can be unused before evicted by the sweeper (if < 0 never evict unused entries).
*/
public ValidatingCachingContentFactory(@Name("authority") HttpContent.Factory authority,
@Name("validationTime") long validationTime,
@Name("scheduler") Scheduler scheduler,
@Name("sweepDelay") long sweepDelay,
@Name("maxCacheIdleTime") long maxCacheIdleTime)
{
super(authority);
_validationTime = validationTime;
@ -85,36 +102,42 @@ public class EvictingCachingContentFactory extends CachingHttpContentFactory imp
@Override
public void run()
{
ConcurrentMap<String, CachingHttpContent> cache = getCache();
for (Map.Entry<String, CachingHttpContent> entry : cache.entrySet())
try
{
CachingHttpContent value = entry.getValue();
if (_maxCacheIdleTime > 0 && NanoTime.since(value.getLastAccessedNanos()) > TimeUnit.MILLISECONDS.toNanos(_maxCacheIdleTime))
removeFromCache(value);
else if (!value.isValid())
removeFromCache(value);
ConcurrentMap<String, CachingHttpContent> cache = getCache();
for (Map.Entry<String, CachingHttpContent> entry : cache.entrySet())
{
CachingHttpContent value = entry.getValue();
if (_maxCacheIdleTime > 0 && NanoTime.since(value.getLastAccessedNanos()) > TimeUnit.MILLISECONDS.toNanos(_maxCacheIdleTime))
removeFromCache(value);
else if (!value.isValid())
removeFromCache(value);
}
}
finally
{
schedule();
}
schedule();
}
@Override
protected CachingHttpContent newCachedContent(String p, HttpContent httpContent)
{
return new EvictingCachedContent(p, httpContent, _validationTime);
return new ValidatingCachedContent(p, httpContent, _validationTime);
}
@Override
protected CachingHttpContent newNotFoundContent(String p)
{
return new EvictingNotFoundContent(p, _validationTime);
return new ValidatingNotFoundContent(p, _validationTime);
}
protected class EvictingCachedContent extends CachedHttpContent
protected class ValidatingCachedContent extends CachedHttpContent
{
private final long _validationTime;
private final AtomicLong _lastValidated = new AtomicLong();
public EvictingCachedContent(String key, HttpContent httpContent, long validationTime)
public ValidatingCachedContent(String key, HttpContent httpContent, long validationTime)
{
super(key, httpContent);
_lastValidated.set(NanoTime.now());
@ -140,12 +163,12 @@ public class EvictingCachingContentFactory extends CachingHttpContentFactory imp
}
}
protected static class EvictingNotFoundContent extends NotFoundHttpContent
protected static class ValidatingNotFoundContent extends NotFoundHttpContent
{
private final long _validationTime;
private final AtomicLong _lastValidated = new AtomicLong();
public EvictingNotFoundContent(String key, long validationTime)
public ValidatingNotFoundContent(String key, long validationTime)
{
super(key);
_validationTime = validationTime;

View File

@ -17,13 +17,13 @@ import java.time.Duration;
import java.util.List;
import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.EvictingCachingContentFactory;
import org.eclipse.jetty.http.FileMappedHttpContentFactory;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.PreCompressedHttpContentFactory;
import org.eclipse.jetty.http.ResourceHttpContentFactory;
import org.eclipse.jetty.http.ValidatingCachingContentFactory;
import org.eclipse.jetty.server.Context;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
@ -88,7 +88,7 @@ public class ResourceHandler extends Handler.Wrapper
HttpContent.Factory contentFactory = new ResourceHttpContentFactory(ResourceFactory.of(_resourceBase), _mimeTypes);
contentFactory = new PreCompressedHttpContentFactory(contentFactory, _resourceService.getPrecompressedFormats());
contentFactory = new FileMappedHttpContentFactory(contentFactory);
contentFactory = new EvictingCachingContentFactory(contentFactory, Duration.ofSeconds(1).toMillis());
contentFactory = new ValidatingCachingContentFactory(contentFactory, Duration.ofSeconds(1).toMillis());
return contentFactory;
}

View File

@ -38,7 +38,6 @@ import org.eclipse.jetty.http.CachingHttpContentFactory;
import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.DateGenerator;
import org.eclipse.jetty.http.EtagUtils;
import org.eclipse.jetty.http.EvictingCachingContentFactory;
import org.eclipse.jetty.http.FileMappedHttpContentFactory;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpField;
@ -48,6 +47,7 @@ import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.http.PreCompressedHttpContentFactory;
import org.eclipse.jetty.http.ResourceHttpContentFactory;
import org.eclipse.jetty.http.UriCompliance;
import org.eclipse.jetty.http.ValidatingCachingContentFactory;
import org.eclipse.jetty.logging.StacklessLogging;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
@ -669,7 +669,7 @@ public class ResourceHandlerTest
HttpContent.Factory contentFactory = new ResourceHttpContentFactory(ResourceFactory.of(getBaseResource()), getMimeTypes());
contentFactory = new PreCompressedHttpContentFactory(contentFactory, getPrecompressedFormats());
contentFactory = new FileMappedHttpContentFactory(contentFactory);
contentFactory = new EvictingCachingContentFactory(contentFactory, 0);
contentFactory = new ValidatingCachingContentFactory(contentFactory, 0);
return contentFactory;
}
};

View File

@ -45,7 +45,6 @@ import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponseWrapper;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.EvictingCachingContentFactory;
import org.eclipse.jetty.http.FileMappedHttpContentFactory;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpContentWrapper;
@ -59,6 +58,7 @@ import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.PreCompressedHttpContentFactory;
import org.eclipse.jetty.http.ResourceHttpContentFactory;
import org.eclipse.jetty.http.ValidatingCachingContentFactory;
import org.eclipse.jetty.io.ByteBufferInputStream;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.server.Components;
@ -144,7 +144,7 @@ public class DefaultServlet extends HttpServlet
long cacheValidationTime = getInitParameter("cacheValidationTime") != null ? Long.parseLong(getInitParameter("cacheValidationTime")) : -2;
if (maxCachedFiles != -2 || maxCacheSize != -2 || maxCachedFileSize != -2 || cacheValidationTime != -2)
{
EvictingCachingContentFactory cached = new EvictingCachingContentFactory(contentFactory,
ValidatingCachingContentFactory cached = new ValidatingCachingContentFactory(contentFactory,
(cacheValidationTime > -2) ? cacheValidationTime : Duration.ofSeconds(1).toMillis());
contentFactory = cached;
if (maxCacheSize >= 0)

View File

@ -25,7 +25,6 @@ import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.ee9.nested.ContextHandler.APIContext;
import org.eclipse.jetty.ee9.nested.ResourceService.WelcomeFactory;
import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.EvictingCachingContentFactory;
import org.eclipse.jetty.http.FileMappedHttpContentFactory;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpHeader;
@ -34,6 +33,7 @@ import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.PreCompressedHttpContentFactory;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.http.ResourceHttpContentFactory;
import org.eclipse.jetty.http.ValidatingCachingContentFactory;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
@ -112,7 +112,7 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory,
HttpContent.Factory contentFactory = new ResourceHttpContentFactory(this, _mimeTypes);
contentFactory = new PreCompressedHttpContentFactory(contentFactory, _resourceService.getPrecompressedFormats());
contentFactory = new FileMappedHttpContentFactory(contentFactory);
contentFactory = new EvictingCachingContentFactory(contentFactory, Duration.ofSeconds(1).toMillis());
contentFactory = new ValidatingCachingContentFactory(contentFactory, Duration.ofSeconds(1).toMillis());
return contentFactory;
}

View File

@ -32,7 +32,6 @@ import org.eclipse.jetty.ee9.nested.ResourceService;
import org.eclipse.jetty.ee9.nested.ResourceService.WelcomeFactory;
import org.eclipse.jetty.http.CachingHttpContentFactory;
import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.EvictingCachingContentFactory;
import org.eclipse.jetty.http.FileMappedHttpContentFactory;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpHeader;
@ -40,6 +39,7 @@ import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.PreCompressedHttpContentFactory;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.http.ResourceHttpContentFactory;
import org.eclipse.jetty.http.ValidatingCachingContentFactory;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.Resource;
@ -257,7 +257,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory, Welc
long cacheValidationTime = getInitParameter("cacheValidationTime") != null ? Long.parseLong(getInitParameter("cacheValidationTime")) : -2;
if (maxCachedFiles != -2 || maxCacheSize != -2 || maxCachedFileSize != -2 || cacheValidationTime != -2)
{
_cachingContentFactory = new EvictingCachingContentFactory(contentFactory,
_cachingContentFactory = new ValidatingCachingContentFactory(contentFactory,
(cacheValidationTime > -2) ? cacheValidationTime : Duration.ofSeconds(1).toMillis());
contentFactory = _cachingContentFactory;
if (maxCacheSize >= 0)