Changeset 4827
- Timestamp:
- Mar 23, 2009, 1:09:35 PM (15 years ago)
- Location:
- trunk
- Files:
-
- 1 added
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/config/dist/base.config
r4637 r4827 155 155 cache.timeout = 20 156 156 157 # If the static cache should be disabled or enabled 158 # Disabling the static cache may reduce performance for 159 # certain operations 160 cache.static.disabled = false 161 162 # Timeout (in days) for items in the static cache 163 # Items that hasn't been accessed in the configured amount of 164 # of time will be removed from the cache 165 cache.static.max-age = 30 166 157 167 #Overwrite the existing help texts when updating the program 158 168 helptext.update = true -
trunk/doc/src/docbook/appendix/base.config.xml
r4637 r4827 592 592 </listitem> 593 593 </varlistentry> 594 595 <varlistentry> 596 <term><property>cache.static.disabled</property></term> 597 <listitem> 598 <para> 599 If the static cache should be enabled or disabled. It is enabled by 600 default. Disabling the static cache may reduce performance in some 601 cases. The static cache is used to cache processed information, 602 for example images, so that the database doesn't have to be queried 603 on every request. 604 </para> 605 </listitem> 606 </varlistentry> 607 608 <varlistentry> 609 <term><property>cache.static.max-age</property></term> 610 <listitem> 611 <para> 612 The maximum age in days of files in the static cache. Files that 613 hasn't been accessed (read or written) in the specified amount 614 of time are deleted. 615 </para> 616 </listitem> 617 </varlistentry> 618 594 619 <varlistentry> 595 620 <term><property>helptext.update</property></term> -
trunk/src/core/net/sf/basedb/core/Application.java
r4824 r4827 449 449 // Initialise other utility classes 450 450 staticCache = new StaticCache(new java.io.File(userFilesDirectory, "static.cache")); 451 staticCache.setDisabled(true); 451 452 QueryExecutor.init(); 452 453 HibernateUtil.init1(); … … 501 502 RawDataTypes.initPlatforms(); 502 503 503 // Adding a task that cleans the session control cache at regular interval e504 // Adding a task that cleans the session control cache at regular intervals 504 505 long milliSeconds = 60 * 1000 * sessionCacheTimeout; 505 506 getScheduler().schedule(new SessionControlCacheCleaner(), milliSeconds, milliSeconds, false); 507 508 // Adding a task that cleans the static cache at regular intervals 509 staticCache.setDisabled(Config.getBoolean("cache.static.disabled")); 510 if (!staticCache.isDisabled()) 511 { 512 long maxFileAge = 24L * 3600L * 1000L * Config.getLong("cache.static.max-age", 30); 513 long checkInterval = 12 * 3600 * 1000; // Once every twelve hours 514 getScheduler().schedule( 515 staticCache.cleanUpTask(staticCache.olderThan(maxFileAge)), 60000, checkInterval, false); 516 } 506 517 507 518 if (useInternalJobQueue == null) useInternalJobQueue = Config.getBoolean("jobqueue.internal.enabled"); -
trunk/src/core/net/sf/basedb/util/StaticCache.java
r4826 r4827 23 23 24 24 import java.io.File; 25 import java.io.FileFilter; 25 26 import java.io.FileInputStream; 26 27 import java.io.FileOutputStream; 28 import java.io.FilterInputStream; 29 import java.io.FilterOutputStream; 27 30 import java.io.IOException; 28 31 import java.io.InputStream; … … 31 34 import java.io.OutputStream; 32 35 import java.io.Serializable; 36 import java.lang.ref.WeakReference; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.TimerTask; 40 import java.util.WeakHashMap; 41 import java.util.concurrent.TimeUnit; 42 import java.util.concurrent.locks.Lock; 43 import java.util.concurrent.locks.ReadWriteLock; 44 import java.util.concurrent.locks.ReentrantReadWriteLock; 45 import java.util.regex.Pattern; 46 47 import net.sf.basedb.core.InvalidDataException; 33 48 34 49 /** … … 36 51 use case is to store data this is expensive to get from the database 37 52 in a file for later retrieval. The cache can be used in streaming mode 38 with the any of the {@link #read(String )} or {@link #write(String, InputStream)}53 with the any of the {@link #read(String, int)} or {@link #write(String, InputStream, int)} 39 54 methods and their variants. It can also be used to store any {@link Serializable} 40 object with {@link #store(String, Serializable )} and {@link #load(String)}.55 object with {@link #store(String, Serializable, int)} and {@link #load(String, int)}. 41 56 <p> 57 42 58 In all cases the cached entry is identified by a key which is more or 43 59 less directly translated to directories on the file system. 44 60 <p> 45 61 62 This class is thread-safe and can be used by multiple threads at the same 63 time. Write requests to the same entry in the cache are allowed to one 64 thread at a time. Any number of threads may read from the same cache entry 65 as long as no thread is writing to that entry. 46 66 47 67 @author Nicklas … … 51 71 public class StaticCache 52 72 { 73 74 public static final Pattern validKey = Pattern.compile("[\\w\\/\\.\\-]+"); 75 76 /** 77 Log static cache events. 78 */ 79 private static final org.apache.log4j.Logger log = 80 org.apache.log4j.LogManager.getLogger("net.sf.basedb.util.StaticCache"); 81 82 /** 83 Checks if the given key is a vilid cache entry key. 84 Allowed characters are: any alphanumerical character + ./- 85 @param key The key to check 86 @return TRUE if the key is valid 87 */ 88 public static boolean isValidKey(String key) 89 { 90 return validKey.matcher(key).matches(); 91 } 92 53 93 private final File root; 94 private final Map<String, LockEntry> locks; 54 95 private boolean disabled; 96 55 97 56 98 /** … … 61 103 { 62 104 this.root = root; 105 this.locks = new WeakHashMap<String, LockEntry>(); 106 log.info("Creating static cache in directory " + root); 63 107 } 64 108 … … 79 123 public void setDisabled(boolean disabled) 80 124 { 125 if (disabled) 126 { 127 log.info("Disabling static cache in directory " + root); 128 } 129 else 130 { 131 log.info("Enabling static cache in directory " + root); 132 } 81 133 this.disabled = disabled; 82 134 } 83 135 84 136 /** 85 Store information in the cache. If the entry already exists, 137 Remove all files that matches the specified filter from 138 the cache. This method is synchronized and can only 139 be executed by one thread at a time. 140 141 @param filter A file filter that matches the files to 142 remove 143 */ 144 public synchronized void cleanUp(FileFilter filter) 145 { 146 log.info("Cleaning up static cache: " + root); 147 List<File> oldFiles = FileUtil.findFiles(root, filter); 148 if (oldFiles == null) return; 149 150 log.debug("Found " + oldFiles.size() + " files that should be deleted"); 151 int numDeleted = 0; 152 for (File f : oldFiles) 153 { 154 if (f.delete()) 155 { 156 log.debug("Removed cached file: " + f); 157 numDeleted++; 158 } 159 else 160 { 161 log.warn("Failed to remove cached file: " + f); 162 } 163 } 164 log.info("Removed " + numDeleted + " files from the static cache: " + root); 165 } 166 167 /** 168 Creates a file filter that matches all files that 169 are older than the specified age. 170 @param age The age in milliseconds 171 @return A file filter 172 @see OlderThanFileFilter 173 */ 174 public FileFilter olderThan(long age) 175 { 176 return new OlderThanFileFilter(age, true); 177 } 178 179 /** 180 Creates a task that cleans up this cache when it is 181 executed. 182 @param filter The file filter that determines 183 which file that should be deleted 184 @return A TimerTask object 185 @see #cleanUp(FileFilter) 186 */ 187 public TimerTask cleanUpTask(FileFilter filter) 188 { 189 return new CleanupTask(this, filter); 190 } 191 192 /** 193 Store binary information in the cache. If the entry already exists, 86 194 it is overwritten. If the entry is locked by other threads, 87 this thread wai 195 this thread waits the specified number of milliseconds before 196 returning. 88 197 89 198 @param key The cache key 90 199 @param in An input stream to read from 200 @param timeout A timeout in milliseconds to wait for a write lock 201 on the requested cache entry 91 202 @return The number of bytes written 92 203 */ 93 public long write(String key, InputStream in )204 public long write(String key, InputStream in, int timeout) 94 205 throws IOException 95 206 { 96 207 if (disabled) return 0; 97 OutputStream out = getOutputStream(key); 98 long numBytes = FileUtil.copy(in, out); 99 out.flush(); 100 out.close(); 208 long numBytes = 0; 209 OutputStream out = null; 210 try 211 { 212 out = getOutputStream(key, timeout); 213 if (out != null) 214 { 215 numBytes = FileUtil.copy(in, out); 216 out.flush(); 217 } 218 } 219 finally 220 { 221 if (out != null) out.close(); 222 } 101 223 return numBytes; 102 224 } 103 225 104 public OutputStream write(String key) 226 /** 227 Get an output stream that can be used to write binary information 228 to the cache. If the entry already exists, it is overwritten. 229 If the entry is locked by other threads, this thread waits the 230 specified number of milliseconds before giving up. 231 <p> 232 NOTE! It is very important that the caller closes the 233 output stream as soon as all data has been written to it. 234 Failure to do so may result in locking the cache entry from 235 reading by other threads. 236 237 @param key The cache key 238 @param timeout A timeout in milliseconds to wait for a write lock 239 on the requested cache entry 240 @return An output stream, or null if a write lock could not 241 be aquired 242 */ 243 public OutputStream write(String key, int timeout) 105 244 throws IOException 106 245 { 107 246 if (disabled) return null; 108 return getOutputStream(key); 109 } 110 111 112 public long read(String key, OutputStream out) 247 return getOutputStream(key, timeout); 248 } 249 250 /** 251 Read binary information from the cache. The contents of the 252 specified cache entry will be copied to the specified output 253 stream. 254 255 @param key The cache key 256 @param out An output stream to write the cache contents to 257 @param timeout A timeout in milliseconds to wait for a 258 read lock on the requested cache entry 259 @return The number of bytes copied 260 @throws IOException 261 */ 262 public long read(String key, OutputStream out, int timeout) 113 263 throws IOException 114 264 { 115 265 if (disabled) return 0; 116 InputStream in = getInputStream(key);117 266 long numBytes = 0; 118 if (in != null) FileUtil.copy(in, out); 267 InputStream in = null; 268 try 269 { 270 in = getInputStream(key, timeout); 271 if (in != null) 272 { 273 numBytes = FileUtil.copy(in, out); 274 } 275 } 276 finally 277 { 278 if (in != null) in.close(); 279 } 119 280 return numBytes; 120 281 } 121 282 122 public InputStream read(String key) 283 /** 284 Get an input stream for reading binary information from the cache. 285 286 @param key The cache key 287 @param timeout A timeout in milliseconds to wait for a 288 read lock on the requested cache entry 289 @return An input stream or null if the cache entry doesn't exists 290 or if a read lock could not be aquired 291 @throws IOException 292 */ 293 public InputStream read(String key, int timeout) 123 294 throws IOException 124 295 { 125 296 if (disabled) return null; 126 return getInputStream(key); 127 } 128 129 public boolean store(String key, Serializable object) 297 return getInputStream(key, timeout); 298 } 299 300 /** 301 Store a serializable object in the cache. If the entry already exists, 302 it is overwritten. If the entry is locked by other threads, 303 this thread waits the specified number of milliseconds before 304 returning. 305 306 @param key The cache key 307 @param object The object to store in the cache 308 @param timeout A timeout in milliseconds to wait for a write lock 309 on the requested cache entry 310 @return TRUE if the object could be stored, FALSE otherwise 311 */ 312 public boolean store(String key, Serializable object, int timeout) 130 313 throws IOException 131 314 { 132 315 if (disabled) return false; 133 ObjectOutputStream out = new ObjectOutputStream(getOutputStream(key)); 134 out.writeObject(object); 135 out.flush(); 136 out.close(); 316 OutputStream out = null; 317 try 318 { 319 out = getOutputStream(key, timeout); 320 if (out != null) 321 { 322 ObjectOutputStream oos = new ObjectOutputStream(out); 323 oos.writeObject(object); 324 oos.flush(); 325 } 326 } 327 finally 328 { 329 if (out != null) out.close(); 330 } 137 331 return true; 138 332 } 139 333 140 public Serializable load(String key) 334 /** 335 Read a serializable object from the cache. 336 337 @param key The cache key 338 @param timeout A timeout in milliseconds to wait for a read lock 339 on the requested cache entry 340 @return The materialized object, or null if the cache entry doesn't 341 exists or if a read lock couldn't be aquired 342 */ 343 public Serializable load(String key, int timeout) 141 344 throws IOException 142 345 { 143 346 if (disabled) return null; 144 InputStream in = getInputStream(key);145 347 Serializable object = null; 146 if (in != null) 147 { 148 ObjectInputStream oin = new ObjectInputStream(in); 149 try 150 { 151 object = (Serializable)oin.readObject(); 152 } 153 catch (ClassNotFoundException ex) 154 { 155 throw new IOException(ex); 156 } 348 InputStream in = null; 349 try 350 { 351 in = getInputStream(key, timeout); 352 if (in != null) 353 { 354 ObjectInputStream oin = new ObjectInputStream(in); 355 try 356 { 357 object = (Serializable)oin.readObject(); 358 } 359 catch (ClassNotFoundException ex) 360 { 361 throw new IOException(ex); 362 } 363 } 364 } 365 finally 366 { 367 if (in != null) in.close(); 157 368 } 158 369 return object; … … 161 372 private void validateKey(String key) 162 373 { 163 164 } 165 166 private InputStream getInputStream(String key) 374 if (!isValidKey(key)) 375 { 376 throw new InvalidDataException("Invalid cache key: " + key); 377 } 378 } 379 380 /** 381 Get a lock-safe input stream. 382 */ 383 private InputStream getInputStream(String key, int timeout) 167 384 throws IOException 168 385 { 169 386 validateKey(key); 387 log.debug("Read request for static cache: " + key); 170 388 File f = new File(root, key); 171 if (!f.exists()) return null; 172 return new FileInputStream(f); 173 } 174 175 private OutputStream getOutputStream(String key) 389 if (!f.exists()) 390 { 391 log.debug("Cache entry doesn't exist: " + key); 392 return null; 393 } 394 LockEntry lock = aquireLock(key, false, timeout); 395 if (lock == null) return null; 396 InputStream in = null; 397 try 398 { 399 f.setLastModified(System.currentTimeMillis()); 400 in = new LockSafeInputStream(new FileInputStream(f), lock); 401 } 402 finally 403 { 404 if (in == null) lock.readLock().unlock(); 405 } 406 return in; 407 } 408 409 /** 410 Get a lock-safe output stream. 411 */ 412 private OutputStream getOutputStream(String key, int timeout) 176 413 throws IOException 177 414 { 178 415 validateKey(key); 179 File f = new File(root, key); 180 f.getParentFile().mkdirs(); 181 f.createNewFile(); 182 return new FileOutputStream(f); 183 } 184 185 416 log.debug("Write request for static cache: " + key); 417 LockEntry lock = aquireLock(key, true, timeout); 418 if (lock == null) return null; 419 OutputStream out = null; 420 try 421 { 422 File f = new File(root, key); 423 f.getParentFile().mkdirs(); 424 f.createNewFile(); 425 out = new LockSafeOutputStream(new FileOutputStream(f), lock); 426 } 427 finally 428 { 429 if (out == null) lock.writeLock().unlock(); 430 } 431 return out; 432 } 433 434 /** 435 Aquire a read or write lock on a given cache entry. 436 @return A lock manager or null if the lock could not be aquired 437 */ 438 private LockEntry aquireLock(String key, boolean writeLock, int timeout) 439 { 440 String lockType = writeLock ? "write" : "read"; 441 log.debug("Trying to get " + lockType + " lock on: " + key); 442 LockEntry rwLock = null; 443 synchronized (locks) 444 { 445 log.debug("Number of known lock entries: " + locks.size()); 446 rwLock = locks.get(key); 447 if (rwLock == null) 448 { 449 log.debug("Create new lock holder for: " + key); 450 rwLock = new LockEntry(new ReentrantReadWriteLock(), key); 451 locks.put(key, rwLock); 452 } 453 } 454 455 Lock lock = writeLock ? rwLock.writeLock() : rwLock.readLock(); 456 try 457 { 458 if (!lock.tryLock(timeout, TimeUnit.MILLISECONDS)) 459 { 460 log.debug("Timeout while waiting for " + lockType + " lock on: " + key); 461 rwLock = null; 462 } 463 } 464 catch (InterruptedException ex) 465 { 466 log.debug("Interrupted while waiting for " + lockType + " lock on: " + key); 467 rwLock = null; 468 } 469 if (rwLock != null) log.debug("Got " + lockType + " lock on: " + key); 470 return rwLock; 471 } 472 473 /** 474 A lock-safe output stream that releases the associated 475 write lock when the stream is closed. 476 <p> 477 NOTE! It is important that we keep a strong reference 478 to the 'key' in this class until the stream is closed, 479 since otherwise the locked entry may be reclaimed by the 480 garbage collector. 481 */ 482 static class LockSafeOutputStream 483 extends FilterOutputStream 484 { 485 private final Throwable calledFrom; 486 private ReadWriteLock lock; 487 private String key; 488 private boolean closed; 489 490 LockSafeOutputStream(OutputStream out, LockEntry lock) 491 { 492 super(out); 493 this.lock = lock; 494 this.key = lock.getKey(); 495 this.calledFrom = new Throwable(); 496 } 497 498 @Override 499 public void close() 500 throws IOException 501 { 502 if (closed) return; 503 log.debug("Releasing write lock on: " + key); 504 lock.writeLock().unlock(); 505 closed = true; 506 lock = null; 507 key = null; 508 super.close(); 509 } 510 511 @Override 512 protected void finalize() 513 throws Throwable 514 { 515 if (!closed) 516 { 517 log.warn("Found unreleased write lock on: " + key, calledFrom); 518 close(); 519 } 520 super.finalize(); 521 } 522 523 @Override 524 public String toString() 525 { 526 return "LockSafeOutputStream[" + key + "]"; 527 } 528 529 } 530 531 532 /** 533 A lock-safe input stream that releases the associated 534 read lock when the stream is closed. 535 <p> 536 NOTE! It is important that we keep a strong reference 537 to the 'key' in this class until the stream is closed, 538 since otherwise the locked entry may be reclaimed by the 539 garbage collector. 540 */ 541 static class LockSafeInputStream 542 extends FilterInputStream 543 { 544 545 private final Throwable calledFrom; 546 private ReadWriteLock lock; 547 private String key; 548 private boolean closed; 549 550 LockSafeInputStream(InputStream in, LockEntry lock) 551 { 552 super(in); 553 this.lock = lock; 554 this.key = lock.getKey(); 555 this.calledFrom = new Throwable(); 556 } 557 558 @Override 559 public void close() 560 throws IOException 561 { 562 if (closed) return; 563 log.debug("Releasing read lock on: " + key); 564 lock.readLock().unlock(); 565 closed = true; 566 lock = null; 567 key = null; 568 super.close(); 569 } 570 571 @Override 572 protected void finalize() 573 throws Throwable 574 { 575 if (!closed) 576 { 577 log.warn("Found unreleased read lock on: " + key, calledFrom); 578 close(); 579 } 580 super.finalize(); 581 } 582 583 @Override 584 public String toString() 585 { 586 return "LockSafeInputStream[" + key + "]"; 587 } 588 } 589 590 /** 591 Keeps track of a locked cached entry. The 'key' is the same key 592 instance that was used to create the entry in the first place. 593 It is important that all active maintainers of a lock also 594 keeps a strong reference to the key since the lock entry may 595 otherwise be reclaimed by the garbage collector. 596 */ 597 static class LockEntry 598 implements ReadWriteLock 599 { 600 private ReadWriteLock rwLock; 601 private WeakReference<String> keyRef; 602 603 LockEntry(ReadWriteLock rwLock, String key) 604 { 605 this.rwLock = rwLock; 606 this.keyRef = new WeakReference<String>(key); 607 } 608 609 @Override 610 public Lock writeLock() 611 { 612 return rwLock.writeLock(); 613 } 614 615 @Override 616 public Lock readLock() 617 { 618 return rwLock.readLock(); 619 } 620 621 public String getKey() 622 { 623 return keyRef.get(); 624 } 625 626 @Override 627 public String toString() 628 { 629 return "LockEntry[" + getKey() + "]"; 630 } 631 } 632 633 /** 634 A timer task that clean up the cache when it is executed. 635 */ 636 public class CleanupTask 637 extends TimerTask 638 { 639 640 private final StaticCache cache; 641 private final FileFilter filter; 642 643 public CleanupTask(StaticCache cache, FileFilter filter) 644 { 645 this.cache = cache; 646 this.filter = filter; 647 } 648 649 @Override 650 public void run() 651 { 652 cache.cleanUp(filter); 653 } 654 } 186 655 }
Note: See TracChangeset
for help on using the changeset viewer.