KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mortbay > http > ResourceCache


1 // ========================================================================
2
// $Id: ResourceCache.java,v 1.13 2006/04/04 22:28:02 gregwilkins Exp $
3
// Copyright 2000-2004 Mort Bay Consulting Pty. Ltd.
4
// ------------------------------------------------------------------------
5
// Licensed under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
// http://www.apache.org/licenses/LICENSE-2.0
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
// ========================================================================
15

16 package org.mortbay.http;
17
18 import java.io.IOException JavaDoc;
19 import java.io.Serializable JavaDoc;
20 import java.util.Collections JavaDoc;
21 import java.util.Enumeration JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.Map JavaDoc;
24 import java.util.ResourceBundle JavaDoc;
25
26 import org.apache.commons.logging.Log;
27 import org.mortbay.log.LogFactory;
28 import org.mortbay.util.CachedResource;
29 import org.mortbay.util.LifeCycle;
30 import org.mortbay.util.LogSupport;
31 import org.mortbay.util.Resource;
32 import org.mortbay.util.StringUtil;
33
34
35 /* ------------------------------------------------------------ */
36 /**
37  * @version $Id: ResourceCache.java,v 1.13 2006/04/04 22:28:02 gregwilkins Exp $
38  * @author Greg Wilkins
39  */

40 public class ResourceCache implements LifeCycle,
41                                       Serializable JavaDoc
42 {
43     private static Log log = LogFactory.getLog(ResourceCache.class);
44
45     /* ------------------------------------------------------------ */
46     /* ------------------------------------------------------------ */
47     private final static Map JavaDoc __dftMimeMap = new HashMap JavaDoc();
48     private final static Map JavaDoc __encodings = new HashMap JavaDoc();
49     static
50     {
51         ResourceBundle JavaDoc mime = ResourceBundle.getBundle("org/mortbay/http/mime");
52         Enumeration JavaDoc i = mime.getKeys();
53         while(i.hasMoreElements())
54         {
55             String JavaDoc ext = (String JavaDoc)i.nextElement();
56             __dftMimeMap.put(StringUtil.asciiToLowerCase(ext),mime.getString(ext));
57         }
58         ResourceBundle JavaDoc encoding = ResourceBundle.getBundle("org/mortbay/http/encoding");
59         i = encoding.getKeys();
60         while(i.hasMoreElements())
61         {
62             String JavaDoc type = (String JavaDoc)i.nextElement();
63             __encodings.put(type,encoding.getString(type));
64         }
65     }
66
67     /* ------------------------------------------------------------ */
68     /* ------------------------------------------------------------ */
69     // TODO - handle this
70
// These attributes are serialized by WebApplicationContext, which needs
71
// to be updated if you add to these
72

73     
74     
75     
76     private int _maxCachedFileSize =1*1024;
77     private int _maxCacheSize =1*1024;
78
79     /* ------------------------------------------------------------ */
80     private Resource _resourceBase;
81     private Map JavaDoc _mimeMap;
82     private Map JavaDoc _encodingMap;
83
84
85     /* ------------------------------------------------------------ */
86     private transient boolean _started;
87
88     protected transient Map JavaDoc _cache;
89     protected transient int _cacheSize;
90     protected transient CachedMetaData _mostRecentlyUsed;
91     protected transient CachedMetaData _leastRecentlyUsed;
92
93
94     /* ------------------------------------------------------------ */
95     /** Constructor.
96      */

97     public ResourceCache()
98     {
99         _cache=new HashMap JavaDoc();
100     }
101
102
103     /* ------------------------------------------------------------ */
104     private void readObject(java.io.ObjectInputStream JavaDoc in)
105         throws IOException JavaDoc, ClassNotFoundException JavaDoc
106     {
107         in.defaultReadObject();
108         _cache=new HashMap JavaDoc();
109     }
110     
111     /* ------------------------------------------------------------ */
112     /** Set the Resource Base.
113      * The base resource is the Resource to use as a relative base
114      * for all context resources. The ResourceBase attribute is a
115      * string version of the baseResource.
116      * If a relative file is passed, it is converted to a file
117      * URL based on the current working directory.
118      * @return The file or URL to use as the base for all resources
119      * within the context.
120      */

121     public String JavaDoc getResourceBase()
122     {
123         if (_resourceBase==null)
124             return null;
125         return _resourceBase.toString();
126     }
127
128     /* ------------------------------------------------------------ */
129     /** Set the Resource Base.
130      * The base resource is the Resource to use as a relative base
131      * for all context resources. The ResourceBase attribute is a
132      * string version of the baseResource.
133      * If a relative file is passed, it is converted to a file
134      * URL based on the current working directory.
135      * @param resourceBase A URL prefix or directory name.
136      */

137     public void setResourceBase(String JavaDoc resourceBase)
138     {
139         try{
140             _resourceBase=Resource.newResource(resourceBase);
141             if(log.isDebugEnabled())log.debug("resourceBase="+_resourceBase+" for "+this);
142         }
143         catch(IOException JavaDoc e)
144         {
145             log.debug(LogSupport.EXCEPTION,e);
146             throw new IllegalArgumentException JavaDoc(resourceBase+":"+e.toString());
147         }
148     }
149
150
151     /* ------------------------------------------------------------ */
152     /** Get the base resource.
153      * The base resource is the Resource to use as a relative base
154      * for all context resources. The ResourceBase attribute is a
155      * string version of the baseResource.
156      * @return The resourceBase as a Resource instance
157      */

158     public Resource getBaseResource()
159     {
160         return _resourceBase;
161     }
162
163     /* ------------------------------------------------------------ */
164     /** Set the base resource.
165      * The base resource is the Resource to use as a relative base
166      * for all context resources. The ResourceBase attribute is a
167      * string version of the baseResource.
168      * @param base The resourceBase as a Resource instance
169      */

170     public void setBaseResource(Resource base)
171     {
172         _resourceBase=base;
173     }
174
175
176     /* ------------------------------------------------------------ */
177     public int getMaxCachedFileSize()
178     {
179         return _maxCachedFileSize;
180     }
181
182     /* ------------------------------------------------------------ */
183     public void setMaxCachedFileSize(int maxCachedFileSize)
184     {
185         _maxCachedFileSize = maxCachedFileSize;
186         _cache.clear();
187     }
188
189     /* ------------------------------------------------------------ */
190     public int getMaxCacheSize()
191     {
192         return _maxCacheSize;
193     }
194
195     /* ------------------------------------------------------------ */
196     public void setMaxCacheSize(int maxCacheSize)
197     {
198         _maxCacheSize = maxCacheSize;
199         _cache.clear();
200     }
201
202     /* ------------------------------------------------------------ */
203     public void flushCache()
204     {
205         _cache.clear();
206         System.gc();
207     }
208
209     /* ------------------------------------------------------------ */
210     /** Get a resource from the context.
211      * Cached Resources are returned if the resource fits within the LRU
212      * cache. Directories may have CachedResources returned, but the
213      * caller must use the CachedResource.setCachedData method to set the
214      * formatted directory content.
215      *
216      * @param pathInContext
217      * @return Resource
218      * @exception IOException
219      */

220     public Resource getResource(String JavaDoc pathInContext)
221         throws IOException JavaDoc
222     {
223         if(log.isTraceEnabled())log.trace("getResource "+pathInContext);
224         if (_resourceBase==null)
225             return null;
226
227         Resource resource=null;
228
229         // Cache operations
230
synchronized(_cache)
231         {
232             // Look for it in the cache
233
CachedResource cached = (CachedResource)_cache.get(pathInContext);
234             if (cached!=null)
235             {
236                 if(log.isTraceEnabled())log.trace("CACHE HIT: "+cached);
237                 CachedMetaData cmd = (CachedMetaData)cached.getAssociate();
238                 if (cmd!=null && cmd.isValid())
239                     return cached;
240             }
241
242             // Make the resource
243
resource=_resourceBase.addPath(_resourceBase.encode(pathInContext));
244             if(log.isTraceEnabled())log.trace("CACHE MISS: "+resource);
245             if (resource==null)
246                 return null;
247
248             
249             // Check for file aliasing
250
if (resource.getAlias()!=null)
251             {
252                 log.warn("Alias request of '"+resource.getAlias()+
253                              "' for '"+resource+"'");
254                 return null;
255             }
256
257             // Is it an existing file?
258
long len = resource.length();
259             if (resource.exists())
260             {
261                 // Is it badly named?
262
if (!resource.isDirectory() && pathInContext.endsWith("/"))
263                     return null;
264
265                 // Guess directory length.
266
if (resource.isDirectory())
267                 {
268                     if (resource.list()!=null)
269                         len=resource.list().length*100;
270                     else
271                         len=0;
272                 }
273
274                 // Is it cacheable?
275
if (len>0 && len<_maxCachedFileSize && len<_maxCacheSize)
276                 {
277                     int needed=_maxCacheSize-(int)len;
278                     while(_cacheSize>needed)
279                         _leastRecentlyUsed.invalidate();
280
281                     cached=resource.cache();
282                     if(log.isTraceEnabled())log.trace("CACHED: "+resource);
283                     new CachedMetaData(cached,pathInContext);
284                     return cached;
285                 }
286             }
287         }
288
289         // Non cached response
290
new ResourceMetaData(resource);
291         return resource;
292     }
293
294
295     /* ------------------------------------------------------------ */
296     public synchronized Map JavaDoc getMimeMap()
297     {
298         return _mimeMap;
299     }
300
301     /* ------------------------------------------------------------ */
302     /**
303      * Also sets the org.mortbay.http.mimeMap context attribute
304      * @param mimeMap
305      */

306     public void setMimeMap(Map JavaDoc mimeMap)
307     {
308         _mimeMap = mimeMap;
309     }
310
311     /* ------------------------------------------------------------ */
312     /** Get the MIME type by filename extension.
313      * @param filename A file name
314      * @return MIME type matching the longest dot extension of the
315      * file name.
316      */

317     public String JavaDoc getMimeByExtension(String JavaDoc filename)
318     {
319         String JavaDoc type=null;
320
321         if (filename!=null)
322         {
323             int i=-1;
324             while(type==null)
325             {
326                 i=filename.indexOf(".",i+1);
327
328                 if (i<0 || i>=filename.length())
329                     break;
330
331                 String JavaDoc ext=StringUtil.asciiToLowerCase(filename.substring(i+1));
332                 if (_mimeMap!=null)
333                     type = (String JavaDoc)_mimeMap.get(ext);
334                 if (type==null)
335                     type=(String JavaDoc)__dftMimeMap.get(ext);
336             }
337         }
338
339         if (type==null)
340         {
341             if (_mimeMap!=null)
342                 type=(String JavaDoc)_mimeMap.get("*");
343              if (type==null)
344                  type=(String JavaDoc)__dftMimeMap.get("*");
345         }
346
347         return type;
348     }
349
350     /* ------------------------------------------------------------ */
351     /** Set a mime mapping
352      * @param extension
353      * @param type
354      */

355     public void setMimeMapping(String JavaDoc extension,String JavaDoc type)
356     {
357         if (_mimeMap==null)
358             _mimeMap=new HashMap JavaDoc();
359         _mimeMap.put(StringUtil.asciiToLowerCase(extension),type);
360     }
361
362
363     /* ------------------------------------------------------------ */
364     /** Get the map of mime type to char encoding.
365      * @return Map of mime type to character encodings.
366      */

367     public synchronized Map JavaDoc getEncodingMap()
368     {
369         if (_encodingMap==null)
370             _encodingMap=Collections.unmodifiableMap(__encodings);
371         return _encodingMap;
372     }
373
374     /* ------------------------------------------------------------ */
375     /** Set the map of mime type to char encoding.
376      * Also sets the org.mortbay.http.encodingMap context attribute
377      * @param encodingMap Map of mime type to character encodings.
378      */

379     public void setEncodingMap(Map JavaDoc encodingMap)
380     {
381         _encodingMap = encodingMap;
382     }
383
384     /* ------------------------------------------------------------ */
385     /** Get char encoding by mime type.
386      * @param type A mime type.
387      * @return The prefered character encoding for that type if known.
388      */

389     public String JavaDoc getEncodingByMimeType(String JavaDoc type)
390     {
391         String JavaDoc encoding =null;
392
393         if (type!=null)
394             encoding=(String JavaDoc)_encodingMap.get(type);
395
396         return encoding;
397     }
398
399     /* ------------------------------------------------------------ */
400     /** Set the encoding that should be used for a mimeType.
401      * @param mimeType
402      * @param encoding
403      */

404     public void setTypeEncoding(String JavaDoc mimeType,String JavaDoc encoding)
405     {
406         getEncodingMap().put(mimeType,encoding);
407     }
408
409     /* ------------------------------------------------------------ */
410     public synchronized void start()
411         throws Exception JavaDoc
412     {
413         if (isStarted())
414             return;
415         getMimeMap();
416         getEncodingMap();
417         _started=true;
418     }
419
420     /* ------------------------------------------------------------ */
421     public boolean isStarted()
422     {
423         return _started;
424     }
425
426     /* ------------------------------------------------------------ */
427     /** Stop the context.
428      */

429     public void stop()
430         throws InterruptedException JavaDoc
431     {
432         _started=false;
433         _cache.clear();
434     }
435
436
437     /* ------------------------------------------------------------ */
438     /** Destroy a context.
439      * Destroy a context and remove it from the HttpServer. The
440      * HttpContext must be stopped before it can be destroyed.
441      */

442     public void destroy()
443     {
444         if (isStarted())
445             throw new IllegalStateException JavaDoc("Started");
446
447         setMimeMap(null);
448         _encodingMap=null;
449     }
450
451
452     /* ------------------------------------------------------------ */
453     /** Get Resource MetaData.
454      * @param resource
455      * @return Meta data for the resource.
456      */

457     public ResourceMetaData getResourceMetaData(Resource resource)
458     {
459         Object JavaDoc o=resource.getAssociate();
460         if (o instanceof ResourceMetaData)
461             return (ResourceMetaData)o;
462         return new ResourceMetaData(resource);
463     }
464     
465     /* ------------------------------------------------------------ */
466     /* ------------------------------------------------------------ */
467     /** MetaData associated with a context Resource.
468      */

469     public class ResourceMetaData
470     {
471         protected String JavaDoc _name;
472         protected Resource _resource;
473
474         ResourceMetaData(Resource resource)
475         {
476             _resource=resource;
477             _name=_resource.toString();
478             _resource.setAssociate(this);
479         }
480
481         public String JavaDoc getLength()
482         {
483             return Long.toString(_resource.length());
484         }
485
486         public String JavaDoc getLastModified()
487         {
488             return HttpFields.formatDate(_resource.lastModified(),false);
489         }
490
491         public String JavaDoc getMimeType()
492         {
493             return getMimeByExtension(_name);
494         }
495     }
496
497     /* ------------------------------------------------------------ */
498     /* ------------------------------------------------------------ */
499     private class CachedMetaData extends ResourceMetaData
500     {
501         String JavaDoc _lastModified;
502         String JavaDoc _encoding;
503         String JavaDoc _length;
504         String JavaDoc _key;
505
506         CachedResource _cached;
507         CachedMetaData _prev;
508         CachedMetaData _next;
509
510         CachedMetaData(CachedResource resource, String JavaDoc pathInContext)
511         {
512             super(resource);
513             _cached=resource;
514             _length=super.getLength();
515             _lastModified=super.getLastModified();
516             _encoding=super.getMimeType();
517             _key=pathInContext;
518
519             _next=_mostRecentlyUsed;
520             _mostRecentlyUsed=this;
521             if (_next!=null)
522                 _next._prev=this;
523             _prev=null;
524             if (_leastRecentlyUsed==null)
525                 _leastRecentlyUsed=this;
526
527             _cache.put(_key,resource);
528
529             _cacheSize+=_cached.length();
530
531         }
532
533         public String JavaDoc getLength()
534         {
535             return _length;
536         }
537
538         public String JavaDoc getLastModified()
539         {
540             return _lastModified;
541         }
542
543         public String JavaDoc getMimeType()
544         {
545             return _encoding;
546         }
547
548         /* ------------------------------------------------------------ */
549         boolean isValid()
550             throws IOException JavaDoc
551         {
552             if (_cached.isUptoDate())
553             {
554                 if (_mostRecentlyUsed!=this)
555                 {
556                     CachedMetaData tp = _prev;
557                     CachedMetaData tn = _next;
558
559                     _next=_mostRecentlyUsed;
560                     _mostRecentlyUsed=this;
561                     if (_next!=null)
562                         _next._prev=this;
563                     _prev=null;
564
565                     if (tp!=null)
566                         tp._next=tn;
567                     if (tn!=null)
568                         tn._prev=tp;
569
570                     if (_leastRecentlyUsed==this && tp!=null)
571                         _leastRecentlyUsed=tp;
572                 }
573                 return true;
574             }
575
576             invalidate();
577             return false;
578         }
579
580         public void invalidate()
581         {
582             // Invalidate it
583
_cache.remove(_key);
584             _cacheSize=_cacheSize-(int)_cached.length();
585
586
587             if (_mostRecentlyUsed==this)
588                 _mostRecentlyUsed=_next;
589             else
590                 _prev._next=_next;
591
592             if (_leastRecentlyUsed==this)
593                 _leastRecentlyUsed=_prev;
594             else
595                 _next._prev=_prev;
596
597             _prev=null;
598             _next=null;
599         }
600     }
601     
602
603 }
604
Popular Tags