319334 improved resource cache
Made the cache use concurrent data structures Removed the dual NIO/BIO caches Use the cache for streaming data made DefaultServlet check for shared cache some javadoc cleanups git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@2085 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
parent
2605267f3f
commit
9f0a75da75
|
@ -1,4 +1,5 @@
|
|||
jetty-7.2-SNAPSHOT
|
||||
+ 319334 Concurrent, sharable ResourceCache
|
||||
|
||||
jetty-7.1.5.v20100705
|
||||
+ Update ecj to 3.6 Helios release drop
|
||||
|
|
|
@ -157,7 +157,7 @@ public class ContentExchangeTest
|
|||
getExchange.setMethod(HttpMethods.HEAD);
|
||||
|
||||
_client.send(getExchange);
|
||||
int state = getExchange.waitForDone();
|
||||
getExchange.waitForDone();
|
||||
|
||||
int responseStatus = getExchange.getResponseStatus();
|
||||
|
||||
|
|
|
@ -534,7 +534,6 @@ public class HttpExchangeTest extends TestCase
|
|||
}
|
||||
catch(InterruptedException e)
|
||||
{
|
||||
System.err.println(e);
|
||||
Log.debug(e);
|
||||
}
|
||||
catch(IOException e)
|
||||
|
|
|
@ -17,7 +17,6 @@ import java.util.Collections;
|
|||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.client.ContentExchangeTest.TestHandler;
|
||||
import org.eclipse.jetty.client.security.Realm;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.security.Constraint;
|
||||
|
@ -40,6 +39,7 @@ public class SecuredErrorStatusTest
|
|||
private Realm _testRealm;
|
||||
private Realm _dummyRealm;
|
||||
|
||||
@Override
|
||||
public void testPutUnauthorized()
|
||||
throws Exception
|
||||
{
|
||||
|
@ -60,6 +60,7 @@ public class SecuredErrorStatusTest
|
|||
setRealm(_testRealm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testGetUnauthorized()
|
||||
throws Exception
|
||||
{
|
||||
|
@ -80,6 +81,7 @@ public class SecuredErrorStatusTest
|
|||
setRealm(_testRealm);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureServer(Server server)
|
||||
throws Exception
|
||||
{
|
||||
|
|
|
@ -28,7 +28,8 @@ public interface HttpContent
|
|||
{
|
||||
Buffer getContentType();
|
||||
Buffer getLastModified();
|
||||
Buffer getBuffer();
|
||||
Buffer getIndirectBuffer();
|
||||
Buffer getDirectBuffer();
|
||||
Resource getResource();
|
||||
long getContentLength();
|
||||
InputStream getInputStream() throws IOException;
|
||||
|
|
|
@ -46,6 +46,8 @@ import org.eclipse.jetty.io.EndPoint;
|
|||
import org.eclipse.jetty.io.EofException;
|
||||
import org.eclipse.jetty.io.RuntimeIOException;
|
||||
import org.eclipse.jetty.io.UncheckedPrintWriter;
|
||||
import org.eclipse.jetty.server.nio.NIOConnector;
|
||||
import org.eclipse.jetty.server.ssl.SslConnector;
|
||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
|
@ -1162,10 +1164,11 @@ public class HttpConnection implements Connection
|
|||
if (super._generator.getContentWritten() > 0)
|
||||
throw new IllegalStateException("!empty");
|
||||
|
||||
// Convert HTTP content to contentl
|
||||
if (content instanceof HttpContent)
|
||||
{
|
||||
HttpContent c = (HttpContent) content;
|
||||
Buffer contentType = c.getContentType();
|
||||
HttpContent httpContent = (HttpContent) content;
|
||||
Buffer contentType = httpContent.getContentType();
|
||||
if (contentType != null && !_responseFields.containsKey(HttpHeaders.CONTENT_TYPE_BUFFER))
|
||||
{
|
||||
String enc = _response.getSetCharacterEncoding();
|
||||
|
@ -1191,21 +1194,22 @@ public class HttpConnection implements Connection
|
|||
}
|
||||
}
|
||||
}
|
||||
if (c.getContentLength() > 0)
|
||||
_responseFields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER, c.getContentLength());
|
||||
Buffer lm = c.getLastModified();
|
||||
long lml=c.getResource().lastModified();
|
||||
if (httpContent.getContentLength() > 0)
|
||||
_responseFields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER, httpContent.getContentLength());
|
||||
Buffer lm = httpContent.getLastModified();
|
||||
long lml=httpContent.getResource().lastModified();
|
||||
if (lm != null)
|
||||
_responseFields.put(HttpHeaders.LAST_MODIFIED_BUFFER, lm,lml);
|
||||
else if (c.getResource()!=null)
|
||||
else if (httpContent.getResource()!=null)
|
||||
{
|
||||
if (lml!=-1)
|
||||
_responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER, lml);
|
||||
}
|
||||
|
||||
content = c.getBuffer();
|
||||
boolean direct=_connector instanceof NIOConnector && ((NIOConnector)_connector).getUseDirectBuffers() && !(_connector instanceof SslConnector);
|
||||
content = direct?httpContent.getDirectBuffer():httpContent.getIndirectBuffer();
|
||||
if (content==null)
|
||||
content=c.getInputStream();
|
||||
content=httpContent.getInputStream();
|
||||
}
|
||||
else if (content instanceof Resource)
|
||||
{
|
||||
|
@ -1214,7 +1218,7 @@ public class HttpConnection implements Connection
|
|||
content=resource.getInputStream();
|
||||
}
|
||||
|
||||
|
||||
// Process content.
|
||||
if (content instanceof Buffer)
|
||||
{
|
||||
super._generator.addContent((Buffer) content, Generator.LAST);
|
||||
|
|
|
@ -13,11 +13,16 @@
|
|||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Comparator;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jetty.http.HttpContent;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
|
@ -25,7 +30,9 @@ import org.eclipse.jetty.http.MimeTypes;
|
|||
import org.eclipse.jetty.io.Buffer;
|
||||
import org.eclipse.jetty.io.ByteArrayBuffer;
|
||||
import org.eclipse.jetty.io.View;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.io.nio.DirectNIOBuffer;
|
||||
import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceFactory;
|
||||
|
||||
|
@ -34,38 +41,44 @@ import org.eclipse.jetty.util.resource.ResourceFactory;
|
|||
/**
|
||||
*
|
||||
*/
|
||||
public class ResourceCache extends AbstractLifeCycle
|
||||
public class ResourceCache
|
||||
{
|
||||
protected final Map _cache;
|
||||
private final ConcurrentMap<String,Content> _cache;
|
||||
private final AtomicInteger _cachedSize;
|
||||
private final AtomicInteger _cachedFiles;
|
||||
private final boolean _useFileMappedBuffer;
|
||||
private final ResourceFactory _factory;
|
||||
|
||||
private final MimeTypes _mimeTypes;
|
||||
private int _maxCachedFileSize =1024*1024;
|
||||
private int _maxCachedFiles=2048;
|
||||
private int _maxCacheSize =16*1024*1024;
|
||||
|
||||
protected int _cachedSize;
|
||||
protected int _cachedFiles;
|
||||
protected Content _mostRecentlyUsed;
|
||||
protected Content _leastRecentlyUsed;
|
||||
private int _maxCacheSize =32*1024*1024;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Constructor.
|
||||
* @param mimeTypes Mimetype to use for meta data
|
||||
* @param fileMappedBuffers True if file mapped buffers can be used for DirectBuffers
|
||||
*/
|
||||
public ResourceCache(MimeTypes mimeTypes)
|
||||
public ResourceCache(ResourceFactory factory, MimeTypes mimeTypes,boolean fileMappedBuffers)
|
||||
{
|
||||
_cache=new HashMap();
|
||||
_factory = factory;
|
||||
_cache=new ConcurrentHashMap<String,Content>();
|
||||
_cachedSize=new AtomicInteger();
|
||||
_cachedFiles=new AtomicInteger();
|
||||
_useFileMappedBuffer=fileMappedBuffers;
|
||||
_mimeTypes=mimeTypes;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public int getCachedSize()
|
||||
{
|
||||
return _cachedSize;
|
||||
return _cachedSize.get();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public int getCachedFiles()
|
||||
{
|
||||
return _cachedFiles;
|
||||
return _cachedFiles.get();
|
||||
}
|
||||
|
||||
|
||||
|
@ -79,7 +92,7 @@ public class ResourceCache extends AbstractLifeCycle
|
|||
public void setMaxCachedFileSize(int maxCachedFileSize)
|
||||
{
|
||||
_maxCachedFileSize = maxCachedFileSize;
|
||||
flushCache();
|
||||
shrinkCache();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -92,7 +105,7 @@ public class ResourceCache extends AbstractLifeCycle
|
|||
public void setMaxCacheSize(int maxCacheSize)
|
||||
{
|
||||
_maxCacheSize = maxCacheSize;
|
||||
flushCache();
|
||||
shrinkCache();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -111,6 +124,7 @@ public class ResourceCache extends AbstractLifeCycle
|
|||
public void setMaxCachedFiles(int maxCachedFiles)
|
||||
{
|
||||
_maxCachedFiles = maxCachedFiles;
|
||||
shrinkCache();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -118,18 +132,14 @@ public class ResourceCache extends AbstractLifeCycle
|
|||
{
|
||||
if (_cache!=null)
|
||||
{
|
||||
synchronized(this)
|
||||
while (_cache.size()>0)
|
||||
{
|
||||
ArrayList<Content> values=new ArrayList<Content>(_cache.values());
|
||||
for (Content content : values)
|
||||
content.invalidate();
|
||||
|
||||
_cache.clear();
|
||||
|
||||
_cachedSize=0;
|
||||
_cachedFiles=0;
|
||||
_mostRecentlyUsed=null;
|
||||
_leastRecentlyUsed=null;
|
||||
for (String path : _cache.keySet())
|
||||
{
|
||||
Content content = _cache.remove(path);
|
||||
if (content!=null)
|
||||
content.invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,49 +149,26 @@ public class ResourceCache extends AbstractLifeCycle
|
|||
* Get either a valid entry object or create a new one if possible.
|
||||
*
|
||||
* @param pathInContext The key into the cache
|
||||
* @param factory If no matching entry is found, this {@link ResourceFactory} will be used to create the {@link Resource}
|
||||
* for the new enry that is created.
|
||||
* @return The entry matching <code>pathInContext</code>, or a new entry if no matching entry was found
|
||||
* @throws IOException Problem loading the resource
|
||||
*/
|
||||
public Content lookup(String pathInContext, ResourceFactory factory)
|
||||
public Content lookup(String pathInContext)
|
||||
throws IOException
|
||||
{
|
||||
Content content=null;
|
||||
|
||||
|
||||
// Look up cache operations
|
||||
synchronized(_cache)
|
||||
{
|
||||
// Look for it in the cache
|
||||
content = (Content)_cache.get(pathInContext);
|
||||
content = _cache.get(pathInContext);
|
||||
|
||||
if (content!=null && content.isValid())
|
||||
{
|
||||
return content;
|
||||
}
|
||||
}
|
||||
Resource resource=factory.getResource(pathInContext);
|
||||
return load(pathInContext,resource);
|
||||
if (content!=null && content.isValid())
|
||||
return content;
|
||||
|
||||
Resource resource=_factory.getResource(pathInContext);
|
||||
Content loaded = load(pathInContext,resource);
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Content lookup(String pathInContext, Resource resource)
|
||||
throws IOException
|
||||
{
|
||||
Content content=null;
|
||||
|
||||
// Look up cache operations
|
||||
synchronized(_cache)
|
||||
{
|
||||
// Look for it in the cache
|
||||
content = (Content)_cache.get(pathInContext);
|
||||
|
||||
if (content!=null && content.isValid())
|
||||
{
|
||||
return content;
|
||||
}
|
||||
}
|
||||
return load(pathInContext,resource);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private Content load(String pathInContext, Resource resource)
|
||||
|
@ -191,115 +178,122 @@ public class ResourceCache extends AbstractLifeCycle
|
|||
if (resource!=null && resource.exists() && !resource.isDirectory())
|
||||
{
|
||||
long len = resource.length();
|
||||
|
||||
// Will it fit in the cache?
|
||||
if (len>0 && len<_maxCachedFileSize && len<_maxCacheSize)
|
||||
{
|
||||
int must_be_smaller_than=_maxCacheSize-(int)len;
|
||||
// Create the Content (to increment the cache sizes before adding the content
|
||||
content = new Content(pathInContext,resource);
|
||||
|
||||
synchronized(_cache)
|
||||
{
|
||||
// check the cache is not full of locked content before loading content
|
||||
|
||||
while(_leastRecentlyUsed!=null && (_cachedSize>must_be_smaller_than || (_maxCachedFiles>0 && _cachedFiles>=_maxCachedFiles)))
|
||||
_leastRecentlyUsed.invalidate();
|
||||
|
||||
if(_cachedSize>must_be_smaller_than || (_maxCachedFiles>0 && _cachedFiles>=_maxCachedFiles))
|
||||
return null;
|
||||
}
|
||||
// reduce the cache to an acceptable size.
|
||||
shrinkCache();
|
||||
|
||||
content = new Content(resource);
|
||||
fill(content);
|
||||
|
||||
synchronized(_cache)
|
||||
// Add it to the cache.
|
||||
Content added = _cache.putIfAbsent(pathInContext,content);
|
||||
if (added!=null)
|
||||
{
|
||||
// check that somebody else did not fill this spot.
|
||||
Content content2 =(Content)_cache.get(pathInContext);
|
||||
if (content2!=null)
|
||||
{
|
||||
content.release();
|
||||
return content2;
|
||||
}
|
||||
|
||||
while(_leastRecentlyUsed!=null && (_cachedSize>must_be_smaller_than || (_maxCachedFiles>0 && _cachedFiles>=_maxCachedFiles)))
|
||||
_leastRecentlyUsed.invalidate();
|
||||
|
||||
if(_cachedSize>must_be_smaller_than || (_maxCachedFiles>0 && _cachedFiles>=_maxCachedFiles))
|
||||
return null; // this could waste an allocated File or DirectBuffer
|
||||
|
||||
content.cache(pathInContext);
|
||||
|
||||
return content;
|
||||
content.invalidate();
|
||||
content=added;
|
||||
}
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private void shrinkCache()
|
||||
{
|
||||
// While we need to shrink
|
||||
while (_cache.size()>0 && (_cachedFiles.get()>_maxCachedFiles || _cachedSize.get()>_maxCacheSize))
|
||||
{
|
||||
// Scan the entire cache and generate an ordered list by last accessed time.
|
||||
SortedSet<Content> sorted= new TreeSet<Content>(
|
||||
new Comparator<Content>()
|
||||
{
|
||||
public int compare(Content c1, Content c2)
|
||||
{
|
||||
if (c1._lastAccessed<c2._lastAccessed)
|
||||
return -1;
|
||||
|
||||
if (c1._lastAccessed>c2._lastAccessed)
|
||||
return 1;
|
||||
|
||||
if (c1._length<c2._length)
|
||||
return -1;
|
||||
|
||||
return c1._key.compareTo(c2._key);
|
||||
}
|
||||
});
|
||||
for (Content content : _cache.values())
|
||||
sorted.add(content);
|
||||
|
||||
// Invalidate least recently used first
|
||||
for (Content content : sorted)
|
||||
{
|
||||
if (_cachedFiles.get()<=_maxCachedFiles && _cachedSize.get()<=_maxCacheSize)
|
||||
break;
|
||||
if (content==_cache.remove(content.getKey()))
|
||||
content.invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Remember a Resource Miss!
|
||||
* @param pathInContext
|
||||
* @param resource
|
||||
* @throws IOException
|
||||
* @param pathInContext Path the cache resource at
|
||||
* @param resource The resource to cache.
|
||||
*/
|
||||
public void miss(String pathInContext, Resource resource)
|
||||
throws IOException
|
||||
{
|
||||
synchronized(_cache)
|
||||
{
|
||||
while(_maxCachedFiles>0 && _cachedFiles>=_maxCachedFiles && _leastRecentlyUsed!=null)
|
||||
_leastRecentlyUsed.invalidate();
|
||||
if (_maxCachedFiles>0 && _cachedFiles>=_maxCachedFiles)
|
||||
return;
|
||||
|
||||
// check that somebody else did not fill this spot.
|
||||
Miss miss = new Miss(resource);
|
||||
Content content2 =(Content)_cache.get(pathInContext);
|
||||
if (content2!=null)
|
||||
{
|
||||
miss.release();
|
||||
return;
|
||||
}
|
||||
if (_maxCachedFiles>0 && _cachedFiles.get()>=_maxCachedFiles)
|
||||
return;
|
||||
|
||||
miss.cache(pathInContext);
|
||||
}
|
||||
// check that somebody else did not fill this spot.
|
||||
Miss miss = new Miss(pathInContext,resource);
|
||||
if (_cache.putIfAbsent(pathInContext,miss)!=null)
|
||||
miss.invalidate();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public synchronized void doStart()
|
||||
throws Exception
|
||||
{
|
||||
_cache.clear();
|
||||
_cachedSize=0;
|
||||
_cachedFiles=0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Stop the context.
|
||||
*/
|
||||
@Override
|
||||
public void doStop()
|
||||
throws InterruptedException
|
||||
{
|
||||
flushCache();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected void fill(Content content)
|
||||
throws IOException
|
||||
protected Buffer getIndirectBuffer(Resource resource)
|
||||
{
|
||||
try
|
||||
{
|
||||
InputStream in = content.getResource().getInputStream();
|
||||
int len=(int)content.getResource().length();
|
||||
Buffer buffer = new ByteArrayBuffer(len);
|
||||
buffer.readFrom(in,len);
|
||||
in.close();
|
||||
content.setBuffer(buffer);
|
||||
int len=(int)resource.length();
|
||||
Buffer buffer = new IndirectNIOBuffer(len);
|
||||
InputStream is = resource.getInputStream();
|
||||
buffer.readFrom(is,len);
|
||||
is.close();
|
||||
return buffer;
|
||||
}
|
||||
finally
|
||||
catch(IOException e)
|
||||
{
|
||||
content.getResource().release();
|
||||
Log.warn(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected Buffer getDirectBuffer(Resource resource)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_useFileMappedBuffer && resource.getFile()!=null)
|
||||
return new DirectNIOBuffer(resource.getFile());
|
||||
|
||||
int len=(int)resource.length();
|
||||
Buffer buffer = new DirectNIOBuffer(len);
|
||||
InputStream is = resource.getInputStream();
|
||||
buffer.readFrom(is,len);
|
||||
is.close();
|
||||
return buffer;
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
Log.warn(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -310,95 +304,34 @@ public class ResourceCache extends AbstractLifeCycle
|
|||
public class Content implements HttpContent
|
||||
{
|
||||
final Resource _resource;
|
||||
final int _length;
|
||||
final String _key;
|
||||
final long _lastModified;
|
||||
boolean _locked;
|
||||
String _key;
|
||||
Content _prev;
|
||||
Content _next;
|
||||
final Buffer _lastModifiedBytes;
|
||||
final Buffer _contentType;
|
||||
|
||||
Buffer _lastModifiedBytes;
|
||||
Buffer _contentType;
|
||||
Buffer _buffer;
|
||||
volatile long _lastAccessed;
|
||||
AtomicReference<Buffer> _indirectBuffer=new AtomicReference<Buffer>();
|
||||
AtomicReference<Buffer> _directBuffer=new AtomicReference<Buffer>();
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
Content(Resource resource)
|
||||
{
|
||||
_resource=resource;
|
||||
|
||||
_next=this;
|
||||
_prev=this;
|
||||
_contentType=_mimeTypes.getMimeByExtension(_resource.toString());
|
||||
|
||||
_lastModified=resource.lastModified();
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return true if the content is locked in the cache
|
||||
*/
|
||||
public boolean isLocked()
|
||||
{
|
||||
return _locked;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param locked true if the content is locked in the cache
|
||||
*/
|
||||
public void setLocked(boolean locked)
|
||||
{
|
||||
synchronized (_cache)
|
||||
{
|
||||
if (_locked && !locked)
|
||||
{
|
||||
_locked = locked;
|
||||
_next=_mostRecentlyUsed;
|
||||
_mostRecentlyUsed=this;
|
||||
if (_next!=null)
|
||||
_next._prev=this;
|
||||
_prev=null;
|
||||
if (_leastRecentlyUsed==null)
|
||||
_leastRecentlyUsed=this;
|
||||
}
|
||||
else if (!_locked && locked)
|
||||
{
|
||||
if (_prev!=null)
|
||||
_prev._next=_next;
|
||||
if (_next!=null)
|
||||
_next._prev=_prev;
|
||||
_next=_prev=null;
|
||||
}
|
||||
else
|
||||
_locked = locked;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
void cache(String pathInContext)
|
||||
Content(String pathInContext,Resource resource)
|
||||
{
|
||||
_key=pathInContext;
|
||||
_resource=resource;
|
||||
|
||||
_contentType=_mimeTypes.getMimeByExtension(_resource.toString());
|
||||
boolean exists=resource.exists();
|
||||
_lastModified=exists?resource.lastModified():-1;
|
||||
_lastModifiedBytes=_lastModified<0?null:new ByteArrayBuffer(HttpFields.formatDate(_lastModified));
|
||||
|
||||
if (!_locked)
|
||||
{
|
||||
_next=_mostRecentlyUsed;
|
||||
_mostRecentlyUsed=this;
|
||||
if (_next!=null)
|
||||
_next._prev=this;
|
||||
_prev=null;
|
||||
if (_leastRecentlyUsed==null)
|
||||
_leastRecentlyUsed=this;
|
||||
}
|
||||
_cache.put(_key,this);
|
||||
if (_buffer!=null)
|
||||
_cachedSize+=_buffer.length();
|
||||
_cachedFiles++;
|
||||
if (_lastModified!=-1)
|
||||
_lastModifiedBytes=new ByteArrayBuffer(HttpFields.formatDate(_lastModified));
|
||||
_length=exists?(int)resource.length():0;
|
||||
_cachedSize.addAndGet(_length);
|
||||
_cachedFiles.incrementAndGet();
|
||||
_lastAccessed=System.currentTimeMillis();
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public String getKey()
|
||||
{
|
||||
|
@ -422,59 +355,22 @@ public class ResourceCache extends AbstractLifeCycle
|
|||
{
|
||||
if (_lastModified==_resource.lastModified())
|
||||
{
|
||||
if (!_locked && _mostRecentlyUsed!=this)
|
||||
{
|
||||
Content tp = _prev;
|
||||
Content tn = _next;
|
||||
|
||||
_next=_mostRecentlyUsed;
|
||||
_mostRecentlyUsed=this;
|
||||
if (_next!=null)
|
||||
_next._prev=this;
|
||||
_prev=null;
|
||||
|
||||
if (tp!=null)
|
||||
tp._next=tn;
|
||||
if (tn!=null)
|
||||
tn._prev=tp;
|
||||
|
||||
if (_leastRecentlyUsed==this && tp!=null)
|
||||
_leastRecentlyUsed=tp;
|
||||
}
|
||||
_lastAccessed=System.currentTimeMillis();
|
||||
return true;
|
||||
}
|
||||
|
||||
invalidate();
|
||||
if (this==_cache.remove(_key))
|
||||
invalidate();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void invalidate()
|
||||
protected void invalidate()
|
||||
{
|
||||
synchronized(this)
|
||||
{
|
||||
// Invalidate it
|
||||
_cache.remove(_key);
|
||||
_key=null;
|
||||
if (_buffer!=null)
|
||||
_cachedSize=_cachedSize-_buffer.length();
|
||||
_cachedFiles--;
|
||||
|
||||
if (_mostRecentlyUsed==this)
|
||||
_mostRecentlyUsed=_next;
|
||||
else
|
||||
_prev._next=_next;
|
||||
|
||||
if (_leastRecentlyUsed==this)
|
||||
_leastRecentlyUsed=_prev;
|
||||
else
|
||||
_next._prev=_prev;
|
||||
|
||||
_prev=null;
|
||||
_next=null;
|
||||
_resource.release();
|
||||
|
||||
}
|
||||
// Invalidate it
|
||||
_cachedSize.addAndGet(-_length);
|
||||
_cachedFiles.decrementAndGet();
|
||||
_resource.release();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -489,46 +385,67 @@ public class ResourceCache extends AbstractLifeCycle
|
|||
return _contentType;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void setContentType(Buffer type)
|
||||
{
|
||||
_contentType=type;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void release()
|
||||
{
|
||||
// don't release while cached. Release when invalidated.
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Buffer getBuffer()
|
||||
public Buffer getIndirectBuffer()
|
||||
{
|
||||
if (_buffer==null)
|
||||
Buffer buffer = _indirectBuffer.get();
|
||||
if (buffer==null)
|
||||
{
|
||||
synchronized (_indirectBuffer)
|
||||
{
|
||||
if (_indirectBuffer.get()==null)
|
||||
{
|
||||
buffer=ResourceCache.this.getIndirectBuffer(_resource);
|
||||
_indirectBuffer.set(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (buffer==null)
|
||||
return null;
|
||||
return new View(_buffer);
|
||||
return new View(buffer);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Buffer getDirectBuffer()
|
||||
{
|
||||
Buffer buffer = _directBuffer.get();
|
||||
if (buffer==null)
|
||||
{
|
||||
synchronized (_directBuffer)
|
||||
{
|
||||
if (_directBuffer.get()==null)
|
||||
{
|
||||
buffer=ResourceCache.this.getDirectBuffer(_resource);
|
||||
_directBuffer.set(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (buffer==null)
|
||||
return null;
|
||||
|
||||
return new View(buffer);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void setBuffer(Buffer buffer)
|
||||
{
|
||||
_buffer=buffer;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public long getContentLength()
|
||||
{
|
||||
if (_buffer==null)
|
||||
{
|
||||
if (_resource!=null)
|
||||
return _resource.length();
|
||||
return -1;
|
||||
}
|
||||
return _buffer.length();
|
||||
return _length;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public InputStream getInputStream() throws IOException
|
||||
{
|
||||
Buffer indirect = getIndirectBuffer();
|
||||
if (indirect!=null && indirect.array()!=null)
|
||||
return new ByteArrayInputStream(indirect.array(),indirect.getIndex(),indirect.length());
|
||||
|
||||
return _resource.getInputStream();
|
||||
}
|
||||
|
||||
|
@ -549,9 +466,9 @@ public class ResourceCache extends AbstractLifeCycle
|
|||
*/
|
||||
public class Miss extends Content
|
||||
{
|
||||
Miss(Resource resource)
|
||||
Miss(String pathInContext,Resource resource)
|
||||
{
|
||||
super(resource);
|
||||
super(pathInContext,resource);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -560,7 +477,8 @@ public class ResourceCache extends AbstractLifeCycle
|
|||
{
|
||||
if (_resource.exists())
|
||||
{
|
||||
invalidate();
|
||||
if (this==_cache.remove(_key))
|
||||
invalidate();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -270,26 +270,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
|
|||
return _vhosts;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @deprecated use {@link #setConnectorNames(String[])}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setHosts(String[] hosts)
|
||||
{
|
||||
setConnectorNames(hosts);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get the hosts for the context.
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public String[] getHosts()
|
||||
{
|
||||
return getConnectorNames();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return an array of connector names that this context
|
||||
|
|
|
@ -32,7 +32,7 @@ public class ResourceCacheTest
|
|||
private Resource directory;
|
||||
private File[] files=new File[10];
|
||||
private String[] names=new String[files.length];
|
||||
private ResourceCache cache = new ResourceCache(new MimeTypes());
|
||||
private ResourceCache cache;
|
||||
private ResourceFactory factory;
|
||||
|
||||
@Before
|
||||
|
@ -40,7 +40,7 @@ public class ResourceCacheTest
|
|||
{
|
||||
for (int i=0;i<files.length;i++)
|
||||
{
|
||||
files[i]=File.createTempFile("RCT",".txt");
|
||||
files[i]=File.createTempFile("R-"+i+"-",".txt");
|
||||
files[i].deleteOnExit();
|
||||
names[i]=files[i].getName();
|
||||
FileOutputStream out = new FileOutputStream(files[i]);
|
||||
|
@ -67,83 +67,111 @@ public class ResourceCacheTest
|
|||
}
|
||||
|
||||
};
|
||||
|
||||
cache=new ResourceCache(factory,new MimeTypes(),false);
|
||||
|
||||
cache.setMaxCacheSize(95);
|
||||
cache.setMaxCachedFileSize(85);
|
||||
cache.setMaxCachedFiles(4);
|
||||
cache.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void destroy() throws Exception
|
||||
{
|
||||
cache.stop();
|
||||
cache.flushCache();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceCache() throws Exception
|
||||
{
|
||||
assertTrue(cache.lookup("does not exist",factory)==null);
|
||||
assertTrue(cache.lookup(names[9],factory)==null);
|
||||
assertTrue(cache.lookup("does not exist")==null);
|
||||
assertTrue(cache.lookup(names[9])==null);
|
||||
|
||||
Content content;
|
||||
content=cache.lookup(names[8],factory);
|
||||
content=cache.lookup(names[8]);
|
||||
assertTrue(content!=null);
|
||||
assertEquals(80,content.getContentLength());
|
||||
|
||||
assertEquals(80,cache.getCachedSize());
|
||||
assertEquals(1,cache.getCachedFiles());
|
||||
|
||||
content=cache.lookup(names[1],factory);
|
||||
Thread.sleep(2);
|
||||
|
||||
content=cache.lookup(names[1]);
|
||||
assertEquals(90,cache.getCachedSize());
|
||||
assertEquals(2,cache.getCachedFiles());
|
||||
|
||||
content=cache.lookup(names[2],factory);
|
||||
Thread.sleep(2);
|
||||
|
||||
content=cache.lookup(names[2]);
|
||||
assertEquals(30,cache.getCachedSize());
|
||||
assertEquals(2,cache.getCachedFiles());
|
||||
|
||||
content=cache.lookup(names[3],factory);
|
||||
Thread.sleep(2);
|
||||
|
||||
content=cache.lookup(names[3]);
|
||||
assertEquals(60,cache.getCachedSize());
|
||||
assertEquals(3,cache.getCachedFiles());
|
||||
|
||||
content=cache.lookup(names[4],factory);
|
||||
Thread.sleep(2);
|
||||
|
||||
content=cache.lookup(names[4]);
|
||||
assertEquals(90,cache.getCachedSize());
|
||||
assertEquals(3,cache.getCachedFiles());
|
||||
|
||||
content=cache.lookup(names[5],factory);
|
||||
Thread.sleep(2);
|
||||
|
||||
content=cache.lookup(names[5]);
|
||||
assertEquals(90,cache.getCachedSize());
|
||||
assertEquals(2,cache.getCachedFiles());
|
||||
|
||||
content=cache.lookup(names[6],factory);
|
||||
Thread.sleep(2);
|
||||
|
||||
content=cache.lookup(names[6]);
|
||||
assertEquals(60,cache.getCachedSize());
|
||||
assertEquals(1,cache.getCachedFiles());
|
||||
|
||||
Thread.sleep(2);
|
||||
|
||||
FileOutputStream out = new FileOutputStream(files[6]);
|
||||
out.write(' ');
|
||||
out.close();
|
||||
content=cache.lookup(names[7],factory);
|
||||
content=cache.lookup(names[7]);
|
||||
assertEquals(70,cache.getCachedSize());
|
||||
assertEquals(1,cache.getCachedFiles());
|
||||
|
||||
content=cache.lookup(names[6],factory);
|
||||
Thread.sleep(2);
|
||||
|
||||
content=cache.lookup(names[6]);
|
||||
assertEquals(71,cache.getCachedSize());
|
||||
assertEquals(2,cache.getCachedFiles());
|
||||
|
||||
content=cache.lookup(names[0],factory);
|
||||
Thread.sleep(2);
|
||||
|
||||
content=cache.lookup(names[0]);
|
||||
assertEquals(72,cache.getCachedSize());
|
||||
assertEquals(3,cache.getCachedFiles());
|
||||
|
||||
content=cache.lookup(names[1],factory);
|
||||
Thread.sleep(2);
|
||||
|
||||
content=cache.lookup(names[1]);
|
||||
assertEquals(82,cache.getCachedSize());
|
||||
assertEquals(4,cache.getCachedFiles());
|
||||
|
||||
content=cache.lookup(names[2],factory);
|
||||
Thread.sleep(2);
|
||||
|
||||
content=cache.lookup(names[2]);
|
||||
assertEquals(32,cache.getCachedSize());
|
||||
assertEquals(4,cache.getCachedFiles());
|
||||
|
||||
content=cache.lookup(names[3],factory);
|
||||
Thread.sleep(2);
|
||||
|
||||
content=cache.lookup(names[3]);
|
||||
assertEquals(61,cache.getCachedSize());
|
||||
assertEquals(4,cache.getCachedFiles());
|
||||
|
||||
Thread.sleep(2);
|
||||
|
||||
cache.flushCache();
|
||||
assertEquals(0,cache.getCachedSize());
|
||||
assertEquals(0,cache.getCachedFiles());
|
||||
|
|
|
@ -92,6 +92,9 @@ import org.eclipse.jetty.util.resource.ResourceFactory;
|
|||
*
|
||||
* resourceBase Set to replace the context resource base
|
||||
*
|
||||
* resourceCache If set, this is a context attribute name, which the servlet
|
||||
* will use to look for a shared ResourceCache instance.
|
||||
*
|
||||
* relativeResourceBase
|
||||
* Set with a pathname relative to the base of the
|
||||
* servlet context root. Useful for only serving static content out
|
||||
|
@ -103,9 +106,6 @@ import org.eclipse.jetty.util.resource.ResourceFactory;
|
|||
* maxCacheSize The maximum total size of the cache or 0 for no cache.
|
||||
* maxCachedFileSize The maximum size of a file to cache
|
||||
* maxCachedFiles The maximum number of files to cache
|
||||
* cacheType Set to "bio", "nio" or "both" to determine the type resource cache.
|
||||
* A bio cached buffer may be used by nio but is not as efficient as an
|
||||
* nio buffer. An nio cached buffer may not be used by bio.
|
||||
*
|
||||
* useFileMappedBuffer
|
||||
* If set to true, it will use mapped file buffer to serve static content
|
||||
|
@ -125,6 +125,7 @@ import org.eclipse.jetty.util.resource.ResourceFactory;
|
|||
*/
|
||||
public class DefaultServlet extends HttpServlet implements ResourceFactory
|
||||
{
|
||||
private static final long serialVersionUID = 4930458713846881193L;
|
||||
private ServletContext _servletContext;
|
||||
private ContextHandler _contextHandler;
|
||||
|
||||
|
@ -136,8 +137,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
private boolean _gzip=true;
|
||||
|
||||
private Resource _resourceBase;
|
||||
private NIOResourceCache _nioCache;
|
||||
private ResourceCache _bioCache;
|
||||
private ResourceCache _cache;
|
||||
|
||||
private MimeTypes _mimeTypes;
|
||||
private String[] _welcomes;
|
||||
|
@ -209,50 +209,42 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
if (t!=null)
|
||||
_cacheControl=new ByteArrayBuffer(t);
|
||||
|
||||
try
|
||||
String resourceCache = getInitParameter("resourceCache");
|
||||
int max_cache_size=getInitInt("maxCacheSize", -2);
|
||||
int max_cached_file_size=getInitInt("maxCachedFileSize", -2);
|
||||
int max_cached_files=getInitInt("maxCachedFiles", -2);
|
||||
if (resourceCache!=null)
|
||||
{
|
||||
String cache_type =getInitParameter("cacheType");
|
||||
int max_cache_size=getInitInt("maxCacheSize", -2);
|
||||
int max_cached_file_size=getInitInt("maxCachedFileSize", -2);
|
||||
int max_cached_files=getInitInt("maxCachedFiles", -2);
|
||||
|
||||
if (cache_type==null || "nio".equals(cache_type)|| "both".equals(cache_type))
|
||||
{
|
||||
if (max_cache_size==-2 || max_cache_size>0)
|
||||
{
|
||||
_nioCache=new NIOResourceCache(_mimeTypes);
|
||||
_nioCache.setUseFileMappedBuffer(_useFileMappedBuffer);
|
||||
if (max_cache_size>0)
|
||||
_nioCache.setMaxCacheSize(max_cache_size);
|
||||
if (max_cached_file_size>=-1)
|
||||
_nioCache.setMaxCachedFileSize(max_cached_file_size);
|
||||
if (max_cached_files>=-1)
|
||||
_nioCache.setMaxCachedFiles(max_cached_files);
|
||||
_nioCache.start();
|
||||
}
|
||||
}
|
||||
if ("bio".equals(cache_type)|| "both".equals(cache_type))
|
||||
{
|
||||
if (max_cache_size==-2 || max_cache_size>0)
|
||||
{
|
||||
_bioCache=new ResourceCache(_mimeTypes);
|
||||
if (max_cache_size>0)
|
||||
_bioCache.setMaxCacheSize(max_cache_size);
|
||||
if (max_cached_file_size>=-1)
|
||||
_bioCache.setMaxCachedFileSize(max_cached_file_size);
|
||||
if (max_cached_files>=-1)
|
||||
_bioCache.setMaxCachedFiles(max_cached_files);
|
||||
_bioCache.start();
|
||||
}
|
||||
}
|
||||
if (_nioCache==null)
|
||||
_bioCache=null;
|
||||
if (max_cache_size!=-1 || max_cached_file_size!= -2 || max_cached_files!=-2)
|
||||
_servletContext.log("ignoring resource cache configuration, using resourceCache attribute");
|
||||
if (_relativeResourceBase!=null || _resourceBase!=null)
|
||||
throw new UnavailableException("resourceCache specified with resource bases");
|
||||
_cache=(ResourceCache)_servletContext.getAttribute(resourceCache);
|
||||
|
||||
if (_cache==null)
|
||||
throw new UnavailableException("Could not find resourceCache "+resourceCache);
|
||||
}
|
||||
catch (Exception e)
|
||||
else
|
||||
{
|
||||
Log.warn(Log.EXCEPTION,e);
|
||||
throw new UnavailableException(e.toString());
|
||||
try
|
||||
{
|
||||
if (max_cached_files>0)
|
||||
{
|
||||
_cache= new ResourceCache(this,_mimeTypes,_useFileMappedBuffer);
|
||||
|
||||
if (max_cache_size>0)
|
||||
_cache.setMaxCacheSize(max_cache_size);
|
||||
if (max_cached_file_size>=-1)
|
||||
_cache.setMaxCachedFileSize(max_cached_file_size);
|
||||
if (max_cached_files>=-1)
|
||||
_cache.setMaxCachedFiles(max_cached_files);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.warn(Log.EXCEPTION,e);
|
||||
throw new UnavailableException(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
_servletHandler= (ServletHandler) _contextHandler.getChildHandlerByClass(ServletHandler.class);
|
||||
|
@ -332,13 +324,14 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
String servletPath=null;
|
||||
String pathInfo=null;
|
||||
Enumeration reqRanges = null;
|
||||
Enumeration<String> reqRanges = null;
|
||||
Boolean included =request.getAttribute(Dispatcher.INCLUDE_REQUEST_URI)!=null;
|
||||
if (included!=null && included.booleanValue())
|
||||
{
|
||||
|
@ -378,9 +371,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
// Find the resource and content
|
||||
Resource resource=null;
|
||||
HttpContent content=null;
|
||||
|
||||
Connector connector = HttpConnection.getCurrentConnection().getConnector();
|
||||
ResourceCache cache=(connector instanceof NIOConnector && !(connector instanceof SslConnector)) ?_nioCache:_bioCache;
|
||||
|
||||
try
|
||||
{
|
||||
// Try gzipped content first
|
||||
|
@ -388,27 +379,27 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
{
|
||||
pathInContextGz=pathInContext+".gz";
|
||||
|
||||
if (cache==null)
|
||||
if (_cache==null)
|
||||
{
|
||||
resource=getResource(pathInContextGz);
|
||||
}
|
||||
else
|
||||
{
|
||||
content=cache.lookup(pathInContextGz,this);
|
||||
content=_cache.lookup(pathInContextGz);
|
||||
|
||||
if (content!=null)
|
||||
resource=content.getResource();
|
||||
else
|
||||
else if (!(content instanceof ResourceCache.Miss))
|
||||
resource=getResource(pathInContextGz);
|
||||
}
|
||||
|
||||
if (resource==null || !resource.exists()|| resource.isDirectory())
|
||||
{
|
||||
if (cache!=null && content==null)
|
||||
if (_cache!=null && content==null)
|
||||
{
|
||||
String real_path=_servletContext.getRealPath(pathInContextGz);
|
||||
if (real_path!=null)
|
||||
cache.miss(pathInContextGz,_contextHandler.newResource(real_path));
|
||||
_cache.miss(pathInContextGz,_contextHandler.newResource(real_path));
|
||||
}
|
||||
gzip=false;
|
||||
pathInContextGz=null;
|
||||
|
@ -418,11 +409,11 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
// find resource
|
||||
if (!gzip)
|
||||
{
|
||||
if (cache==null)
|
||||
if (_cache==null)
|
||||
resource=getResource(pathInContext);
|
||||
else
|
||||
{
|
||||
content=cache.lookup(pathInContext,this);
|
||||
content=_cache.lookup(pathInContext);
|
||||
|
||||
if (content!=null)
|
||||
resource=content.getResource();
|
||||
|
@ -544,7 +535,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private boolean hasDefinedRange(Enumeration reqRanges)
|
||||
private boolean hasDefinedRange(Enumeration<String> reqRanges)
|
||||
{
|
||||
return (reqRanges!=null && reqRanges.hasMoreElements());
|
||||
}
|
||||
|
@ -723,8 +714,21 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
Enumeration reqRanges)
|
||||
throws IOException
|
||||
{
|
||||
long content_length=content==null?resource.length():content.getContentLength();
|
||||
|
||||
boolean direct;
|
||||
long content_length;
|
||||
if (content==null)
|
||||
{
|
||||
direct=false;
|
||||
content_length=resource.length();
|
||||
}
|
||||
else
|
||||
{
|
||||
Connector connector = HttpConnection.getCurrentConnection().getConnector();
|
||||
direct=connector instanceof NIOConnector && ((NIOConnector)connector).getUseDirectBuffers() && !(connector instanceof SslConnector);
|
||||
content_length=content.getContentLength();
|
||||
}
|
||||
|
||||
|
||||
// Get the output stream (or writer)
|
||||
OutputStream out =null;
|
||||
try{out = response.getOutputStream();}
|
||||
|
@ -740,29 +744,39 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
else
|
||||
{
|
||||
// See if a direct methods can be used?
|
||||
if (out instanceof HttpConnection.Output)
|
||||
if (out instanceof HttpConnection.Output && content!=null)
|
||||
{
|
||||
if (response instanceof Response)
|
||||
{
|
||||
writeOptionHeaders(((Response)response).getHttpFields());
|
||||
((HttpConnection.Output)out).sendContent(content);
|
||||
}
|
||||
else if (content.getBuffer()!=null)
|
||||
else
|
||||
{
|
||||
writeHeaders(response,content,content_length);
|
||||
((HttpConnection.Output)out).sendContent(content.getBuffer());
|
||||
}
|
||||
else
|
||||
{
|
||||
writeHeaders(response,content,content_length);
|
||||
resource.writeTo(out,0,content_length);
|
||||
Buffer buffer = direct?content.getDirectBuffer():content.getIndirectBuffer();
|
||||
if (buffer!=null)
|
||||
{
|
||||
writeHeaders(response,content,content_length);
|
||||
((HttpConnection.Output)out).sendContent(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeHeaders(response,content,content_length);
|
||||
resource.writeTo(out,0,content_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
// Write content normally
|
||||
// Write headers normally
|
||||
writeHeaders(response,content,content_length);
|
||||
resource.writeTo(out,0,content_length);
|
||||
|
||||
// Write content normally
|
||||
Buffer buffer = (content==null)?null:content.getIndirectBuffer();
|
||||
if (buffer!=null)
|
||||
buffer.writeTo(out);
|
||||
else
|
||||
resource.writeTo(out,0,content_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -944,31 +958,9 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_nioCache!=null)
|
||||
_nioCache.stop();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Log.warn(Log.EXCEPTION,e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_bioCache!=null)
|
||||
_bioCache.stop();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Log.warn(Log.EXCEPTION,e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
if (_cache!=null)
|
||||
_cache.flushCache();
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -996,7 +988,13 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Buffer getBuffer()
|
||||
public Buffer getDirectBuffer()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Buffer getIndirectBuffer()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.eclipse.jetty.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.io.Buffer;
|
||||
import org.eclipse.jetty.io.nio.DirectNIOBuffer;
|
||||
import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
|
||||
import org.eclipse.jetty.io.nio.NIOBuffer;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.HttpConnection;
|
||||
import org.eclipse.jetty.server.ResourceCache;
|
||||
import org.eclipse.jetty.server.nio.NIOConnector;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
|
||||
class NIOResourceCache extends ResourceCache
|
||||
{
|
||||
boolean _useFileMappedBuffer;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public NIOResourceCache(MimeTypes mimeTypes)
|
||||
{
|
||||
super(mimeTypes);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected void fill(Content content) throws IOException
|
||||
{
|
||||
Buffer buffer=null;
|
||||
Resource resource=content.getResource();
|
||||
long length=resource.length();
|
||||
|
||||
if (_useFileMappedBuffer && resource.getFile()!=null)
|
||||
{
|
||||
buffer = new DirectNIOBuffer(resource.getFile());
|
||||
}
|
||||
else
|
||||
{
|
||||
InputStream is = resource.getInputStream();
|
||||
try
|
||||
{
|
||||
Connector connector = HttpConnection.getCurrentConnection().getConnector();
|
||||
buffer = ((NIOConnector)connector).getUseDirectBuffers()?
|
||||
(NIOBuffer)new DirectNIOBuffer((int)length):
|
||||
(NIOBuffer)new IndirectNIOBuffer((int)length);
|
||||
}
|
||||
catch(OutOfMemoryError e)
|
||||
{
|
||||
Log.warn(e.toString());
|
||||
Log.debug(e);
|
||||
buffer = new IndirectNIOBuffer((int) length);
|
||||
}
|
||||
buffer.readFrom(is,(int)length);
|
||||
is.close();
|
||||
}
|
||||
content.setBuffer(buffer);
|
||||
}
|
||||
|
||||
public boolean isUseFileMappedBuffer()
|
||||
{
|
||||
return _useFileMappedBuffer;
|
||||
}
|
||||
|
||||
public void setUseFileMappedBuffer(boolean useFileMappedBuffer)
|
||||
{
|
||||
_useFileMappedBuffer = useFileMappedBuffer;
|
||||
}
|
||||
}
|
|
@ -595,25 +595,6 @@ public class ServletHandler extends ScopedHandler
|
|||
return chain;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return Returns the initializeAtStart.
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean isInitializeAtStart()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param initializeAtStart The initializeAtStart to set.
|
||||
* @deprecated
|
||||
*/
|
||||
public void setInitializeAtStart(boolean initializeAtStart)
|
||||
{
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return true if the handler is started and there are no unavailable servlets
|
||||
|
@ -719,7 +700,7 @@ public class ServletHandler extends ScopedHandler
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public ServletHolder newServletHolder(Class servlet)
|
||||
public ServletHolder newServletHolder(Class<? extends Servlet> servlet)
|
||||
{
|
||||
return new ServletHolder(servlet);
|
||||
}
|
||||
|
@ -782,18 +763,6 @@ public class ServletHandler extends ScopedHandler
|
|||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Convenience method to add a servlet with a servlet mapping.
|
||||
* @param className
|
||||
* @param pathSpec
|
||||
* @return the ServletHolder
|
||||
* @deprecated use {@link #addServletWithMapping(Class, String)} instead
|
||||
*/
|
||||
public ServletHolder addServlet (String className, String pathSpec)
|
||||
{
|
||||
return addServletWithMapping (className, pathSpec);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**Convenience method to add a pre-constructed ServletHolder.
|
||||
|
@ -901,19 +870,6 @@ public class ServletHandler extends ScopedHandler
|
|||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Convenience method to add a filter with a mapping
|
||||
* @param className
|
||||
* @param pathSpec
|
||||
* @param dispatches
|
||||
* @return the filter holder created
|
||||
* @deprecated use {@link #addFilterWithMapping(Class, String, int)} instead
|
||||
*/
|
||||
public FilterHolder addFilter (String className,String pathSpec,int dispatches)
|
||||
{
|
||||
return addFilterWithMapping(className, pathSpec, dispatches);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* convenience method to add a filter and mapping
|
||||
|
@ -1325,6 +1281,7 @@ public class ServletHandler extends ScopedHandler
|
|||
_maxFilterChainsCacheSize = maxFilterChainsCacheSize;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Customize a servlet.
|
||||
*
|
||||
|
@ -1342,14 +1299,14 @@ public class ServletHandler extends ScopedHandler
|
|||
return servlet;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Servlet customizeServletDestroy (Servlet servlet)
|
||||
throws Exception
|
||||
{
|
||||
return servlet;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Customize a Filter.
|
||||
*
|
||||
|
@ -1357,9 +1314,9 @@ public class ServletHandler extends ScopedHandler
|
|||
* Subclasses of ServletHandler should override
|
||||
* this method.
|
||||
*
|
||||
* @param filter
|
||||
* @param filter The filter to customize.
|
||||
* @return the potentially customized filter
|
||||
* @throws Exception
|
||||
* @throws Exception If there was a problem
|
||||
*/
|
||||
public Filter customizeFilter (Filter filter)
|
||||
throws Exception
|
||||
|
@ -1367,7 +1324,7 @@ public class ServletHandler extends ScopedHandler
|
|||
return filter;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Filter customizeFilterDestroy (Filter filter)
|
||||
throws Exception
|
||||
{
|
||||
|
@ -1375,8 +1332,8 @@ public class ServletHandler extends ScopedHandler
|
|||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected void dump(StringBuilder b,String indent)
|
||||
{
|
||||
super.dump(b,indent);
|
||||
|
@ -1423,6 +1380,4 @@ public class ServletHandler extends ScopedHandler
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -27,16 +27,14 @@ import org.eclipse.jetty.util.log.Log;
|
|||
/* ------------------------------------------------------------ */
|
||||
class JarFileResource extends JarResource
|
||||
{
|
||||
|
||||
transient JarFile _jarFile;
|
||||
transient File _file;
|
||||
transient String[] _list;
|
||||
transient JarEntry _entry;
|
||||
transient boolean _directory;
|
||||
transient String _jarUrl;
|
||||
transient String _path;
|
||||
transient boolean _exists;
|
||||
|
||||
private JarFile _jarFile;
|
||||
private File _file;
|
||||
private String[] _list;
|
||||
private JarEntry _entry;
|
||||
private boolean _directory;
|
||||
private String _jarUrl;
|
||||
private String _path;
|
||||
private boolean _exists;
|
||||
|
||||
/* -------------------------------------------------------- */
|
||||
JarFileResource(URL url)
|
||||
|
@ -44,6 +42,7 @@ class JarFileResource extends JarResource
|
|||
super(url);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
JarFileResource(URL url, boolean useCaches)
|
||||
{
|
||||
super(url, useCaches);
|
||||
|
@ -65,7 +64,8 @@ class JarFileResource extends JarResource
|
|||
@Override
|
||||
protected boolean checkConnection()
|
||||
{
|
||||
try{
|
||||
try
|
||||
{
|
||||
super.checkConnection();
|
||||
}
|
||||
finally
|
||||
|
@ -84,7 +84,7 @@ class JarFileResource extends JarResource
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected void newConnection()
|
||||
protected synchronized void newConnection()
|
||||
throws IOException
|
||||
{
|
||||
super.newConnection();
|
||||
|
|
|
@ -31,8 +31,7 @@ import org.eclipse.jetty.util.log.Log;
|
|||
/* ------------------------------------------------------------ */
|
||||
public class JarResource extends URLResource
|
||||
{
|
||||
|
||||
protected transient JarURLConnection _jarConnection;
|
||||
protected JarURLConnection _jarConnection;
|
||||
|
||||
/* -------------------------------------------------------- */
|
||||
JarResource(URL url)
|
||||
|
@ -56,7 +55,7 @@ public class JarResource extends URLResource
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected boolean checkConnection()
|
||||
protected synchronized boolean checkConnection()
|
||||
{
|
||||
super.checkConnection();
|
||||
try
|
||||
|
@ -120,16 +119,6 @@ public class JarResource extends URLResource
|
|||
InputStream is = url.openStream();
|
||||
return is;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Deprecated
|
||||
public void extract(File dest, boolean deleteOnExit)
|
||||
throws IOException
|
||||
{
|
||||
if (deleteOnExit)
|
||||
dest.deleteOnExit();
|
||||
copyTo(dest);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
|
|
|
@ -37,10 +37,10 @@ import org.eclipse.jetty.util.log.Log;
|
|||
/**
|
||||
* Abstract resource class.
|
||||
*/
|
||||
public abstract class Resource implements Serializable
|
||||
public abstract class Resource
|
||||
{
|
||||
public static boolean __defaultUseCaches = true;
|
||||
Object _associate;
|
||||
volatile Object _associate;
|
||||
|
||||
/**
|
||||
* Change the default setting for url connection caches.
|
||||
|
@ -51,7 +51,8 @@ public abstract class Resource implements Serializable
|
|||
{
|
||||
__defaultUseCaches=useCaches;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static boolean getDefaultUseCaches ()
|
||||
{
|
||||
return __defaultUseCaches;
|
||||
|
@ -61,6 +62,7 @@ public abstract class Resource implements Serializable
|
|||
/** Construct a resource from a uri.
|
||||
* @param uri A URI.
|
||||
* @return A Resource object.
|
||||
* @throws IOException Problem accessing URI
|
||||
*/
|
||||
public static Resource newResource(URI uri)
|
||||
throws IOException
|
||||
|
@ -72,6 +74,7 @@ public abstract class Resource implements Serializable
|
|||
/** Construct a resource from a url.
|
||||
* @param url A URL.
|
||||
* @return A Resource object.
|
||||
* @throws IOException Problem accessing URL
|
||||
*/
|
||||
public static Resource newResource(URL url)
|
||||
throws IOException
|
||||
|
@ -199,6 +202,9 @@ public abstract class Resource implements Serializable
|
|||
/** Construct a system resource from a string.
|
||||
* The resource is tried as classloader resource before being
|
||||
* treated as a normal resource.
|
||||
* @param resource Resource as string representation
|
||||
* @return The new Resource
|
||||
* @throws IOException Problem accessing resource.
|
||||
*/
|
||||
public static Resource newSystemResource(String resource)
|
||||
throws IOException
|
||||
|
@ -287,6 +293,7 @@ public abstract class Resource implements Serializable
|
|||
return newResource(url,useCaches);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static boolean isContainedIn (Resource r, Resource containingResource) throws MalformedURLException
|
||||
{
|
||||
return r.isContainedIn(containingResource);
|
||||
|
@ -298,12 +305,13 @@ public abstract class Resource implements Serializable
|
|||
{
|
||||
release();
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public abstract boolean isContainedIn (Resource r) throws MalformedURLException;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Release any resources held by the resource.
|
||||
/** Release any temporary resources held by the resource.
|
||||
*/
|
||||
public abstract void release();
|
||||
|
||||
|
|
|
@ -32,12 +32,11 @@ import org.eclipse.jetty.util.log.Log;
|
|||
*/
|
||||
public class URLResource extends Resource
|
||||
{
|
||||
|
||||
|
||||
protected URL _url;
|
||||
protected final URL _url;
|
||||
protected String _urlString;
|
||||
protected transient URLConnection _connection;
|
||||
protected transient InputStream _in=null;
|
||||
|
||||
protected URLConnection _connection;
|
||||
protected InputStream _in=null;
|
||||
transient boolean _useCaches = Resource.__defaultUseCaches;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -48,6 +47,7 @@ public class URLResource extends Resource
|
|||
_connection=connection;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected URLResource (URL url, URLConnection connection, boolean useCaches)
|
||||
{
|
||||
this (url, connection);
|
||||
|
|
|
@ -125,7 +125,21 @@ public class QueuedThreadPool extends AbstractLifeCycle implements ThreadPool, E
|
|||
Thread.yield();
|
||||
int size=_threads.size();
|
||||
if (size>0)
|
||||
{
|
||||
Log.warn(size+" threads could not be stopped");
|
||||
|
||||
if (Log.isDebugEnabled())
|
||||
{
|
||||
for (Thread unstopped : _threads)
|
||||
{
|
||||
Log.debug("Couldn't stop "+unstopped);
|
||||
for (StackTraceElement element : unstopped.getStackTrace())
|
||||
{
|
||||
Log.debug(" at "+element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (_joinLock)
|
||||
{
|
||||
|
|
|
@ -93,7 +93,7 @@
|
|||
</filter>
|
||||
<filter-mapping>
|
||||
<filter-name>GzipFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
<url-pattern>/dump/gzip/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.eclipse.jetty.server.Connector;
|
|||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.NCSARequestLog;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.bio.SocketConnector;
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
|
@ -37,7 +38,7 @@ public class TestServer
|
|||
{
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
Log.getLog().setDebugEnabled(true);
|
||||
Log.getLog().setDebugEnabled(false);
|
||||
((StdErrLog)Log.getLog()).setSource(false);
|
||||
|
||||
String jetty_root = "..";
|
||||
|
@ -50,7 +51,6 @@ public class TestServer
|
|||
server.getContainer().addEventListener(mbContainer);
|
||||
server.addBean(mbContainer);
|
||||
mbContainer.addBean(Log.getLog());
|
||||
|
||||
|
||||
// Setup Threadpool
|
||||
QueuedThreadPool threadPool = new QueuedThreadPool();
|
||||
|
@ -58,12 +58,27 @@ public class TestServer
|
|||
server.setThreadPool(threadPool);
|
||||
|
||||
// Setup Connectors
|
||||
SelectChannelConnector connector = new SelectChannelConnector();
|
||||
connector.setPort(8080);
|
||||
connector.setMaxIdleTime(30000);
|
||||
connector.setConfidentialPort(8443);
|
||||
server.setConnectors(new Connector[]
|
||||
{ connector });
|
||||
SelectChannelConnector connector0 = new SelectChannelConnector();
|
||||
connector0.setPort(8080);
|
||||
connector0.setMaxIdleTime(30000);
|
||||
connector0.setConfidentialPort(8443);
|
||||
connector0.setUseDirectBuffers(true);
|
||||
server.addConnector(connector0);
|
||||
|
||||
// Setup Connectors
|
||||
SelectChannelConnector connector1 = new SelectChannelConnector();
|
||||
connector1.setPort(8081);
|
||||
connector1.setMaxIdleTime(30000);
|
||||
connector1.setConfidentialPort(8443);
|
||||
connector1.setUseDirectBuffers(false);
|
||||
server.addConnector(connector1);
|
||||
|
||||
// Setup Connectors
|
||||
SocketConnector connector2 = new SocketConnector();
|
||||
connector2.setPort(8082);
|
||||
connector2.setMaxIdleTime(30000);
|
||||
connector2.setConfidentialPort(8443);
|
||||
server.addConnector(connector2);
|
||||
|
||||
SslSelectChannelConnector ssl_connector = new SslSelectChannelConnector();
|
||||
ssl_connector.setPort(8443);
|
||||
|
|
Loading…
Reference in New Issue