484622 - Improve handling of Direct and Mapped buffers for static content
ResourceHttpContent now applies a maxBufferSize that is passed through the call to getContent ResourceCache now accounts for the exact memory usage of content, which may have an indirect buffer plus either a direct or mapped buffer. Thus content size may be 0, 1 or 2 times the file size. Some more limited unit tests
This commit is contained in:
parent
ecbfe7c1d0
commit
5cd676581c
|
@ -68,6 +68,13 @@ public interface HttpContent
|
||||||
|
|
||||||
public interface Factory
|
public interface Factory
|
||||||
{
|
{
|
||||||
HttpContent getContent(String path) throws IOException;
|
/**
|
||||||
|
* @param path The path within the context to the resource
|
||||||
|
* @param maxBuffer The maximum buffer to allocated for this request. For cached content, a larger buffer may have
|
||||||
|
* previously been allocated and returned by the {@link HttpContent#getDirectBuffer()} or {@link HttpContent#getIndirectBuffer()} calls.
|
||||||
|
* @return A {@link HttpContent}
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
HttpContent getContent(String path,int maxBuffer) throws IOException;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,7 +125,7 @@ public class ResourceHttpContent implements HttpContent
|
||||||
@Override
|
@Override
|
||||||
public ByteBuffer getDirectBuffer()
|
public ByteBuffer getDirectBuffer()
|
||||||
{
|
{
|
||||||
if (_resource.length()<=0 || _maxBuffer<_resource.length())
|
if (_resource.length()<=0 || _maxBuffer>0 && _maxBuffer<_resource.length())
|
||||||
return null;
|
return null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -155,7 +155,7 @@ public class ResourceHttpContent implements HttpContent
|
||||||
@Override
|
@Override
|
||||||
public ByteBuffer getIndirectBuffer()
|
public ByteBuffer getIndirectBuffer()
|
||||||
{
|
{
|
||||||
if (_resource.length()<=0 || _maxBuffer<_resource.length())
|
if (_resource.length()<=0 || _maxBuffer>0 && _maxBuffer<_resource.length())
|
||||||
return null;
|
return null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -765,6 +765,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ByteBuffer buffer = _channel.useDirectBuffers() ? httpContent.getDirectBuffer() : null;
|
ByteBuffer buffer = _channel.useDirectBuffers() ? httpContent.getDirectBuffer() : null;
|
||||||
if (buffer == null)
|
if (buffer == null)
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.MappedByteBuffer;
|
||||||
import java.nio.channels.ReadableByteChannel;
|
import java.nio.channels.ReadableByteChannel;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
|
@ -172,7 +173,7 @@ public class ResourceCache implements HttpContent.Factory
|
||||||
public HttpContent lookup(String pathInContext)
|
public HttpContent lookup(String pathInContext)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
return getContent(pathInContext);
|
return getContent(pathInContext,_maxCachedFileSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -180,6 +181,8 @@ public class ResourceCache implements HttpContent.Factory
|
||||||
* Get either a valid entry object or create a new one if possible.
|
* Get either a valid entry object or create a new one if possible.
|
||||||
*
|
*
|
||||||
* @param pathInContext The key into the cache
|
* @param pathInContext The key into the cache
|
||||||
|
* @param maxBuffer The maximum buffer to allocated for this request. For cached content, a larger buffer may have
|
||||||
|
* previously been allocated and returned by the {@link HttpContent#getDirectBuffer()} or {@link HttpContent#getIndirectBuffer()} calls.
|
||||||
* @return The entry matching <code>pathInContext</code>, or a new entry
|
* @return The entry matching <code>pathInContext</code>, or a new entry
|
||||||
* if no matching entry was found. If the content exists but is not cachable,
|
* if no matching entry was found. If the content exists but is not cachable,
|
||||||
* then a {@link ResourceHttpContent} instance is return. If
|
* then a {@link ResourceHttpContent} instance is return. If
|
||||||
|
@ -187,7 +190,7 @@ public class ResourceCache implements HttpContent.Factory
|
||||||
* @throws IOException Problem loading the resource
|
* @throws IOException Problem loading the resource
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public HttpContent getContent(String pathInContext)
|
public HttpContent getContent(String pathInContext,int maxBufferSize)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
// Is the content in this cache?
|
// Is the content in this cache?
|
||||||
|
@ -197,14 +200,14 @@ public class ResourceCache implements HttpContent.Factory
|
||||||
|
|
||||||
// try loading the content from our factory.
|
// try loading the content from our factory.
|
||||||
Resource resource=_factory.getResource(pathInContext);
|
Resource resource=_factory.getResource(pathInContext);
|
||||||
HttpContent loaded = load(pathInContext,resource);
|
HttpContent loaded = load(pathInContext,resource,maxBufferSize);
|
||||||
if (loaded!=null)
|
if (loaded!=null)
|
||||||
return loaded;
|
return loaded;
|
||||||
|
|
||||||
// Is the content in the parent cache?
|
// Is the content in the parent cache?
|
||||||
if (_parent!=null)
|
if (_parent!=null)
|
||||||
{
|
{
|
||||||
HttpContent httpContent=_parent.lookup(pathInContext);
|
HttpContent httpContent=_parent.getContent(pathInContext,maxBufferSize);
|
||||||
if (httpContent!=null)
|
if (httpContent!=null)
|
||||||
return httpContent;
|
return httpContent;
|
||||||
}
|
}
|
||||||
|
@ -225,14 +228,13 @@ public class ResourceCache implements HttpContent.Factory
|
||||||
long len = resource.length();
|
long len = resource.length();
|
||||||
|
|
||||||
// Will it fit in the cache?
|
// Will it fit in the cache?
|
||||||
return (len>0 && len<_maxCachedFileSize && len<_maxCacheSize);
|
return (len>0 && (_useFileMappedBuffer || (len<_maxCachedFileSize && len<_maxCacheSize)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
private HttpContent load(String pathInContext, Resource resource)
|
private HttpContent load(String pathInContext, Resource resource, int maxBufferSize)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
|
||||||
if (resource==null || !resource.exists())
|
if (resource==null || !resource.exists())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
@ -256,7 +258,6 @@ public class ResourceCache implements HttpContent.Factory
|
||||||
if (resourceGz.exists() && resourceGz.lastModified()>=resource.lastModified() && resourceGz.length()<resource.length())
|
if (resourceGz.exists() && resourceGz.lastModified()>=resource.lastModified() && resourceGz.length()<resource.length())
|
||||||
{
|
{
|
||||||
contentGz = new CachedHttpContent(pathInContextGz,resourceGz,null);
|
contentGz = new CachedHttpContent(pathInContextGz,resourceGz,null);
|
||||||
shrinkCache();
|
|
||||||
CachedHttpContent added = _cache.putIfAbsent(pathInContextGz,contentGz);
|
CachedHttpContent added = _cache.putIfAbsent(pathInContextGz,contentGz);
|
||||||
if (added!=null)
|
if (added!=null)
|
||||||
{
|
{
|
||||||
|
@ -270,9 +271,6 @@ public class ResourceCache implements HttpContent.Factory
|
||||||
else
|
else
|
||||||
content = new CachedHttpContent(pathInContext,resource,null);
|
content = new CachedHttpContent(pathInContext,resource,null);
|
||||||
|
|
||||||
// reduce the cache to an acceptable size.
|
|
||||||
shrinkCache();
|
|
||||||
|
|
||||||
// Add it to the cache.
|
// Add it to the cache.
|
||||||
CachedHttpContent added = _cache.putIfAbsent(pathInContext,content);
|
CachedHttpContent added = _cache.putIfAbsent(pathInContext,content);
|
||||||
if (added!=null)
|
if (added!=null)
|
||||||
|
@ -284,7 +282,7 @@ public class ResourceCache implements HttpContent.Factory
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for a gzip resource or content
|
// Look for non Cacheable gzip resource or content
|
||||||
String mt = _mimeTypes.getMimeByExtension(pathInContext);
|
String mt = _mimeTypes.getMimeByExtension(pathInContext);
|
||||||
if (_gzip)
|
if (_gzip)
|
||||||
{
|
{
|
||||||
|
@ -292,16 +290,16 @@ public class ResourceCache implements HttpContent.Factory
|
||||||
String pathInContextGz=pathInContext+".gz";
|
String pathInContextGz=pathInContext+".gz";
|
||||||
CachedHttpContent contentGz = _cache.get(pathInContextGz);
|
CachedHttpContent contentGz = _cache.get(pathInContextGz);
|
||||||
if (contentGz!=null && contentGz.isValid() && contentGz.getResource().lastModified()>=resource.lastModified())
|
if (contentGz!=null && contentGz.isValid() && contentGz.getResource().lastModified()>=resource.lastModified())
|
||||||
return new ResourceHttpContent(resource,mt,getMaxCachedFileSize(),contentGz);
|
return new ResourceHttpContent(resource,mt,maxBufferSize,contentGz);
|
||||||
|
|
||||||
// Is there a gzip resource?
|
// Is there a gzip resource?
|
||||||
Resource resourceGz=_factory.getResource(pathInContextGz);
|
Resource resourceGz=_factory.getResource(pathInContextGz);
|
||||||
if (resourceGz.exists() && resourceGz.lastModified()>=resource.lastModified() && resourceGz.length()<resource.length())
|
if (resourceGz.exists() && resourceGz.lastModified()>=resource.lastModified() && resourceGz.length()<resource.length())
|
||||||
return new ResourceHttpContent(resource,mt,getMaxCachedFileSize(),
|
return new ResourceHttpContent(resource,mt,maxBufferSize,
|
||||||
new ResourceHttpContent(resourceGz,_mimeTypes.getMimeByExtension(pathInContextGz),getMaxCachedFileSize()));
|
new ResourceHttpContent(resourceGz,_mimeTypes.getMimeByExtension(pathInContextGz),maxBufferSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ResourceHttpContent(resource,mt,getMaxCachedFileSize());
|
return new ResourceHttpContent(resource,mt,maxBufferSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -359,6 +357,8 @@ public class ResourceCache implements HttpContent.Factory
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
protected ByteBuffer getDirectBuffer(Resource resource)
|
protected ByteBuffer getDirectBuffer(Resource resource)
|
||||||
{
|
{
|
||||||
|
// Only use file mapped buffers for cached resources, otherwise too much virtual memory commitment for
|
||||||
|
// a non shared resource. Also ignore max buffer size
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_useFileMappedBuffer && resource.getFile()!=null && resource.length()<Integer.MAX_VALUE)
|
if (_useFileMappedBuffer && resource.getFile()!=null && resource.length()<Integer.MAX_VALUE)
|
||||||
|
@ -421,8 +421,9 @@ public class ResourceCache implements HttpContent.Factory
|
||||||
_contentLengthValue=exists?(int)resource.length():0;
|
_contentLengthValue=exists?(int)resource.length():0;
|
||||||
_contentLength=new PreEncodedHttpField(HttpHeader.CONTENT_LENGTH,Long.toString(_contentLengthValue));
|
_contentLength=new PreEncodedHttpField(HttpHeader.CONTENT_LENGTH,Long.toString(_contentLengthValue));
|
||||||
|
|
||||||
_cachedSize.addAndGet(_contentLengthValue);
|
if (_cachedFiles.incrementAndGet()>_maxCachedFiles)
|
||||||
_cachedFiles.incrementAndGet();
|
shrinkCache();
|
||||||
|
|
||||||
_lastAccessed=System.currentTimeMillis();
|
_lastAccessed=System.currentTimeMillis();
|
||||||
|
|
||||||
_etag=ResourceCache.this._etags?new PreEncodedHttpField(HttpHeader.ETAG,resource.getWeakETag()):null;
|
_etag=ResourceCache.this._etags?new PreEncodedHttpField(HttpHeader.ETAG,resource.getWeakETag()):null;
|
||||||
|
@ -487,8 +488,14 @@ public class ResourceCache implements HttpContent.Factory
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
protected void invalidate()
|
protected void invalidate()
|
||||||
{
|
{
|
||||||
// Invalidate it
|
ByteBuffer indirect=_indirectBuffer.get();
|
||||||
_cachedSize.addAndGet(-_contentLengthValue);
|
if (indirect!=null && _indirectBuffer.compareAndSet(indirect,null))
|
||||||
|
_cachedSize.addAndGet(-BufferUtil.length(indirect));
|
||||||
|
|
||||||
|
ByteBuffer direct=_directBuffer.get();
|
||||||
|
if (direct!=null && !BufferUtil.isMappedBuffer(direct) && _directBuffer.compareAndSet(direct,null))
|
||||||
|
_cachedSize.addAndGet(-BufferUtil.length(direct));
|
||||||
|
|
||||||
_cachedFiles.decrementAndGet();
|
_cachedFiles.decrementAndGet();
|
||||||
_resource.close();
|
_resource.close();
|
||||||
}
|
}
|
||||||
|
@ -507,7 +514,6 @@ public class ResourceCache implements HttpContent.Factory
|
||||||
return _lastModified==null?null:_lastModified.getValue();
|
return _lastModified==null?null:_lastModified.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
@Override
|
@Override
|
||||||
public HttpField getContentType()
|
public HttpField getContentType()
|
||||||
|
@ -569,7 +575,11 @@ public class ResourceCache implements HttpContent.Factory
|
||||||
if (buffer2==null)
|
if (buffer2==null)
|
||||||
LOG.warn("Could not load "+this);
|
LOG.warn("Could not load "+this);
|
||||||
else if (_indirectBuffer.compareAndSet(null,buffer2))
|
else if (_indirectBuffer.compareAndSet(null,buffer2))
|
||||||
|
{
|
||||||
buffer=buffer2;
|
buffer=buffer2;
|
||||||
|
if (_cachedSize.addAndGet(BufferUtil.length(buffer))>_maxCacheSize)
|
||||||
|
shrinkCache();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
buffer=_indirectBuffer.get();
|
buffer=_indirectBuffer.get();
|
||||||
}
|
}
|
||||||
|
@ -578,7 +588,6 @@ public class ResourceCache implements HttpContent.Factory
|
||||||
return buffer.slice();
|
return buffer.slice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
@Override
|
@Override
|
||||||
public ByteBuffer getDirectBuffer()
|
public ByteBuffer getDirectBuffer()
|
||||||
|
@ -591,7 +600,12 @@ public class ResourceCache implements HttpContent.Factory
|
||||||
if (buffer2==null)
|
if (buffer2==null)
|
||||||
LOG.warn("Could not load "+this);
|
LOG.warn("Could not load "+this);
|
||||||
else if (_directBuffer.compareAndSet(null,buffer2))
|
else if (_directBuffer.compareAndSet(null,buffer2))
|
||||||
|
{
|
||||||
buffer=buffer2;
|
buffer=buffer2;
|
||||||
|
|
||||||
|
if (!BufferUtil.isMappedBuffer(buffer) && _cachedSize.addAndGet(BufferUtil.length(buffer))>_maxCacheSize)
|
||||||
|
shrinkCache();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
buffer=_directBuffer.get();
|
buffer=_directBuffer.get();
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,55 +27,47 @@ import org.eclipse.jetty.http.ResourceHttpContent;
|
||||||
import org.eclipse.jetty.util.resource.Resource;
|
import org.eclipse.jetty.util.resource.Resource;
|
||||||
import org.eclipse.jetty.util.resource.ResourceFactory;
|
import org.eclipse.jetty.util.resource.ResourceFactory;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A HttpContent.Factory for transient content. The HttpContent's created by
|
||||||
|
* this factory are not intended to be cached, so memory limits for individual
|
||||||
|
* HttpOutput streams are enforced.
|
||||||
|
*/
|
||||||
public class ResourceContentFactory implements Factory
|
public class ResourceContentFactory implements Factory
|
||||||
{
|
{
|
||||||
private final ResourceFactory _factory;
|
private final ResourceFactory _factory;
|
||||||
private final MimeTypes _mimeTypes;
|
private final MimeTypes _mimeTypes;
|
||||||
private final int _maxBufferSize;
|
|
||||||
private final boolean _gzip;
|
private final boolean _gzip;
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public ResourceContentFactory(ResourceFactory factory, MimeTypes mimeTypes, int maxBufferSize, boolean gzip)
|
public ResourceContentFactory(ResourceFactory factory, MimeTypes mimeTypes, boolean gzip)
|
||||||
{
|
{
|
||||||
_factory=factory;
|
_factory=factory;
|
||||||
_mimeTypes=mimeTypes;
|
_mimeTypes=mimeTypes;
|
||||||
_maxBufferSize=maxBufferSize;
|
|
||||||
_gzip=gzip;
|
_gzip=gzip;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/** Get a Entry from the cache.
|
|
||||||
* Get either a valid entry object or create a new one if possible.
|
|
||||||
*
|
|
||||||
* @param pathInContext The key into the cache
|
|
||||||
* @return The entry matching <code>pathInContext</code>, or a new entry
|
|
||||||
* if no matching entry was found. If the content exists but is not cachable,
|
|
||||||
* then a {@link ResourceHttpContent} instance is return. If
|
|
||||||
* the resource does not exist, then null is returned.
|
|
||||||
* @throws IOException Problem loading the resource
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public HttpContent getContent(String pathInContext)
|
public HttpContent getContent(String pathInContext,int maxBufferSize)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
|
||||||
// try loading the content from our factory.
|
// try loading the content from our factory.
|
||||||
Resource resource=_factory.getResource(pathInContext);
|
Resource resource=_factory.getResource(pathInContext);
|
||||||
HttpContent loaded = load(pathInContext,resource);
|
HttpContent loaded = load(pathInContext,resource,maxBufferSize);
|
||||||
return loaded;
|
return loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
private HttpContent load(String pathInContext, Resource resource)
|
private HttpContent load(String pathInContext, Resource resource, int maxBufferSize)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
if (resource==null || !resource.exists())
|
if (resource==null || !resource.exists())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (resource.isDirectory())
|
if (resource.isDirectory())
|
||||||
return new ResourceHttpContent(resource,_mimeTypes.getMimeByExtension(resource.toString()),_maxBufferSize);
|
return new ResourceHttpContent(resource,_mimeTypes.getMimeByExtension(resource.toString()),maxBufferSize);
|
||||||
|
|
||||||
// Look for a gzip resource or content
|
// Look for a gzip resource or content
|
||||||
String mt = _mimeTypes.getMimeByExtension(pathInContext);
|
String mt = _mimeTypes.getMimeByExtension(pathInContext);
|
||||||
|
@ -85,11 +77,11 @@ public class ResourceContentFactory implements Factory
|
||||||
String pathInContextGz=pathInContext+".gz";
|
String pathInContextGz=pathInContext+".gz";
|
||||||
Resource resourceGz=_factory.getResource(pathInContextGz);
|
Resource resourceGz=_factory.getResource(pathInContextGz);
|
||||||
if (resourceGz.exists() && resourceGz.lastModified()>=resource.lastModified() && resourceGz.length()<resource.length())
|
if (resourceGz.exists() && resourceGz.lastModified()>=resource.lastModified() && resourceGz.length()<resource.length())
|
||||||
return new ResourceHttpContent(resource,mt,_maxBufferSize,
|
return new ResourceHttpContent(resource,mt,maxBufferSize,
|
||||||
new ResourceHttpContent(resourceGz,_mimeTypes.getMimeByExtension(pathInContextGz),_maxBufferSize));
|
new ResourceHttpContent(resourceGz,_mimeTypes.getMimeByExtension(pathInContextGz),maxBufferSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ResourceHttpContent(resource,mt,_maxBufferSize);
|
return new ResourceHttpContent(resource,mt,maxBufferSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.server;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -30,6 +31,7 @@ import java.io.OutputStream;
|
||||||
import org.eclipse.jetty.http.HttpContent;
|
import org.eclipse.jetty.http.HttpContent;
|
||||||
import org.eclipse.jetty.http.MimeTypes;
|
import org.eclipse.jetty.http.MimeTypes;
|
||||||
import org.eclipse.jetty.http.ResourceHttpContent;
|
import org.eclipse.jetty.http.ResourceHttpContent;
|
||||||
|
import org.eclipse.jetty.toolchain.test.OS;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.resource.Resource;
|
import org.eclipse.jetty.util.resource.Resource;
|
||||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||||
|
@ -136,50 +138,83 @@ public class ResourceCacheTest
|
||||||
cache.setMaxCachedFileSize(85);
|
cache.setMaxCachedFileSize(85);
|
||||||
cache.setMaxCachedFiles(4);
|
cache.setMaxCachedFiles(4);
|
||||||
|
|
||||||
assertTrue(cache.lookup("does not exist")==null);
|
assertTrue(cache.getContent("does not exist",4096)==null);
|
||||||
assertTrue(cache.lookup(names[9]) instanceof ResourceHttpContent);
|
assertTrue(cache.getContent(names[9],4096) instanceof ResourceHttpContent);
|
||||||
|
assertTrue(cache.getContent(names[9],4096).getIndirectBuffer()!=null);
|
||||||
|
|
||||||
HttpContent content;
|
HttpContent content;
|
||||||
content=cache.lookup(names[8]);
|
content=cache.getContent(names[8],4096);
|
||||||
assertTrue(content!=null);
|
assertTrue(content!=null);
|
||||||
assertEquals(80,content.getContentLengthValue());
|
assertEquals(80,content.getContentLengthValue());
|
||||||
|
assertEquals(0,cache.getCachedSize());
|
||||||
|
|
||||||
|
if (OS.IS_LINUX)
|
||||||
|
{
|
||||||
|
// Initially not using memory mapped files
|
||||||
|
content.getDirectBuffer();
|
||||||
|
assertEquals(80,cache.getCachedSize());
|
||||||
|
|
||||||
|
// with both types of buffer loaded, this is too large for cache
|
||||||
|
content.getIndirectBuffer();
|
||||||
|
assertEquals(0,cache.getCachedSize());
|
||||||
|
assertEquals(0,cache.getCachedFiles());
|
||||||
|
|
||||||
|
cache=new ResourceCache(null,directory,new MimeTypes(),true,false,false);
|
||||||
|
cache.setMaxCacheSize(95);
|
||||||
|
cache.setMaxCachedFileSize(85);
|
||||||
|
cache.setMaxCachedFiles(4);
|
||||||
|
|
||||||
|
content=cache.getContent(names[8],4096);
|
||||||
|
content.getDirectBuffer();
|
||||||
|
assertEquals(cache.isUseFileMappedBuffer()?0:80,cache.getCachedSize());
|
||||||
|
|
||||||
|
// with both types of buffer loaded, this is not too large for cache because
|
||||||
|
// mapped buffers don't count, so we can continue
|
||||||
|
}
|
||||||
|
content.getIndirectBuffer();
|
||||||
assertEquals(80,cache.getCachedSize());
|
assertEquals(80,cache.getCachedSize());
|
||||||
assertEquals(1,cache.getCachedFiles());
|
assertEquals(1,cache.getCachedFiles());
|
||||||
|
|
||||||
Thread.sleep(200);
|
Thread.sleep(200);
|
||||||
|
|
||||||
content=cache.lookup(names[1]);
|
content=cache.getContent(names[1],4096);
|
||||||
|
assertEquals(80,cache.getCachedSize());
|
||||||
|
content.getIndirectBuffer();
|
||||||
assertEquals(90,cache.getCachedSize());
|
assertEquals(90,cache.getCachedSize());
|
||||||
assertEquals(2,cache.getCachedFiles());
|
assertEquals(2,cache.getCachedFiles());
|
||||||
|
|
||||||
Thread.sleep(200);
|
Thread.sleep(200);
|
||||||
|
|
||||||
content=cache.lookup(names[2]);
|
content=cache.getContent(names[2],4096);
|
||||||
|
content.getIndirectBuffer();
|
||||||
assertEquals(30,cache.getCachedSize());
|
assertEquals(30,cache.getCachedSize());
|
||||||
assertEquals(2,cache.getCachedFiles());
|
assertEquals(2,cache.getCachedFiles());
|
||||||
|
|
||||||
Thread.sleep(200);
|
Thread.sleep(200);
|
||||||
|
|
||||||
content=cache.lookup(names[3]);
|
content=cache.getContent(names[3],4096);
|
||||||
|
content.getIndirectBuffer();
|
||||||
assertEquals(60,cache.getCachedSize());
|
assertEquals(60,cache.getCachedSize());
|
||||||
assertEquals(3,cache.getCachedFiles());
|
assertEquals(3,cache.getCachedFiles());
|
||||||
|
|
||||||
Thread.sleep(200);
|
Thread.sleep(200);
|
||||||
|
|
||||||
content=cache.lookup(names[4]);
|
content=cache.getContent(names[4],4096);
|
||||||
|
content.getIndirectBuffer();
|
||||||
assertEquals(90,cache.getCachedSize());
|
assertEquals(90,cache.getCachedSize());
|
||||||
assertEquals(3,cache.getCachedFiles());
|
assertEquals(3,cache.getCachedFiles());
|
||||||
|
|
||||||
Thread.sleep(200);
|
Thread.sleep(200);
|
||||||
|
|
||||||
content=cache.lookup(names[5]);
|
content=cache.getContent(names[5],4096);
|
||||||
|
content.getIndirectBuffer();
|
||||||
assertEquals(90,cache.getCachedSize());
|
assertEquals(90,cache.getCachedSize());
|
||||||
assertEquals(2,cache.getCachedFiles());
|
assertEquals(2,cache.getCachedFiles());
|
||||||
|
|
||||||
Thread.sleep(200);
|
Thread.sleep(200);
|
||||||
|
|
||||||
content=cache.lookup(names[6]);
|
content=cache.getContent(names[6],4096);
|
||||||
|
content.getIndirectBuffer();
|
||||||
assertEquals(60,cache.getCachedSize());
|
assertEquals(60,cache.getCachedSize());
|
||||||
assertEquals(1,cache.getCachedFiles());
|
assertEquals(1,cache.getCachedFiles());
|
||||||
|
|
||||||
|
@ -189,37 +224,43 @@ public class ResourceCacheTest
|
||||||
{
|
{
|
||||||
out.write(' ');
|
out.write(' ');
|
||||||
}
|
}
|
||||||
content=cache.lookup(names[7]);
|
content=cache.getContent(names[7],4096);
|
||||||
|
content.getIndirectBuffer();
|
||||||
assertEquals(70,cache.getCachedSize());
|
assertEquals(70,cache.getCachedSize());
|
||||||
assertEquals(1,cache.getCachedFiles());
|
assertEquals(1,cache.getCachedFiles());
|
||||||
|
|
||||||
Thread.sleep(200);
|
Thread.sleep(200);
|
||||||
|
|
||||||
content=cache.lookup(names[6]);
|
content=cache.getContent(names[6],4096);
|
||||||
|
content.getIndirectBuffer();
|
||||||
assertEquals(71,cache.getCachedSize());
|
assertEquals(71,cache.getCachedSize());
|
||||||
assertEquals(2,cache.getCachedFiles());
|
assertEquals(2,cache.getCachedFiles());
|
||||||
|
|
||||||
Thread.sleep(200);
|
Thread.sleep(200);
|
||||||
|
|
||||||
content=cache.lookup(names[0]);
|
content=cache.getContent(names[0],4096);
|
||||||
|
content.getIndirectBuffer();
|
||||||
assertEquals(72,cache.getCachedSize());
|
assertEquals(72,cache.getCachedSize());
|
||||||
assertEquals(3,cache.getCachedFiles());
|
assertEquals(3,cache.getCachedFiles());
|
||||||
|
|
||||||
Thread.sleep(200);
|
Thread.sleep(200);
|
||||||
|
|
||||||
content=cache.lookup(names[1]);
|
content=cache.getContent(names[1],4096);
|
||||||
|
content.getIndirectBuffer();
|
||||||
assertEquals(82,cache.getCachedSize());
|
assertEquals(82,cache.getCachedSize());
|
||||||
assertEquals(4,cache.getCachedFiles());
|
assertEquals(4,cache.getCachedFiles());
|
||||||
|
|
||||||
Thread.sleep(200);
|
Thread.sleep(200);
|
||||||
|
|
||||||
content=cache.lookup(names[2]);
|
content=cache.getContent(names[2],4096);
|
||||||
|
content.getIndirectBuffer();
|
||||||
assertEquals(32,cache.getCachedSize());
|
assertEquals(32,cache.getCachedSize());
|
||||||
assertEquals(4,cache.getCachedFiles());
|
assertEquals(4,cache.getCachedFiles());
|
||||||
|
|
||||||
Thread.sleep(200);
|
Thread.sleep(200);
|
||||||
|
|
||||||
content=cache.lookup(names[3]);
|
content=cache.getContent(names[3],4096);
|
||||||
|
content.getIndirectBuffer();
|
||||||
assertEquals(61,cache.getCachedSize());
|
assertEquals(61,cache.getCachedSize());
|
||||||
assertEquals(4,cache.getCachedFiles());
|
assertEquals(4,cache.getCachedFiles());
|
||||||
|
|
||||||
|
|
|
@ -288,8 +288,15 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
||||||
throw new UnavailableException(e.toString());
|
throw new UnavailableException(e.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
_contentFactory=_cache==null?new ResourceContentFactory(this,_mimeTypes,-1,_gzip):_cache; // TODO pass a buffer size
|
if (_cache!=null)
|
||||||
|
_contentFactory=_cache;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_contentFactory=new ResourceContentFactory(this,_mimeTypes,_gzip);
|
||||||
|
if (resourceCache!=null)
|
||||||
|
_servletContext.setAttribute(resourceCache,_contentFactory);
|
||||||
|
}
|
||||||
|
|
||||||
_gzipEquivalentFileExtensions = new ArrayList<String>();
|
_gzipEquivalentFileExtensions = new ArrayList<String>();
|
||||||
String otherGzipExtensions = getInitParameter("otherGzipFileExtensions");
|
String otherGzipExtensions = getInitParameter("otherGzipFileExtensions");
|
||||||
if (otherGzipExtensions != null)
|
if (otherGzipExtensions != null)
|
||||||
|
@ -460,7 +467,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Find the content
|
// Find the content
|
||||||
content=_contentFactory.getContent(pathInContext);
|
content=_contentFactory.getContent(pathInContext,response.getBufferSize());
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.info("content={}",content);
|
LOG.info("content={}",content);
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
@ -39,8 +40,10 @@ import javax.servlet.ServletRequest;
|
||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.DateGenerator;
|
import org.eclipse.jetty.http.DateGenerator;
|
||||||
|
import org.eclipse.jetty.http.HttpContent;
|
||||||
import org.eclipse.jetty.server.HttpConfiguration;
|
import org.eclipse.jetty.server.HttpConfiguration;
|
||||||
import org.eclipse.jetty.server.LocalConnector;
|
import org.eclipse.jetty.server.LocalConnector;
|
||||||
|
import org.eclipse.jetty.server.ResourceContentFactory;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker;
|
import org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker;
|
||||||
import org.eclipse.jetty.toolchain.test.FS;
|
import org.eclipse.jetty.toolchain.test.FS;
|
||||||
|
@ -48,6 +51,7 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||||
import org.eclipse.jetty.toolchain.test.OS;
|
import org.eclipse.jetty.toolchain.test.OS;
|
||||||
import org.eclipse.jetty.toolchain.test.TestingDir;
|
import org.eclipse.jetty.toolchain.test.TestingDir;
|
||||||
import org.eclipse.jetty.util.IO;
|
import org.eclipse.jetty.util.IO;
|
||||||
|
import org.eclipse.jetty.util.resource.Resource;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
@ -521,6 +525,45 @@ public class DefaultServletTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDirectFromResourceHttpContent() throws Exception
|
||||||
|
{
|
||||||
|
if (!OS.IS_LINUX)
|
||||||
|
return;
|
||||||
|
|
||||||
|
testdir.ensureEmpty();
|
||||||
|
File resBase = testdir.getPathFile("docroot").toFile();
|
||||||
|
FS.ensureDirExists(resBase);
|
||||||
|
context.setBaseResource(Resource.newResource(resBase));
|
||||||
|
|
||||||
|
File index = new File(resBase, "index.html");
|
||||||
|
createFile(index, "<h1>Hello World</h1>");
|
||||||
|
|
||||||
|
ServletHolder defholder = context.addServlet(DefaultServlet.class, "/");
|
||||||
|
defholder.setInitParameter("dirAllowed", "true");
|
||||||
|
defholder.setInitParameter("redirectWelcome", "false");
|
||||||
|
defholder.setInitParameter("useFileMappedBuffer", "true");
|
||||||
|
defholder.setInitParameter("welcomeServlets", "exact");
|
||||||
|
defholder.setInitParameter("gzip", "false");
|
||||||
|
defholder.setInitParameter("resourceCache","resourceCache");
|
||||||
|
|
||||||
|
String response;
|
||||||
|
|
||||||
|
response = connector.getResponses("GET /context/index.html HTTP/1.0\r\n\r\n");
|
||||||
|
assertResponseContains("<h1>Hello World</h1>", response);
|
||||||
|
|
||||||
|
ResourceContentFactory factory = (ResourceContentFactory)context.getServletContext().getAttribute("resourceCache");
|
||||||
|
|
||||||
|
HttpContent content = factory.getContent("/index.html",200);
|
||||||
|
ByteBuffer buffer = content.getDirectBuffer();
|
||||||
|
Assert.assertTrue(buffer.isDirect());
|
||||||
|
content = factory.getContent("/index.html",5);
|
||||||
|
buffer = content.getDirectBuffer();
|
||||||
|
Assert.assertTrue(buffer==null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRangeRequests() throws Exception
|
public void testRangeRequests() throws Exception
|
||||||
{
|
{
|
||||||
|
|
|
@ -1107,10 +1107,10 @@ public class DoSFilter implements Filter
|
||||||
{
|
{
|
||||||
private static final long serialVersionUID = 3534663738034577872L;
|
private static final long serialVersionUID = 3534663738034577872L;
|
||||||
|
|
||||||
protected transient final String _id;
|
protected final String _id;
|
||||||
protected transient final int _type;
|
protected final int _type;
|
||||||
protected transient final long[] _timestamps;
|
protected final long[] _timestamps;
|
||||||
protected transient int _next;
|
protected int _next;
|
||||||
|
|
||||||
public RateTracker(String id, int type, int maxRequestsPerSecond)
|
public RateTracker(String id, int type, int maxRequestsPerSecond)
|
||||||
{
|
{
|
||||||
|
@ -1164,16 +1164,14 @@ public class DoSFilter implements Filter
|
||||||
public void sessionWillPassivate(HttpSessionEvent se)
|
public void sessionWillPassivate(HttpSessionEvent se)
|
||||||
{
|
{
|
||||||
//take the tracker of the list of trackers (if its still there)
|
//take the tracker of the list of trackers (if its still there)
|
||||||
//and ensure that we take ourselves out of the session so we are not saved
|
|
||||||
_rateTrackers.remove(_id);
|
_rateTrackers.remove(_id);
|
||||||
se.getSession().removeAttribute(__TRACKER);
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("Value removed: {}", getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sessionDidActivate(HttpSessionEvent se)
|
public void sessionDidActivate(HttpSessionEvent se)
|
||||||
{
|
{
|
||||||
LOG.warn("Unexpected session activation");
|
RateTracker tracker = (RateTracker)se.getSession().getAttribute(__TRACKER);
|
||||||
|
if (tracker!=null)
|
||||||
|
_rateTrackers.put(tracker.getId(),tracker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,17 +19,21 @@
|
||||||
package org.eclipse.jetty.util;
|
package org.eclipse.jetty.util;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileDescriptor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.RandomAccessFile;
|
import java.io.RandomAccessFile;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.nio.Buffer;
|
import java.nio.Buffer;
|
||||||
import java.nio.BufferOverflowException;
|
import java.nio.BufferOverflowException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.MappedByteBuffer;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
import java.nio.channels.FileChannel.MapMode;
|
import java.nio.channels.FileChannel.MapMode;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
@ -903,12 +907,48 @@ public class BufferUtil
|
||||||
|
|
||||||
public static ByteBuffer toMappedBuffer(File file) throws IOException
|
public static ByteBuffer toMappedBuffer(File file) throws IOException
|
||||||
{
|
{
|
||||||
try (RandomAccessFile raf = new RandomAccessFile(file, "r"))
|
try (FileChannel channel = FileChannel.open(file.toPath(),StandardOpenOption.READ))
|
||||||
{
|
{
|
||||||
return raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length());
|
return channel.map(MapMode.READ_ONLY, 0, file.length());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final Field fdMappedByteBuffer;
|
||||||
|
static
|
||||||
|
{
|
||||||
|
Field fd = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fd=MappedByteBuffer.class.getDeclaredField("fd");
|
||||||
|
fd.setAccessible(true);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
fdMappedByteBuffer=fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMappedBuffer(ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
if (!(buffer instanceof MappedByteBuffer))
|
||||||
|
return false;
|
||||||
|
MappedByteBuffer mapped = (MappedByteBuffer) buffer;
|
||||||
|
|
||||||
|
if (fdMappedByteBuffer!=null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (fdMappedByteBuffer.get(mapped) instanceof FileDescriptor)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static ByteBuffer toBuffer(Resource resource,boolean direct) throws IOException
|
public static ByteBuffer toBuffer(Resource resource,boolean direct) throws IOException
|
||||||
{
|
{
|
||||||
int len=(int)resource.length();
|
int len=(int)resource.length();
|
||||||
|
@ -1156,4 +1196,5 @@ public class BufferUtil
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,13 +21,20 @@ package org.eclipse.jetty.util;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.BufferOverflowException;
|
import java.nio.BufferOverflowException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.channels.FileChannel.MapMode;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.OpenOption;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
|
@ -335,4 +342,39 @@ public class BufferUtilTest
|
||||||
BufferUtil.writeTo(buffer.asReadOnlyBuffer(), out);
|
BufferUtil.writeTo(buffer.asReadOnlyBuffer(), out);
|
||||||
assertThat("Bytes in out equal bytes in buffer", Arrays.equals(bytes, out.toByteArray()), is(true));
|
assertThat("Bytes in out equal bytes in buffer", Arrays.equals(bytes, out.toByteArray()), is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMappedFile() throws Exception
|
||||||
|
{
|
||||||
|
String data="Now is the time for all good men to come to the aid of the party";
|
||||||
|
File file = File.createTempFile("test",".txt");
|
||||||
|
file.deleteOnExit();
|
||||||
|
try(FileWriter out = new FileWriter(file);)
|
||||||
|
{
|
||||||
|
out.write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer mapped = BufferUtil.toMappedBuffer(file);
|
||||||
|
assertEquals(data,BufferUtil.toString(mapped));
|
||||||
|
assertTrue(BufferUtil.isMappedBuffer(mapped));
|
||||||
|
|
||||||
|
ByteBuffer direct = BufferUtil.allocateDirect(data.length());
|
||||||
|
direct.clear();
|
||||||
|
direct.put(data.getBytes(StandardCharsets.ISO_8859_1));
|
||||||
|
direct.flip();
|
||||||
|
assertEquals(data,BufferUtil.toString(direct));
|
||||||
|
assertFalse(BufferUtil.isMappedBuffer(direct));
|
||||||
|
|
||||||
|
ByteBuffer slice = direct.slice();
|
||||||
|
assertEquals(data,BufferUtil.toString(slice));
|
||||||
|
assertFalse(BufferUtil.isMappedBuffer(slice));
|
||||||
|
|
||||||
|
ByteBuffer duplicate = direct.duplicate();
|
||||||
|
assertEquals(data,BufferUtil.toString(duplicate));
|
||||||
|
assertFalse(BufferUtil.isMappedBuffer(duplicate));
|
||||||
|
|
||||||
|
ByteBuffer readonly = direct.asReadOnlyBuffer();
|
||||||
|
assertEquals(data,BufferUtil.toString(readonly));
|
||||||
|
assertFalse(BufferUtil.isMappedBuffer(readonly));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue