View Javadoc

    1   // ========================================================================
    2   // Copyright 2000-2005 Mort Bay Consulting Pty. Ltd.
    3   // ------------------------------------------------------------------------
    4   // Licensed under the Apache License, Version 2.0 (the "License");
    5   // you may not use this file except in compliance with the License.
    6   // You may obtain a copy of the License at
    7   // http://www.apache.org/licenses/LICENSE-2.0
    8   // Unless required by applicable law or agreed to in writing, software
    9   // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  // ========================================================================
    14
    15  package org.mortbay.jetty;
    16
    17  import java.io.IOException;
    18  import java.io.InputStream;
    19  import java.io.Serializable;
    20  import java.util.ArrayList;
    21  import java.util.HashMap;
    22  import java.util.Iterator;
    23  import java.util.Map;
    24
    25  import org.mortbay.component.AbstractLifeCycle;
    26  import org.mortbay.io.Buffer;
    27  import org.mortbay.io.ByteArrayBuffer;
    28  import org.mortbay.io.View;
    29  import org.mortbay.resource.Resource;
    30  import org.mortbay.resource.ResourceFactory;
    31
    32
    33  /* ------------------------------------------------------------ */
    34  /**
    35   * @author Greg Wilkins
    36   */
    37  public class ResourceCache extends AbstractLifeCycle implements Serializable
    38  {
    39      private int _maxCachedFileSize =1024*1024;
    40      private int _maxCachedFiles=2048;
    41      private int _maxCacheSize =16*1024*1024;
    42      private MimeTypes _mimeTypes;
    43
    44      protected transient Map _cache;
    45      protected transient int _cachedSize;
    46      protected transient int _cachedFiles;
    47      protected transient Content _mostRecentlyUsed;
    48      protected transient Content _leastRecentlyUsed;
    49
    50
    51      /* ------------------------------------------------------------ */
    52      /** Constructor.
    53       */
    54      public ResourceCache(MimeTypes mimeTypes)
    55      {
    56          _mimeTypes=mimeTypes;
    57      }
    58
    59      /* ------------------------------------------------------------ */
    60      public int getCachedSize()
    61      {
    62          return _cachedSize;
    63      }
    64
    65      /* ------------------------------------------------------------ */
    66      public int getCachedFiles()
    67      {
    68          return _cachedFiles;
    69      }
    70
    71
    72      /* ------------------------------------------------------------ */
    73      public int getMaxCachedFileSize()
    74      {
    75          return _maxCachedFileSize;
    76      }
    77
    78      /* ------------------------------------------------------------ */
    79      public void setMaxCachedFileSize(int maxCachedFileSize)
    80      {
    81          _maxCachedFileSize = maxCachedFileSize;
    82          flushCache();
    83      }
    84
    85      /* ------------------------------------------------------------ */
    86      public int getMaxCacheSize()
    87      {
    88          return _maxCacheSize;
    89      }
    90
    91      /* ------------------------------------------------------------ */
    92      public void setMaxCacheSize(int maxCacheSize)
    93      {
    94          _maxCacheSize = maxCacheSize;
    95          flushCache();
    96      }
    97
    98      /* ------------------------------------------------------------ */
    99      /**
    100      * @return Returns the maxCachedFiles.
    101      */
    102     public int getMaxCachedFiles()
    103     {
    104         return _maxCachedFiles;
    105     }
    106
    107     /* ------------------------------------------------------------ */
    108     /**
    109      * @param maxCachedFiles The maxCachedFiles to set.
    110      */
    111     public void setMaxCachedFiles(int maxCachedFiles)
    112     {
    113         _maxCachedFiles = maxCachedFiles;
    114     }
    115
    116     /* ------------------------------------------------------------ */
    117     public void flushCache()
    118     {
    119         if (_cache!=null)
    120         {
    121             synchronized(this)
    122             {
    123                 ArrayList values=new ArrayList(_cache.values());
    124                 Iterator iter = values.iterator();
    125                 while(iter.hasNext())
    126                 {
    127                     Content content = (Content)iter.next();
    128                     content.invalidate();
    129                 }
    130
    131                 _cache.clear();
    132                 _cachedSize=0;
    133                 _cachedFiles=0;
    134                 _mostRecentlyUsed=null;
    135                 _leastRecentlyUsed=null;
    136             }
    137         }
    138     }
    139
    140     /* ------------------------------------------------------------ */
    141     /** Get a Entry from the cache.
    142      * Get either a valid entry object or create a new one if possible.
    143      *
    144      * @param pathInContext The key into the cache
    145      * @param factory If no matching entry is found, this {@link ResourceFactory} will be used to create the {@link Resource}
    146      *                for the new enry that is created.
    147      * @return The entry matching <code>pathInContext</code>, or a new entry if no matching entry was found
    148      */
    149     public Content lookup(String pathInContext, ResourceFactory factory)
    150         throws IOException
    151     {
    152         Content content=null;
    153
    154         // Look up cache operations
    155         synchronized(_cache)
    156         {
    157             // Look for it in the cache
    158             content = (Content)_cache.get(pathInContext);
    159
    160             if (content!=null && content.isValid())
    161             {
    162                 return content;
    163             }
    164         }
    165         Resource resource=factory.getResource(pathInContext);
    166         return load(pathInContext,resource);
    167     }
    168
    169     public Content lookup(String pathInContext, Resource resource)
    170         throws IOException
    171     {
    172         Content content=null;
    173
    174         // Look up cache operations
    175         synchronized(_cache)
    176         {
    177             // Look for it in the cache
    178             content = (Content)_cache.get(pathInContext);
    179
    180             if (content!=null && content.isValid())
    181             {
    182                 return content;
    183             }
    184         }
    185         return load(pathInContext,resource);
    186     }
    187
    188     private Content load(String pathInContext, Resource resource)
    189         throws IOException
    190     {
    191         Content content=null;
    192         if (resource!=null && resource.exists() && !resource.isDirectory())
    193         {
    194             long len = resource.length();
    195             if (len>0 && len<_maxCachedFileSize && len<_maxCacheSize)
    196             {
    197                 content = new Content(resource);
    198                 fill(content);
    199
    200                 synchronized(_cache)
    201                 {
    202                     // check that somebody else did not fill this spot.
    203                     Content content2 =(Content)_cache.get(pathInContext);
    204                     if (content2!=null)
    205                     {
    206                         content.release();
    207                         return content2;
    208                     }
    209
    210                     int must_be_smaller_than=_maxCacheSize-(int)len;
    211                     while(_cachedSize>must_be_smaller_than || (_maxCachedFiles>0 && _cachedFiles>=_maxCachedFiles))
    212                         _leastRecentlyUsed.invalidate();
    213                     content.cache(pathInContext);
    214
    215                     return content;
    216                 }
    217             }
    218         }
    219
    220         return null;
    221     }
    222
    223
    224     /* ------------------------------------------------------------ */
    225     public synchronized void doStart()
    226         throws Exception
    227     {
    228         _cache=new HashMap();
    229         _cachedSize=0;
    230         _cachedFiles=0;
    231     }
    232
    233     /* ------------------------------------------------------------ */
    234     /** Stop the context.
    235      */
    236     public void doStop()
    237         throws InterruptedException
    238     {
    239         flushCache();
    240     }
    241
    242     /* ------------------------------------------------------------ */
    243     protected void fill(Content content)
    244         throws IOException
    245     {
    246         try
    247         {
    248             InputStream in = content.getResource().getInputStream();
    249             int len=(int)content.getResource().length();
    250             Buffer buffer = new ByteArrayBuffer(len);
    251             buffer.readFrom(in,len);
    252             in.close();
    253             content.setBuffer(buffer);
    254         }
    255         finally
    256         {
    257             content.getResource().release();
    258         }
    259     }
    260
    261     /* ------------------------------------------------------------ */
    262     /* ------------------------------------------------------------ */
    263     /** MetaData associated with a context Resource.
    264      */
    265     public class Content implements HttpContent
    266     {
    267         String _key;
    268         Resource _resource;
    269         long _lastModified;
    270         Content _prev;
    271         Content _next;
    272
    273         Buffer _lastModifiedBytes;
    274         Buffer _contentType;
    275         Buffer _buffer;
    276
    277         /* ------------------------------------------------------------ */
    278         Content(Resource resource)
    279         {
    280             _resource=resource;
    281
    282             _next=this;
    283             _prev=this;
    284             _contentType=_mimeTypes.getMimeByExtension(_resource.toString());
    285
    286             _lastModified=resource.lastModified();
    287         }
    288
    289         /* ------------------------------------------------------------ */
    290         void cache(String pathInContext)
    291         {
    292             _key=pathInContext;
    293             _next=_mostRecentlyUsed;
    294             _mostRecentlyUsed=this;
    295             if (_next!=null)
    296                 _next._prev=this;
    297             _prev=null;
    298             if (_leastRecentlyUsed==null)
    299                 _leastRecentlyUsed=this;
    300
    301             _cache.put(_key,this);
    302             _cachedSize+=_buffer.length();
    303             _cachedFiles++;
    304             if (_lastModified!=-1)
    305                 _lastModifiedBytes=new ByteArrayBuffer(HttpFields.formatDate(_lastModified,false));
    306         }
    307
    308         /* ------------------------------------------------------------ */
    309         public String getKey()
    310         {
    311             return _key;
    312         }
    313
    314         /* ------------------------------------------------------------ */
    315         public boolean isCached()
    316         {
    317             return _key!=null;
    318         }
    319
    320         /* ------------------------------------------------------------ */
    321         public Resource getResource()
    322         {
    323             return _resource;
    324         }
    325
    326         /* ------------------------------------------------------------ */
    327         boolean isValid()
    328         {
    329             if (_lastModified==_resource.lastModified())
    330             {
    331                 if (_mostRecentlyUsed!=this)
    332                 {
    333                     Content tp = _prev;
    334                     Content tn = _next;
    335
    336                     _next=_mostRecentlyUsed;
    337                     _mostRecentlyUsed=this;
    338                     if (_next!=null)
    339                         _next._prev=this;
    340                     _prev=null;
    341
    342                     if (tp!=null)
    343                         tp._next=tn;
    344                     if (tn!=null)
    345                         tn._prev=tp;
    346
    347                     if (_leastRecentlyUsed==this && tp!=null)
    348                         _leastRecentlyUsed=tp;
    349                 }
    350                 return true;
    351             }
    352
    353             invalidate();
    354             return false;
    355         }
    356
    357         /* ------------------------------------------------------------ */
    358         public void invalidate()
    359         {
    360             synchronized(this)
    361             {
    362                 // Invalidate it
    363                 _cache.remove(_key);
    364                 _key=null;
    365                 _cachedSize=_cachedSize-(int)_buffer.length();
    366                 _cachedFiles--;
    367
    368                 if (_mostRecentlyUsed==this)
    369                     _mostRecentlyUsed=_next;
    370                 else
    371                     _prev._next=_next;
    372
    373                 if (_leastRecentlyUsed==this)
    374                     _leastRecentlyUsed=_prev;
    375                 else
    376                     _next._prev=_prev;
    377
    378                 _prev=null;
    379                 _next=null;
    380                 if (_resource!=null)
    381                     _resource.release();
    382                 _resource=null;
    383
    384             }
    385         }
    386
    387         /* ------------------------------------------------------------ */
    388         public Buffer getLastModified()
    389         {
    390             return _lastModifiedBytes;
    391         }
    392
    393         /* ------------------------------------------------------------ */
    394         public Buffer getContentType()
    395         {
    396             return _contentType;
    397         }
    398
    399         /* ------------------------------------------------------------ */
    400         public void setContentType(Buffer type)
    401         {
    402             _contentType=type;
    403         }
    404
    405         /* ------------------------------------------------------------ */
    406         public void release()
    407         {
    408         }
    409
    410         /* ------------------------------------------------------------ */
    411         public Buffer getBuffer()
    412         {
    413             if (_buffer==null)
    414                 return null;
    415             return new View(_buffer);
    416         }
    417
    418         /* ------------------------------------------------------------ */
    419         public void setBuffer(Buffer buffer)
    420         {
    421             _buffer=buffer;
    422         }
    423
    424         /* ------------------------------------------------------------ */
    425         public long getContentLength()
    426         {
    427             if (_buffer==null)
    428                 return -1;
    429             return _buffer.length();
    430         }
    431
    432         /* ------------------------------------------------------------ */
    433         public InputStream getInputStream() throws IOException
    434         {
    435             return _resource.getInputStream();
    436         }
    437
    438     }
    439
    440 }