1
2
3
4
5
6
7
8
9
10
11
12
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
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
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
101
102 public int getMaxCachedFiles()
103 {
104 return _maxCachedFiles;
105 }
106
107
108
109
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
142
143
144
145
146
147
148
149 public Content lookup(String pathInContext, ResourceFactory factory)
150 throws IOException
151 {
152 Content content=null;
153
154
155 synchronized(_cache)
156 {
157
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
175 synchronized(_cache)
176 {
177
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
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
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
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
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 }