1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jetty.server;
15
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.io.Serializable;
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.Map;
22
23 import org.eclipse.jetty.http.HttpContent;
24 import org.eclipse.jetty.http.HttpFields;
25 import org.eclipse.jetty.http.MimeTypes;
26 import org.eclipse.jetty.io.Buffer;
27 import org.eclipse.jetty.io.ByteArrayBuffer;
28 import org.eclipse.jetty.io.View;
29 import org.eclipse.jetty.util.component.AbstractLifeCycle;
30 import org.eclipse.jetty.util.resource.Resource;
31 import org.eclipse.jetty.util.resource.ResourceFactory;
32
33
34
35
36
37
38 public class ResourceCache extends AbstractLifeCycle
39 {
40 protected final Map _cache;
41 private final MimeTypes _mimeTypes;
42 private int _maxCachedFileSize =1024*1024;
43 private int _maxCachedFiles=2048;
44 private int _maxCacheSize =16*1024*1024;
45
46 protected int _cachedSize;
47 protected int _cachedFiles;
48 protected Content _mostRecentlyUsed;
49 protected Content _leastRecentlyUsed;
50
51
52
53
54 public ResourceCache(MimeTypes mimeTypes)
55 {
56 _cache=new HashMap();
57 _mimeTypes=mimeTypes;
58 }
59
60
61 public int getCachedSize()
62 {
63 return _cachedSize;
64 }
65
66
67 public int getCachedFiles()
68 {
69 return _cachedFiles;
70 }
71
72
73
74 public int getMaxCachedFileSize()
75 {
76 return _maxCachedFileSize;
77 }
78
79
80 public void setMaxCachedFileSize(int maxCachedFileSize)
81 {
82 _maxCachedFileSize = maxCachedFileSize;
83 flushCache();
84 }
85
86
87 public int getMaxCacheSize()
88 {
89 return _maxCacheSize;
90 }
91
92
93 public void setMaxCacheSize(int maxCacheSize)
94 {
95 _maxCacheSize = maxCacheSize;
96 flushCache();
97 }
98
99
100
101
102
103 public int getMaxCachedFiles()
104 {
105 return _maxCachedFiles;
106 }
107
108
109
110
111
112 public void setMaxCachedFiles(int maxCachedFiles)
113 {
114 _maxCachedFiles = maxCachedFiles;
115 }
116
117
118 public void flushCache()
119 {
120 if (_cache!=null)
121 {
122 synchronized(this)
123 {
124 ArrayList<Content> values=new ArrayList<Content>(_cache.values());
125 for (Content content : values)
126 content.invalidate();
127
128 _cache.clear();
129
130 _cachedSize=0;
131 _cachedFiles=0;
132 _mostRecentlyUsed=null;
133 _leastRecentlyUsed=null;
134 }
135 }
136 }
137
138
139
140
141
142
143
144
145
146
147 public Content lookup(String pathInContext, ResourceFactory factory)
148 throws IOException
149 {
150 Content content=null;
151
152
153 synchronized(_cache)
154 {
155
156 content = (Content)_cache.get(pathInContext);
157
158 if (content!=null && content.isValid())
159 {
160 return content;
161 }
162 }
163 Resource resource=factory.getResource(pathInContext);
164 return load(pathInContext,resource);
165 }
166
167
168 public Content lookup(String pathInContext, Resource resource)
169 throws IOException
170 {
171 Content content=null;
172
173
174 synchronized(_cache)
175 {
176
177 content = (Content)_cache.get(pathInContext);
178
179 if (content!=null && content.isValid())
180 {
181 return content;
182 }
183 }
184 return load(pathInContext,resource);
185 }
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 int must_be_smaller_than=_maxCacheSize-(int)len;
198
199 synchronized(_cache)
200 {
201
202
203 while(_leastRecentlyUsed!=null && (_cachedSize>must_be_smaller_than || (_maxCachedFiles>0 && _cachedFiles>=_maxCachedFiles)))
204 _leastRecentlyUsed.invalidate();
205
206 if(_cachedSize>must_be_smaller_than || (_maxCachedFiles>0 && _cachedFiles>=_maxCachedFiles))
207 return null;
208 }
209
210 content = new Content(resource);
211 fill(content);
212
213 synchronized(_cache)
214 {
215
216 Content content2 =(Content)_cache.get(pathInContext);
217 if (content2!=null)
218 {
219 content.release();
220 return content2;
221 }
222
223 while(_leastRecentlyUsed!=null && (_cachedSize>must_be_smaller_than || (_maxCachedFiles>0 && _cachedFiles>=_maxCachedFiles)))
224 _leastRecentlyUsed.invalidate();
225
226 if(_cachedSize>must_be_smaller_than || (_maxCachedFiles>0 && _cachedFiles>=_maxCachedFiles))
227 return null;
228
229 content.cache(pathInContext);
230
231 return content;
232 }
233 }
234 }
235
236 return null;
237 }
238
239
240
241
242
243
244
245 public void miss(String pathInContext, Resource resource)
246 throws IOException
247 {
248 synchronized(_cache)
249 {
250 while(_maxCachedFiles>0 && _cachedFiles>=_maxCachedFiles && _leastRecentlyUsed!=null)
251 _leastRecentlyUsed.invalidate();
252 if (_maxCachedFiles>0 && _cachedFiles>=_maxCachedFiles)
253 return;
254
255
256 Miss miss = new Miss(resource);
257 Content content2 =(Content)_cache.get(pathInContext);
258 if (content2!=null)
259 {
260 miss.release();
261 return;
262 }
263
264 miss.cache(pathInContext);
265 }
266 }
267
268
269 public synchronized void doStart()
270 throws Exception
271 {
272 _cache.clear();
273 _cachedSize=0;
274 _cachedFiles=0;
275 }
276
277
278
279
280 public void doStop()
281 throws InterruptedException
282 {
283 flushCache();
284 }
285
286
287 protected void fill(Content content)
288 throws IOException
289 {
290 try
291 {
292 InputStream in = content.getResource().getInputStream();
293 int len=(int)content.getResource().length();
294 Buffer buffer = new ByteArrayBuffer(len);
295 buffer.readFrom(in,len);
296 in.close();
297 content.setBuffer(buffer);
298 }
299 finally
300 {
301 content.getResource().release();
302 }
303 }
304
305
306
307
308
309 public class Content implements HttpContent
310 {
311 final Resource _resource;
312 final long _lastModified;
313 boolean _locked;
314 String _key;
315 Content _prev;
316 Content _next;
317
318 Buffer _lastModifiedBytes;
319 Buffer _contentType;
320 Buffer _buffer;
321
322
323 Content(Resource resource)
324 {
325 _resource=resource;
326
327 _next=this;
328 _prev=this;
329 _contentType=_mimeTypes.getMimeByExtension(_resource.toString());
330
331 _lastModified=resource.lastModified();
332 }
333
334
335
336
337
338
339 public boolean isLocked()
340 {
341 return _locked;
342 }
343
344
345
346
347
348
349 public void setLocked(boolean locked)
350 {
351 synchronized (_cache)
352 {
353 if (_locked && !locked)
354 {
355 _locked = locked;
356 _next=_mostRecentlyUsed;
357 _mostRecentlyUsed=this;
358 if (_next!=null)
359 _next._prev=this;
360 _prev=null;
361 if (_leastRecentlyUsed==null)
362 _leastRecentlyUsed=this;
363 }
364 else if (!_locked && locked)
365 {
366 if (_prev!=null)
367 _prev._next=_next;
368 if (_next!=null)
369 _next._prev=_prev;
370 _next=_prev=null;
371 }
372 else
373 _locked = locked;
374 }
375 }
376
377
378
379 void cache(String pathInContext)
380 {
381 _key=pathInContext;
382
383 if (!_locked)
384 {
385 _next=_mostRecentlyUsed;
386 _mostRecentlyUsed=this;
387 if (_next!=null)
388 _next._prev=this;
389 _prev=null;
390 if (_leastRecentlyUsed==null)
391 _leastRecentlyUsed=this;
392 }
393 _cache.put(_key,this);
394 if (_buffer!=null)
395 _cachedSize+=_buffer.length();
396 _cachedFiles++;
397 if (_lastModified!=-1)
398 _lastModifiedBytes=new ByteArrayBuffer(HttpFields.formatDate(_lastModified));
399 }
400
401
402 public String getKey()
403 {
404 return _key;
405 }
406
407
408 public boolean isCached()
409 {
410 return _key!=null;
411 }
412
413
414 public Resource getResource()
415 {
416 return _resource;
417 }
418
419
420 boolean isValid()
421 {
422 if (_lastModified==_resource.lastModified())
423 {
424 if (!_locked && _mostRecentlyUsed!=this)
425 {
426 Content tp = _prev;
427 Content tn = _next;
428
429 _next=_mostRecentlyUsed;
430 _mostRecentlyUsed=this;
431 if (_next!=null)
432 _next._prev=this;
433 _prev=null;
434
435 if (tp!=null)
436 tp._next=tn;
437 if (tn!=null)
438 tn._prev=tp;
439
440 if (_leastRecentlyUsed==this && tp!=null)
441 _leastRecentlyUsed=tp;
442 }
443 return true;
444 }
445
446 invalidate();
447 return false;
448 }
449
450
451 public void invalidate()
452 {
453 synchronized(this)
454 {
455
456 _cache.remove(_key);
457 _key=null;
458 if (_buffer!=null)
459 _cachedSize=_cachedSize-_buffer.length();
460 _cachedFiles--;
461
462 if (_mostRecentlyUsed==this)
463 _mostRecentlyUsed=_next;
464 else
465 _prev._next=_next;
466
467 if (_leastRecentlyUsed==this)
468 _leastRecentlyUsed=_prev;
469 else
470 _next._prev=_prev;
471
472 _prev=null;
473 _next=null;
474 _resource.release();
475
476 }
477 }
478
479
480 public Buffer getLastModified()
481 {
482 return _lastModifiedBytes;
483 }
484
485
486 public Buffer getContentType()
487 {
488 return _contentType;
489 }
490
491
492 public void setContentType(Buffer type)
493 {
494 _contentType=type;
495 }
496
497
498 public void release()
499 {
500 }
501
502
503 public Buffer getBuffer()
504 {
505 if (_buffer==null)
506 return null;
507 return new View(_buffer);
508 }
509
510
511 public void setBuffer(Buffer buffer)
512 {
513 _buffer=buffer;
514 }
515
516
517 public long getContentLength()
518 {
519 if (_buffer==null)
520 return -1;
521 return _buffer.length();
522 }
523
524
525 public InputStream getInputStream() throws IOException
526 {
527 return _resource.getInputStream();
528 }
529
530
531 public String toString()
532 {
533 return "{"+_resource+","+_contentType+","+_lastModifiedBytes+"}";
534 }
535
536
537 }
538
539
540
541
542
543
544 public class Miss extends Content
545 {
546 Miss(Resource resource)
547 {
548 super(resource);
549 }
550
551
552 boolean isValid()
553 {
554 if (_resource.exists())
555 {
556 invalidate();
557 return false;
558 }
559 return true;
560 }
561 }
562 }