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             {
429                 if (_resource!=null)
430                     return _resource.length();
431                 return -1;
432             }
433             return _buffer.length();
434         }
435 
436         /* ------------------------------------------------------------ */
437         public InputStream getInputStream() throws IOException
438         {
439             return _resource.getInputStream();
440         }
441         
442     }
443 
444 }