103928aee4356845252ac6b662d5c72c29903813eJake Slack//
203928aee4356845252ac6b662d5c72c29903813eJake Slack//  ========================================================================
303928aee4356845252ac6b662d5c72c29903813eJake Slack//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
403928aee4356845252ac6b662d5c72c29903813eJake Slack//  ------------------------------------------------------------------------
503928aee4356845252ac6b662d5c72c29903813eJake Slack//  All rights reserved. This program and the accompanying materials
603928aee4356845252ac6b662d5c72c29903813eJake Slack//  are made available under the terms of the Eclipse Public License v1.0
703928aee4356845252ac6b662d5c72c29903813eJake Slack//  and Apache License v2.0 which accompanies this distribution.
803928aee4356845252ac6b662d5c72c29903813eJake Slack//
903928aee4356845252ac6b662d5c72c29903813eJake Slack//      The Eclipse Public License is available at
1003928aee4356845252ac6b662d5c72c29903813eJake Slack//      http://www.eclipse.org/legal/epl-v10.html
1103928aee4356845252ac6b662d5c72c29903813eJake Slack//
1203928aee4356845252ac6b662d5c72c29903813eJake Slack//      The Apache License v2.0 is available at
1303928aee4356845252ac6b662d5c72c29903813eJake Slack//      http://www.opensource.org/licenses/apache2.0.php
1403928aee4356845252ac6b662d5c72c29903813eJake Slack//
1503928aee4356845252ac6b662d5c72c29903813eJake Slack//  You may elect to redistribute this code under either of these licenses.
1603928aee4356845252ac6b662d5c72c29903813eJake Slack//  ========================================================================
1703928aee4356845252ac6b662d5c72c29903813eJake Slack//
1803928aee4356845252ac6b662d5c72c29903813eJake Slack
1903928aee4356845252ac6b662d5c72c29903813eJake Slackpackage org.eclipse.jetty.server;
2003928aee4356845252ac6b662d5c72c29903813eJake Slack
2103928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.ByteArrayInputStream;
2203928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.IOException;
2303928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.InputStream;
2403928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Comparator;
2503928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.SortedSet;
2603928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.TreeSet;
2703928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.concurrent.ConcurrentHashMap;
2803928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.concurrent.ConcurrentMap;
2903928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.concurrent.atomic.AtomicInteger;
3003928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.concurrent.atomic.AtomicReference;
3103928aee4356845252ac6b662d5c72c29903813eJake Slack
3203928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.http.HttpContent;
3303928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.http.HttpContent.ResourceAsHttpContent;
3403928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.http.HttpFields;
3503928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.http.MimeTypes;
3603928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.Buffer;
3703928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.ByteArrayBuffer;
3803928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.View;
3903928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.nio.DirectNIOBuffer;
4003928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.nio.IndirectNIOBuffer;
4103928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.log.Log;
4203928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.log.Logger;
4303928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.resource.Resource;
4403928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.resource.ResourceFactory;
4503928aee4356845252ac6b662d5c72c29903813eJake Slack
4603928aee4356845252ac6b662d5c72c29903813eJake Slack
4703928aee4356845252ac6b662d5c72c29903813eJake Slack/* ------------------------------------------------------------ */
4803928aee4356845252ac6b662d5c72c29903813eJake Slack/**
4903928aee4356845252ac6b662d5c72c29903813eJake Slack *
5003928aee4356845252ac6b662d5c72c29903813eJake Slack */
5103928aee4356845252ac6b662d5c72c29903813eJake Slackpublic class ResourceCache
5203928aee4356845252ac6b662d5c72c29903813eJake Slack{
5303928aee4356845252ac6b662d5c72c29903813eJake Slack    private static final Logger LOG = Log.getLogger(ResourceCache.class);
5403928aee4356845252ac6b662d5c72c29903813eJake Slack
5503928aee4356845252ac6b662d5c72c29903813eJake Slack    private final ConcurrentMap<String,Content> _cache;
5603928aee4356845252ac6b662d5c72c29903813eJake Slack    private final AtomicInteger _cachedSize;
5703928aee4356845252ac6b662d5c72c29903813eJake Slack    private final AtomicInteger _cachedFiles;
5803928aee4356845252ac6b662d5c72c29903813eJake Slack    private final ResourceFactory _factory;
5903928aee4356845252ac6b662d5c72c29903813eJake Slack    private final ResourceCache _parent;
6003928aee4356845252ac6b662d5c72c29903813eJake Slack    private final MimeTypes _mimeTypes;
6103928aee4356845252ac6b662d5c72c29903813eJake Slack    private final boolean _etags;
6203928aee4356845252ac6b662d5c72c29903813eJake Slack
6303928aee4356845252ac6b662d5c72c29903813eJake Slack    private boolean  _useFileMappedBuffer=true;
6403928aee4356845252ac6b662d5c72c29903813eJake Slack    private int _maxCachedFileSize =4*1024*1024;
6503928aee4356845252ac6b662d5c72c29903813eJake Slack    private int _maxCachedFiles=2048;
6603928aee4356845252ac6b662d5c72c29903813eJake Slack    private int _maxCacheSize =32*1024*1024;
6703928aee4356845252ac6b662d5c72c29903813eJake Slack
6803928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
6903928aee4356845252ac6b662d5c72c29903813eJake Slack    /** Constructor.
7003928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param mimeTypes Mimetype to use for meta data
7103928aee4356845252ac6b662d5c72c29903813eJake Slack     */
7203928aee4356845252ac6b662d5c72c29903813eJake Slack    public ResourceCache(ResourceCache parent, ResourceFactory factory, MimeTypes mimeTypes,boolean useFileMappedBuffer,boolean etags)
7303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
7403928aee4356845252ac6b662d5c72c29903813eJake Slack        _factory = factory;
7503928aee4356845252ac6b662d5c72c29903813eJake Slack        _cache=new ConcurrentHashMap<String,Content>();
7603928aee4356845252ac6b662d5c72c29903813eJake Slack        _cachedSize=new AtomicInteger();
7703928aee4356845252ac6b662d5c72c29903813eJake Slack        _cachedFiles=new AtomicInteger();
7803928aee4356845252ac6b662d5c72c29903813eJake Slack        _mimeTypes=mimeTypes;
7903928aee4356845252ac6b662d5c72c29903813eJake Slack        _parent=parent;
8003928aee4356845252ac6b662d5c72c29903813eJake Slack        _etags=etags;
8103928aee4356845252ac6b662d5c72c29903813eJake Slack        _useFileMappedBuffer=useFileMappedBuffer;
8203928aee4356845252ac6b662d5c72c29903813eJake Slack    }
8303928aee4356845252ac6b662d5c72c29903813eJake Slack
8403928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
8503928aee4356845252ac6b662d5c72c29903813eJake Slack    public int getCachedSize()
8603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
8703928aee4356845252ac6b662d5c72c29903813eJake Slack        return _cachedSize.get();
8803928aee4356845252ac6b662d5c72c29903813eJake Slack    }
8903928aee4356845252ac6b662d5c72c29903813eJake Slack
9003928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
9103928aee4356845252ac6b662d5c72c29903813eJake Slack    public int getCachedFiles()
9203928aee4356845252ac6b662d5c72c29903813eJake Slack    {
9303928aee4356845252ac6b662d5c72c29903813eJake Slack        return _cachedFiles.get();
9403928aee4356845252ac6b662d5c72c29903813eJake Slack    }
9503928aee4356845252ac6b662d5c72c29903813eJake Slack
9603928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
9703928aee4356845252ac6b662d5c72c29903813eJake Slack    public int getMaxCachedFileSize()
9803928aee4356845252ac6b662d5c72c29903813eJake Slack    {
9903928aee4356845252ac6b662d5c72c29903813eJake Slack        return _maxCachedFileSize;
10003928aee4356845252ac6b662d5c72c29903813eJake Slack    }
10103928aee4356845252ac6b662d5c72c29903813eJake Slack
10203928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
10303928aee4356845252ac6b662d5c72c29903813eJake Slack    public void setMaxCachedFileSize(int maxCachedFileSize)
10403928aee4356845252ac6b662d5c72c29903813eJake Slack    {
10503928aee4356845252ac6b662d5c72c29903813eJake Slack        _maxCachedFileSize = maxCachedFileSize;
10603928aee4356845252ac6b662d5c72c29903813eJake Slack        shrinkCache();
10703928aee4356845252ac6b662d5c72c29903813eJake Slack    }
10803928aee4356845252ac6b662d5c72c29903813eJake Slack
10903928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
11003928aee4356845252ac6b662d5c72c29903813eJake Slack    public int getMaxCacheSize()
11103928aee4356845252ac6b662d5c72c29903813eJake Slack    {
11203928aee4356845252ac6b662d5c72c29903813eJake Slack        return _maxCacheSize;
11303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
11403928aee4356845252ac6b662d5c72c29903813eJake Slack
11503928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
11603928aee4356845252ac6b662d5c72c29903813eJake Slack    public void setMaxCacheSize(int maxCacheSize)
11703928aee4356845252ac6b662d5c72c29903813eJake Slack    {
11803928aee4356845252ac6b662d5c72c29903813eJake Slack        _maxCacheSize = maxCacheSize;
11903928aee4356845252ac6b662d5c72c29903813eJake Slack        shrinkCache();
12003928aee4356845252ac6b662d5c72c29903813eJake Slack    }
12103928aee4356845252ac6b662d5c72c29903813eJake Slack
12203928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
12303928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
12403928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return Returns the maxCachedFiles.
12503928aee4356845252ac6b662d5c72c29903813eJake Slack     */
12603928aee4356845252ac6b662d5c72c29903813eJake Slack    public int getMaxCachedFiles()
12703928aee4356845252ac6b662d5c72c29903813eJake Slack    {
12803928aee4356845252ac6b662d5c72c29903813eJake Slack        return _maxCachedFiles;
12903928aee4356845252ac6b662d5c72c29903813eJake Slack    }
13003928aee4356845252ac6b662d5c72c29903813eJake Slack
13103928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
13203928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
13303928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param maxCachedFiles The maxCachedFiles to set.
13403928aee4356845252ac6b662d5c72c29903813eJake Slack     */
13503928aee4356845252ac6b662d5c72c29903813eJake Slack    public void setMaxCachedFiles(int maxCachedFiles)
13603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
13703928aee4356845252ac6b662d5c72c29903813eJake Slack        _maxCachedFiles = maxCachedFiles;
13803928aee4356845252ac6b662d5c72c29903813eJake Slack        shrinkCache();
13903928aee4356845252ac6b662d5c72c29903813eJake Slack    }
14003928aee4356845252ac6b662d5c72c29903813eJake Slack
14103928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
14203928aee4356845252ac6b662d5c72c29903813eJake Slack    public boolean isUseFileMappedBuffer()
14303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
14403928aee4356845252ac6b662d5c72c29903813eJake Slack        return _useFileMappedBuffer;
14503928aee4356845252ac6b662d5c72c29903813eJake Slack    }
14603928aee4356845252ac6b662d5c72c29903813eJake Slack
14703928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
14803928aee4356845252ac6b662d5c72c29903813eJake Slack    public void setUseFileMappedBuffer(boolean useFileMappedBuffer)
14903928aee4356845252ac6b662d5c72c29903813eJake Slack    {
15003928aee4356845252ac6b662d5c72c29903813eJake Slack        _useFileMappedBuffer = useFileMappedBuffer;
15103928aee4356845252ac6b662d5c72c29903813eJake Slack    }
15203928aee4356845252ac6b662d5c72c29903813eJake Slack
15303928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
15403928aee4356845252ac6b662d5c72c29903813eJake Slack    public void flushCache()
15503928aee4356845252ac6b662d5c72c29903813eJake Slack    {
15603928aee4356845252ac6b662d5c72c29903813eJake Slack        if (_cache!=null)
15703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
15803928aee4356845252ac6b662d5c72c29903813eJake Slack            while (_cache.size()>0)
15903928aee4356845252ac6b662d5c72c29903813eJake Slack            {
16003928aee4356845252ac6b662d5c72c29903813eJake Slack                for (String path : _cache.keySet())
16103928aee4356845252ac6b662d5c72c29903813eJake Slack                {
16203928aee4356845252ac6b662d5c72c29903813eJake Slack                    Content content = _cache.remove(path);
16303928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (content!=null)
16403928aee4356845252ac6b662d5c72c29903813eJake Slack                        content.invalidate();
16503928aee4356845252ac6b662d5c72c29903813eJake Slack                }
16603928aee4356845252ac6b662d5c72c29903813eJake Slack            }
16703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
16803928aee4356845252ac6b662d5c72c29903813eJake Slack    }
16903928aee4356845252ac6b662d5c72c29903813eJake Slack
17003928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
17103928aee4356845252ac6b662d5c72c29903813eJake Slack    /** Get a Entry from the cache.
17203928aee4356845252ac6b662d5c72c29903813eJake Slack     * Get either a valid entry object or create a new one if possible.
17303928aee4356845252ac6b662d5c72c29903813eJake Slack     *
17403928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param pathInContext The key into the cache
17503928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return The entry matching <code>pathInContext</code>, or a new entry
17603928aee4356845252ac6b662d5c72c29903813eJake Slack     * if no matching entry was found. If the content exists but is not cachable,
17703928aee4356845252ac6b662d5c72c29903813eJake Slack     * then a {@link ResourceAsHttpContent} instance is return. If
17803928aee4356845252ac6b662d5c72c29903813eJake Slack     * the resource does not exist, then null is returned.
17903928aee4356845252ac6b662d5c72c29903813eJake Slack     * @throws IOException Problem loading the resource
18003928aee4356845252ac6b662d5c72c29903813eJake Slack     */
18103928aee4356845252ac6b662d5c72c29903813eJake Slack    public HttpContent lookup(String pathInContext)
18203928aee4356845252ac6b662d5c72c29903813eJake Slack        throws IOException
18303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
18403928aee4356845252ac6b662d5c72c29903813eJake Slack        // Is the content in this cache?
18503928aee4356845252ac6b662d5c72c29903813eJake Slack        Content content =_cache.get(pathInContext);
18603928aee4356845252ac6b662d5c72c29903813eJake Slack        if (content!=null && (content).isValid())
18703928aee4356845252ac6b662d5c72c29903813eJake Slack            return content;
18803928aee4356845252ac6b662d5c72c29903813eJake Slack
18903928aee4356845252ac6b662d5c72c29903813eJake Slack        // try loading the content from our factory.
19003928aee4356845252ac6b662d5c72c29903813eJake Slack        Resource resource=_factory.getResource(pathInContext);
19103928aee4356845252ac6b662d5c72c29903813eJake Slack        HttpContent loaded = load(pathInContext,resource);
19203928aee4356845252ac6b662d5c72c29903813eJake Slack        if (loaded!=null)
19303928aee4356845252ac6b662d5c72c29903813eJake Slack            return loaded;
19403928aee4356845252ac6b662d5c72c29903813eJake Slack
19503928aee4356845252ac6b662d5c72c29903813eJake Slack        // Is the content in the parent cache?
19603928aee4356845252ac6b662d5c72c29903813eJake Slack        if (_parent!=null)
19703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
19803928aee4356845252ac6b662d5c72c29903813eJake Slack            HttpContent httpContent=_parent.lookup(pathInContext);
19903928aee4356845252ac6b662d5c72c29903813eJake Slack            if (httpContent!=null)
20003928aee4356845252ac6b662d5c72c29903813eJake Slack                return httpContent;
20103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
20203928aee4356845252ac6b662d5c72c29903813eJake Slack
20303928aee4356845252ac6b662d5c72c29903813eJake Slack        return null;
20403928aee4356845252ac6b662d5c72c29903813eJake Slack    }
20503928aee4356845252ac6b662d5c72c29903813eJake Slack
20603928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
20703928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
20803928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param resource
20903928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return True if the resource is cacheable. The default implementation tests the cache sizes.
21003928aee4356845252ac6b662d5c72c29903813eJake Slack     */
21103928aee4356845252ac6b662d5c72c29903813eJake Slack    protected boolean isCacheable(Resource resource)
21203928aee4356845252ac6b662d5c72c29903813eJake Slack    {
21303928aee4356845252ac6b662d5c72c29903813eJake Slack        long len = resource.length();
21403928aee4356845252ac6b662d5c72c29903813eJake Slack
21503928aee4356845252ac6b662d5c72c29903813eJake Slack        // Will it fit in the cache?
21603928aee4356845252ac6b662d5c72c29903813eJake Slack        return  (len>0 && len<_maxCachedFileSize && len<_maxCacheSize);
21703928aee4356845252ac6b662d5c72c29903813eJake Slack    }
21803928aee4356845252ac6b662d5c72c29903813eJake Slack
21903928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
22003928aee4356845252ac6b662d5c72c29903813eJake Slack    private HttpContent load(String pathInContext, Resource resource)
22103928aee4356845252ac6b662d5c72c29903813eJake Slack        throws IOException
22203928aee4356845252ac6b662d5c72c29903813eJake Slack    {
22303928aee4356845252ac6b662d5c72c29903813eJake Slack        Content content=null;
22403928aee4356845252ac6b662d5c72c29903813eJake Slack
22503928aee4356845252ac6b662d5c72c29903813eJake Slack        if (resource==null || !resource.exists())
22603928aee4356845252ac6b662d5c72c29903813eJake Slack            return null;
22703928aee4356845252ac6b662d5c72c29903813eJake Slack
22803928aee4356845252ac6b662d5c72c29903813eJake Slack        // Will it fit in the cache?
22903928aee4356845252ac6b662d5c72c29903813eJake Slack        if (!resource.isDirectory() && isCacheable(resource))
23003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
23103928aee4356845252ac6b662d5c72c29903813eJake Slack            // Create the Content (to increment the cache sizes before adding the content
23203928aee4356845252ac6b662d5c72c29903813eJake Slack            content = new Content(pathInContext,resource);
23303928aee4356845252ac6b662d5c72c29903813eJake Slack
23403928aee4356845252ac6b662d5c72c29903813eJake Slack            // reduce the cache to an acceptable size.
23503928aee4356845252ac6b662d5c72c29903813eJake Slack            shrinkCache();
23603928aee4356845252ac6b662d5c72c29903813eJake Slack
23703928aee4356845252ac6b662d5c72c29903813eJake Slack            // Add it to the cache.
23803928aee4356845252ac6b662d5c72c29903813eJake Slack            Content added = _cache.putIfAbsent(pathInContext,content);
23903928aee4356845252ac6b662d5c72c29903813eJake Slack            if (added!=null)
24003928aee4356845252ac6b662d5c72c29903813eJake Slack            {
24103928aee4356845252ac6b662d5c72c29903813eJake Slack                content.invalidate();
24203928aee4356845252ac6b662d5c72c29903813eJake Slack                content=added;
24303928aee4356845252ac6b662d5c72c29903813eJake Slack            }
24403928aee4356845252ac6b662d5c72c29903813eJake Slack
24503928aee4356845252ac6b662d5c72c29903813eJake Slack            return content;
24603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
24703928aee4356845252ac6b662d5c72c29903813eJake Slack
24803928aee4356845252ac6b662d5c72c29903813eJake Slack        return new HttpContent.ResourceAsHttpContent(resource,_mimeTypes.getMimeByExtension(resource.toString()),getMaxCachedFileSize(),_etags);
24903928aee4356845252ac6b662d5c72c29903813eJake Slack
25003928aee4356845252ac6b662d5c72c29903813eJake Slack    }
25103928aee4356845252ac6b662d5c72c29903813eJake Slack
25203928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
25303928aee4356845252ac6b662d5c72c29903813eJake Slack    private void shrinkCache()
25403928aee4356845252ac6b662d5c72c29903813eJake Slack    {
25503928aee4356845252ac6b662d5c72c29903813eJake Slack        // While we need to shrink
25603928aee4356845252ac6b662d5c72c29903813eJake Slack        while (_cache.size()>0 && (_cachedFiles.get()>_maxCachedFiles || _cachedSize.get()>_maxCacheSize))
25703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
25803928aee4356845252ac6b662d5c72c29903813eJake Slack            // Scan the entire cache and generate an ordered list by last accessed time.
25903928aee4356845252ac6b662d5c72c29903813eJake Slack            SortedSet<Content> sorted= new TreeSet<Content>(
26003928aee4356845252ac6b662d5c72c29903813eJake Slack                    new Comparator<Content>()
26103928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
26203928aee4356845252ac6b662d5c72c29903813eJake Slack                        public int compare(Content c1, Content c2)
26303928aee4356845252ac6b662d5c72c29903813eJake Slack                        {
26403928aee4356845252ac6b662d5c72c29903813eJake Slack                            if (c1._lastAccessed<c2._lastAccessed)
26503928aee4356845252ac6b662d5c72c29903813eJake Slack                                return -1;
26603928aee4356845252ac6b662d5c72c29903813eJake Slack
26703928aee4356845252ac6b662d5c72c29903813eJake Slack                            if (c1._lastAccessed>c2._lastAccessed)
26803928aee4356845252ac6b662d5c72c29903813eJake Slack                                return 1;
26903928aee4356845252ac6b662d5c72c29903813eJake Slack
27003928aee4356845252ac6b662d5c72c29903813eJake Slack                            if (c1._length<c2._length)
27103928aee4356845252ac6b662d5c72c29903813eJake Slack                                return -1;
27203928aee4356845252ac6b662d5c72c29903813eJake Slack
27303928aee4356845252ac6b662d5c72c29903813eJake Slack                            return c1._key.compareTo(c2._key);
27403928aee4356845252ac6b662d5c72c29903813eJake Slack                        }
27503928aee4356845252ac6b662d5c72c29903813eJake Slack                    });
27603928aee4356845252ac6b662d5c72c29903813eJake Slack            for (Content content : _cache.values())
27703928aee4356845252ac6b662d5c72c29903813eJake Slack                sorted.add(content);
27803928aee4356845252ac6b662d5c72c29903813eJake Slack
27903928aee4356845252ac6b662d5c72c29903813eJake Slack            // Invalidate least recently used first
28003928aee4356845252ac6b662d5c72c29903813eJake Slack            for (Content content : sorted)
28103928aee4356845252ac6b662d5c72c29903813eJake Slack            {
28203928aee4356845252ac6b662d5c72c29903813eJake Slack                if (_cachedFiles.get()<=_maxCachedFiles && _cachedSize.get()<=_maxCacheSize)
28303928aee4356845252ac6b662d5c72c29903813eJake Slack                    break;
28403928aee4356845252ac6b662d5c72c29903813eJake Slack                if (content==_cache.remove(content.getKey()))
28503928aee4356845252ac6b662d5c72c29903813eJake Slack                    content.invalidate();
28603928aee4356845252ac6b662d5c72c29903813eJake Slack            }
28703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
28803928aee4356845252ac6b662d5c72c29903813eJake Slack    }
28903928aee4356845252ac6b662d5c72c29903813eJake Slack
29003928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
29103928aee4356845252ac6b662d5c72c29903813eJake Slack    protected Buffer getIndirectBuffer(Resource resource)
29203928aee4356845252ac6b662d5c72c29903813eJake Slack    {
29303928aee4356845252ac6b662d5c72c29903813eJake Slack        try
29403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
29503928aee4356845252ac6b662d5c72c29903813eJake Slack            int len=(int)resource.length();
29603928aee4356845252ac6b662d5c72c29903813eJake Slack            if (len<0)
29703928aee4356845252ac6b662d5c72c29903813eJake Slack            {
29803928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.warn("invalid resource: "+String.valueOf(resource)+" "+len);
29903928aee4356845252ac6b662d5c72c29903813eJake Slack                return null;
30003928aee4356845252ac6b662d5c72c29903813eJake Slack            }
30103928aee4356845252ac6b662d5c72c29903813eJake Slack            Buffer buffer = new IndirectNIOBuffer(len);
30203928aee4356845252ac6b662d5c72c29903813eJake Slack            InputStream is = resource.getInputStream();
30303928aee4356845252ac6b662d5c72c29903813eJake Slack            buffer.readFrom(is,len);
30403928aee4356845252ac6b662d5c72c29903813eJake Slack            is.close();
30503928aee4356845252ac6b662d5c72c29903813eJake Slack            return buffer;
30603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
30703928aee4356845252ac6b662d5c72c29903813eJake Slack        catch(IOException e)
30803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
30903928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.warn(e);
31003928aee4356845252ac6b662d5c72c29903813eJake Slack            return null;
31103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
31203928aee4356845252ac6b662d5c72c29903813eJake Slack    }
31303928aee4356845252ac6b662d5c72c29903813eJake Slack
31403928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
31503928aee4356845252ac6b662d5c72c29903813eJake Slack    protected Buffer getDirectBuffer(Resource resource)
31603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
31703928aee4356845252ac6b662d5c72c29903813eJake Slack        try
31803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
31903928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_useFileMappedBuffer && resource.getFile()!=null)
32003928aee4356845252ac6b662d5c72c29903813eJake Slack                return new DirectNIOBuffer(resource.getFile());
32103928aee4356845252ac6b662d5c72c29903813eJake Slack
32203928aee4356845252ac6b662d5c72c29903813eJake Slack            int len=(int)resource.length();
32303928aee4356845252ac6b662d5c72c29903813eJake Slack            if (len<0)
32403928aee4356845252ac6b662d5c72c29903813eJake Slack            {
32503928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.warn("invalid resource: "+String.valueOf(resource)+" "+len);
32603928aee4356845252ac6b662d5c72c29903813eJake Slack                return null;
32703928aee4356845252ac6b662d5c72c29903813eJake Slack            }
32803928aee4356845252ac6b662d5c72c29903813eJake Slack            Buffer buffer = new DirectNIOBuffer(len);
32903928aee4356845252ac6b662d5c72c29903813eJake Slack            InputStream is = resource.getInputStream();
33003928aee4356845252ac6b662d5c72c29903813eJake Slack            buffer.readFrom(is,len);
33103928aee4356845252ac6b662d5c72c29903813eJake Slack            is.close();
33203928aee4356845252ac6b662d5c72c29903813eJake Slack            return buffer;
33303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
33403928aee4356845252ac6b662d5c72c29903813eJake Slack        catch(IOException e)
33503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
33603928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.warn(e);
33703928aee4356845252ac6b662d5c72c29903813eJake Slack            return null;
33803928aee4356845252ac6b662d5c72c29903813eJake Slack        }
33903928aee4356845252ac6b662d5c72c29903813eJake Slack    }
34003928aee4356845252ac6b662d5c72c29903813eJake Slack
34103928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
34203928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
34303928aee4356845252ac6b662d5c72c29903813eJake Slack    public String toString()
34403928aee4356845252ac6b662d5c72c29903813eJake Slack    {
34503928aee4356845252ac6b662d5c72c29903813eJake Slack        return "ResourceCache["+_parent+","+_factory+"]@"+hashCode();
34603928aee4356845252ac6b662d5c72c29903813eJake Slack    }
34703928aee4356845252ac6b662d5c72c29903813eJake Slack
34803928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
34903928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
35003928aee4356845252ac6b662d5c72c29903813eJake Slack    /** MetaData associated with a context Resource.
35103928aee4356845252ac6b662d5c72c29903813eJake Slack     */
35203928aee4356845252ac6b662d5c72c29903813eJake Slack    public class Content implements HttpContent
35303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
35403928aee4356845252ac6b662d5c72c29903813eJake Slack        final Resource _resource;
35503928aee4356845252ac6b662d5c72c29903813eJake Slack        final int _length;
35603928aee4356845252ac6b662d5c72c29903813eJake Slack        final String _key;
35703928aee4356845252ac6b662d5c72c29903813eJake Slack        final long _lastModified;
35803928aee4356845252ac6b662d5c72c29903813eJake Slack        final Buffer _lastModifiedBytes;
35903928aee4356845252ac6b662d5c72c29903813eJake Slack        final Buffer _contentType;
36003928aee4356845252ac6b662d5c72c29903813eJake Slack        final Buffer _etagBuffer;
36103928aee4356845252ac6b662d5c72c29903813eJake Slack
36203928aee4356845252ac6b662d5c72c29903813eJake Slack        volatile long _lastAccessed;
36303928aee4356845252ac6b662d5c72c29903813eJake Slack        AtomicReference<Buffer> _indirectBuffer=new AtomicReference<Buffer>();
36403928aee4356845252ac6b662d5c72c29903813eJake Slack        AtomicReference<Buffer> _directBuffer=new AtomicReference<Buffer>();
36503928aee4356845252ac6b662d5c72c29903813eJake Slack
36603928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
36703928aee4356845252ac6b662d5c72c29903813eJake Slack        Content(String pathInContext,Resource resource)
36803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
36903928aee4356845252ac6b662d5c72c29903813eJake Slack            _key=pathInContext;
37003928aee4356845252ac6b662d5c72c29903813eJake Slack            _resource=resource;
37103928aee4356845252ac6b662d5c72c29903813eJake Slack
37203928aee4356845252ac6b662d5c72c29903813eJake Slack            _contentType=_mimeTypes.getMimeByExtension(_resource.toString());
37303928aee4356845252ac6b662d5c72c29903813eJake Slack            boolean exists=resource.exists();
37403928aee4356845252ac6b662d5c72c29903813eJake Slack            _lastModified=exists?resource.lastModified():-1;
37503928aee4356845252ac6b662d5c72c29903813eJake Slack            _lastModifiedBytes=_lastModified<0?null:new ByteArrayBuffer(HttpFields.formatDate(_lastModified));
37603928aee4356845252ac6b662d5c72c29903813eJake Slack
37703928aee4356845252ac6b662d5c72c29903813eJake Slack            _length=exists?(int)resource.length():0;
37803928aee4356845252ac6b662d5c72c29903813eJake Slack            _cachedSize.addAndGet(_length);
37903928aee4356845252ac6b662d5c72c29903813eJake Slack            _cachedFiles.incrementAndGet();
38003928aee4356845252ac6b662d5c72c29903813eJake Slack            _lastAccessed=System.currentTimeMillis();
38103928aee4356845252ac6b662d5c72c29903813eJake Slack
38203928aee4356845252ac6b662d5c72c29903813eJake Slack            _etagBuffer=_etags?new ByteArrayBuffer(resource.getWeakETag()):null;
38303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
38403928aee4356845252ac6b662d5c72c29903813eJake Slack
38503928aee4356845252ac6b662d5c72c29903813eJake Slack
38603928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
38703928aee4356845252ac6b662d5c72c29903813eJake Slack        public String getKey()
38803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
38903928aee4356845252ac6b662d5c72c29903813eJake Slack            return _key;
39003928aee4356845252ac6b662d5c72c29903813eJake Slack        }
39103928aee4356845252ac6b662d5c72c29903813eJake Slack
39203928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
39303928aee4356845252ac6b662d5c72c29903813eJake Slack        public boolean isCached()
39403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
39503928aee4356845252ac6b662d5c72c29903813eJake Slack            return _key!=null;
39603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
39703928aee4356845252ac6b662d5c72c29903813eJake Slack
39803928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
39903928aee4356845252ac6b662d5c72c29903813eJake Slack        public boolean isMiss()
40003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
40103928aee4356845252ac6b662d5c72c29903813eJake Slack            return false;
40203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
40303928aee4356845252ac6b662d5c72c29903813eJake Slack
40403928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
40503928aee4356845252ac6b662d5c72c29903813eJake Slack        public Resource getResource()
40603928aee4356845252ac6b662d5c72c29903813eJake Slack        {
40703928aee4356845252ac6b662d5c72c29903813eJake Slack            return _resource;
40803928aee4356845252ac6b662d5c72c29903813eJake Slack        }
40903928aee4356845252ac6b662d5c72c29903813eJake Slack
41003928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
41103928aee4356845252ac6b662d5c72c29903813eJake Slack        public Buffer getETag()
41203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
41303928aee4356845252ac6b662d5c72c29903813eJake Slack            return _etagBuffer;
41403928aee4356845252ac6b662d5c72c29903813eJake Slack        }
41503928aee4356845252ac6b662d5c72c29903813eJake Slack
41603928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
41703928aee4356845252ac6b662d5c72c29903813eJake Slack        boolean isValid()
41803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
41903928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_lastModified==_resource.lastModified() && _length==_resource.length())
42003928aee4356845252ac6b662d5c72c29903813eJake Slack            {
42103928aee4356845252ac6b662d5c72c29903813eJake Slack                _lastAccessed=System.currentTimeMillis();
42203928aee4356845252ac6b662d5c72c29903813eJake Slack                return true;
42303928aee4356845252ac6b662d5c72c29903813eJake Slack            }
42403928aee4356845252ac6b662d5c72c29903813eJake Slack
42503928aee4356845252ac6b662d5c72c29903813eJake Slack            if (this==_cache.remove(_key))
42603928aee4356845252ac6b662d5c72c29903813eJake Slack                invalidate();
42703928aee4356845252ac6b662d5c72c29903813eJake Slack            return false;
42803928aee4356845252ac6b662d5c72c29903813eJake Slack        }
42903928aee4356845252ac6b662d5c72c29903813eJake Slack
43003928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
43103928aee4356845252ac6b662d5c72c29903813eJake Slack        protected void invalidate()
43203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
43303928aee4356845252ac6b662d5c72c29903813eJake Slack            // Invalidate it
43403928aee4356845252ac6b662d5c72c29903813eJake Slack            _cachedSize.addAndGet(-_length);
43503928aee4356845252ac6b662d5c72c29903813eJake Slack            _cachedFiles.decrementAndGet();
43603928aee4356845252ac6b662d5c72c29903813eJake Slack            _resource.release();
43703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
43803928aee4356845252ac6b662d5c72c29903813eJake Slack
43903928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
44003928aee4356845252ac6b662d5c72c29903813eJake Slack        public Buffer getLastModified()
44103928aee4356845252ac6b662d5c72c29903813eJake Slack        {
44203928aee4356845252ac6b662d5c72c29903813eJake Slack            return _lastModifiedBytes;
44303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
44403928aee4356845252ac6b662d5c72c29903813eJake Slack
44503928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
44603928aee4356845252ac6b662d5c72c29903813eJake Slack        public Buffer getContentType()
44703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
44803928aee4356845252ac6b662d5c72c29903813eJake Slack            return _contentType;
44903928aee4356845252ac6b662d5c72c29903813eJake Slack        }
45003928aee4356845252ac6b662d5c72c29903813eJake Slack
45103928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
45203928aee4356845252ac6b662d5c72c29903813eJake Slack        public void release()
45303928aee4356845252ac6b662d5c72c29903813eJake Slack        {
45403928aee4356845252ac6b662d5c72c29903813eJake Slack            // don't release while cached. Release when invalidated.
45503928aee4356845252ac6b662d5c72c29903813eJake Slack        }
45603928aee4356845252ac6b662d5c72c29903813eJake Slack
45703928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
45803928aee4356845252ac6b662d5c72c29903813eJake Slack        public Buffer getIndirectBuffer()
45903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
46003928aee4356845252ac6b662d5c72c29903813eJake Slack            Buffer buffer = _indirectBuffer.get();
46103928aee4356845252ac6b662d5c72c29903813eJake Slack            if (buffer==null)
46203928aee4356845252ac6b662d5c72c29903813eJake Slack            {
46303928aee4356845252ac6b662d5c72c29903813eJake Slack                Buffer buffer2=ResourceCache.this.getIndirectBuffer(_resource);
46403928aee4356845252ac6b662d5c72c29903813eJake Slack
46503928aee4356845252ac6b662d5c72c29903813eJake Slack                if (buffer2==null)
46603928aee4356845252ac6b662d5c72c29903813eJake Slack                    LOG.warn("Could not load "+this);
46703928aee4356845252ac6b662d5c72c29903813eJake Slack                else if (_indirectBuffer.compareAndSet(null,buffer2))
46803928aee4356845252ac6b662d5c72c29903813eJake Slack                    buffer=buffer2;
46903928aee4356845252ac6b662d5c72c29903813eJake Slack                else
47003928aee4356845252ac6b662d5c72c29903813eJake Slack                    buffer=_indirectBuffer.get();
47103928aee4356845252ac6b662d5c72c29903813eJake Slack            }
47203928aee4356845252ac6b662d5c72c29903813eJake Slack            if (buffer==null)
47303928aee4356845252ac6b662d5c72c29903813eJake Slack                return null;
47403928aee4356845252ac6b662d5c72c29903813eJake Slack            return new View(buffer);
47503928aee4356845252ac6b662d5c72c29903813eJake Slack        }
47603928aee4356845252ac6b662d5c72c29903813eJake Slack
47703928aee4356845252ac6b662d5c72c29903813eJake Slack
47803928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
47903928aee4356845252ac6b662d5c72c29903813eJake Slack        public Buffer getDirectBuffer()
48003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
48103928aee4356845252ac6b662d5c72c29903813eJake Slack            Buffer buffer = _directBuffer.get();
48203928aee4356845252ac6b662d5c72c29903813eJake Slack            if (buffer==null)
48303928aee4356845252ac6b662d5c72c29903813eJake Slack            {
48403928aee4356845252ac6b662d5c72c29903813eJake Slack                Buffer buffer2=ResourceCache.this.getDirectBuffer(_resource);
48503928aee4356845252ac6b662d5c72c29903813eJake Slack
48603928aee4356845252ac6b662d5c72c29903813eJake Slack                if (buffer2==null)
48703928aee4356845252ac6b662d5c72c29903813eJake Slack                    LOG.warn("Could not load "+this);
48803928aee4356845252ac6b662d5c72c29903813eJake Slack                else if (_directBuffer.compareAndSet(null,buffer2))
48903928aee4356845252ac6b662d5c72c29903813eJake Slack                    buffer=buffer2;
49003928aee4356845252ac6b662d5c72c29903813eJake Slack                else
49103928aee4356845252ac6b662d5c72c29903813eJake Slack                    buffer=_directBuffer.get();
49203928aee4356845252ac6b662d5c72c29903813eJake Slack            }
49303928aee4356845252ac6b662d5c72c29903813eJake Slack            if (buffer==null)
49403928aee4356845252ac6b662d5c72c29903813eJake Slack                return null;
49503928aee4356845252ac6b662d5c72c29903813eJake Slack
49603928aee4356845252ac6b662d5c72c29903813eJake Slack            return new View(buffer);
49703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
49803928aee4356845252ac6b662d5c72c29903813eJake Slack
49903928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
50003928aee4356845252ac6b662d5c72c29903813eJake Slack        public long getContentLength()
50103928aee4356845252ac6b662d5c72c29903813eJake Slack        {
50203928aee4356845252ac6b662d5c72c29903813eJake Slack            return _length;
50303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
50403928aee4356845252ac6b662d5c72c29903813eJake Slack
50503928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
50603928aee4356845252ac6b662d5c72c29903813eJake Slack        public InputStream getInputStream() throws IOException
50703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
50803928aee4356845252ac6b662d5c72c29903813eJake Slack            Buffer indirect = getIndirectBuffer();
50903928aee4356845252ac6b662d5c72c29903813eJake Slack            if (indirect!=null && indirect.array()!=null)
51003928aee4356845252ac6b662d5c72c29903813eJake Slack                return new ByteArrayInputStream(indirect.array(),indirect.getIndex(),indirect.length());
51103928aee4356845252ac6b662d5c72c29903813eJake Slack
51203928aee4356845252ac6b662d5c72c29903813eJake Slack            return _resource.getInputStream();
51303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
51403928aee4356845252ac6b662d5c72c29903813eJake Slack
51503928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
51603928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
51703928aee4356845252ac6b662d5c72c29903813eJake Slack        public String toString()
51803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
51903928aee4356845252ac6b662d5c72c29903813eJake Slack            return String.format("%s %s %d %s %s",_resource,_resource.exists(),_resource.lastModified(),_contentType,_lastModifiedBytes);
52003928aee4356845252ac6b662d5c72c29903813eJake Slack        }
52103928aee4356845252ac6b662d5c72c29903813eJake Slack    }
52203928aee4356845252ac6b662d5c72c29903813eJake Slack}
523