1 25 package org.archive.util; 26 27 import java.io.File ; 28 import java.io.IOException ; 29 import java.io.Serializable ; 30 import java.lang.ref.PhantomReference ; 31 import java.lang.ref.Reference ; 32 import java.lang.ref.ReferenceQueue ; 33 import java.lang.ref.SoftReference ; 34 import java.lang.reflect.Field ; 35 import java.util.AbstractMap ; 36 import java.util.HashMap ; 37 import java.util.Iterator ; 38 import java.util.LinkedList ; 39 import java.util.Map ; 40 import java.util.Set ; 41 import java.util.logging.Level ; 42 import java.util.logging.Logger ; 43 44 import com.sleepycat.bind.EntryBinding; 45 import com.sleepycat.bind.serial.SerialBinding; 46 import com.sleepycat.bind.serial.StoredClassCatalog; 47 import com.sleepycat.bind.tuple.TupleBinding; 48 import com.sleepycat.collections.StoredSortedMap; 49 import com.sleepycat.je.Database; 50 import com.sleepycat.je.DatabaseConfig; 51 import com.sleepycat.je.DatabaseException; 52 import com.sleepycat.je.Environment; 53 import com.sleepycat.je.EnvironmentConfig; 54 55 66 public class CachedBdbMap<K,V> extends AbstractMap <K,V> 67 implements Map <K,V>, Serializable { 68 69 private static final long serialVersionUID = -8655539411367047332L; 70 71 private static final Logger logger = 72 Logger.getLogger(CachedBdbMap.class.getName()); 73 74 75 private static final String CLASS_CATALOG = "java_class_catalog"; 76 77 81 private static final Map <String ,DbEnvironmentEntry> dbEnvironmentMap = 82 new HashMap <String ,DbEnvironmentEntry>(); 83 84 86 private transient DbEnvironmentEntry dbEnvironment; 87 88 89 protected transient Database db; 90 91 92 protected transient StoredSortedMap diskMap; 93 94 95 private transient Map <K,SoftEntry<V>> memMap; 96 97 protected transient ReferenceQueue <V> refQueue; 98 99 101 protected int diskMapSize = 0; 102 103 106 private long cacheHit = 0; 107 108 111 private long countOfGets = 0; 112 113 117 private long diskHit = 0; 118 119 122 private String dbName = null; 123 124 127 protected static Field referentField; 128 static { 129 try { 135 referentField = Reference .class.getDeclaredField("referent"); 136 referentField.setAccessible(true); 137 } catch (SecurityException e) { 138 throw new RuntimeException (e); 139 } catch (NoSuchFieldException e) { 140 throw new RuntimeException (e); 141 } 142 } 143 144 147 protected static class DbEnvironmentEntry { 148 Environment environment; 149 StoredClassCatalog classCatalog; 150 int openDbCount = 0; 151 File dbDir; 152 } 153 154 157 private CachedBdbMap() { 158 super(); 159 } 160 161 172 public CachedBdbMap(final String dbName) { 173 this(); 174 this.dbName = dbName; 175 } 176 177 197 public CachedBdbMap(final File dbDir, final String dbName, 198 final Class <K> keyClass, final Class <V> valueClass) 199 throws DatabaseException { 200 this(dbName); 201 this.dbEnvironment = getDbEnvironment(dbDir); 202 this.dbEnvironment.openDbCount++; 203 initialize(dbEnvironment.environment, keyClass, valueClass, 204 dbEnvironment.classCatalog); 205 if (logger.isLoggable(Level.INFO)) { 206 EnvironmentConfig cfg = this.dbEnvironment.environment.getConfig(); 208 logger.info("BdbConfiguration: Cache percentage " + 209 cfg.getCachePercent() + ", cache size " + cfg.getCacheSize() + 210 ", Map size: " + size()); 211 } 212 } 213 214 226 public synchronized void initialize(final Environment env, final Class keyClass, 227 final Class valueClass, final StoredClassCatalog classCatalog) 228 throws DatabaseException { 229 initializeInstance(); 230 this.db = openDatabase(env, this.dbName); 231 this.diskMap = createDiskMap(this.db, classCatalog, keyClass, 232 valueClass); 233 } 234 235 239 protected void initializeInstance() { 240 this.memMap = new HashMap <K,SoftEntry<V>>(); 241 this.refQueue = new ReferenceQueue <V>(); 242 } 243 244 protected StoredSortedMap createDiskMap(Database database, 245 StoredClassCatalog classCatalog, Class keyClass, Class valueClass) { 246 EntryBinding keyBinding = TupleBinding.getPrimitiveBinding(keyClass); 247 if(keyBinding == null) { 248 keyBinding = new SerialBinding(classCatalog, keyClass); 249 } 250 EntryBinding valueBinding = TupleBinding.getPrimitiveBinding(valueClass); 251 if(valueBinding == null) { 252 valueBinding = new SerialBinding(classCatalog, valueClass); 253 } 254 return new StoredSortedMap(database, keyBinding, valueBinding, true); 255 } 256 257 268 private DbEnvironmentEntry getDbEnvironment(File dbDir) { 269 if (dbEnvironmentMap.containsKey(dbDir.getAbsolutePath())) { 270 return (DbEnvironmentEntry) dbEnvironmentMap.get(dbDir 271 .getAbsolutePath()); 272 } 273 EnvironmentConfig envConfig = new EnvironmentConfig(); 274 envConfig.setAllowCreate(true); 275 envConfig.setTransactional(false); 276 277 envConfig.setCachePercent(1); 280 DbEnvironmentEntry env = new DbEnvironmentEntry(); 281 try { 282 env.environment = new Environment(dbDir, envConfig); 283 env.dbDir = dbDir; 284 dbEnvironmentMap.put(dbDir.getAbsolutePath(), env); 285 286 DatabaseConfig dbConfig = new DatabaseConfig(); 287 dbConfig.setTransactional(false); 288 dbConfig.setAllowCreate(true); 289 290 Database catalogDb = env.environment.openDatabase(null, 291 CLASS_CATALOG, dbConfig); 292 293 env.classCatalog = new StoredClassCatalog(catalogDb); 294 } catch (DatabaseException e) { 295 e.printStackTrace(); 296 } 298 return env; 299 } 300 301 protected Database openDatabase(final Environment environment, 302 final String dbName) throws DatabaseException { 303 DatabaseConfig dbConfig = new DatabaseConfig(); 304 dbConfig.setTransactional(false); 305 dbConfig.setAllowCreate(true); 306 return environment.openDatabase(null, dbName, dbConfig); 307 } 308 309 public synchronized void close() throws DatabaseException { 310 if (this.db != null) { 312 try { 313 this.db.close(); 314 } catch (DatabaseException e) { 315 e.printStackTrace(); 316 } finally { 317 this.db = null; 318 } 319 } 320 if (dbEnvironment != null) { 321 dbEnvironment.openDbCount--; 322 if (dbEnvironment.openDbCount <= 0) { 323 dbEnvironment.classCatalog.close(); 324 dbEnvironment.environment.close(); 325 dbEnvironmentMap.remove(dbEnvironment.dbDir.getAbsolutePath()); 326 dbEnvironment = null; 327 } 328 } 329 } 330 331 protected void finalize() throws Throwable { 332 close(); 333 super.finalize(); 334 } 335 336 341 @SuppressWarnings ("unchecked") 342 public Set <K> keySet() { 343 return diskMap.keySet(); 344 } 345 346 public Set <Map.Entry <K,V>> entrySet() { 347 throw new UnsupportedOperationException (); 350 } 351 352 public synchronized V get(final Object object) { 353 K key = toKey(object); 354 countOfGets++; 355 expungeStaleEntries(); 356 if (countOfGets % 10000 == 0) { 357 logCacheSummary(); 358 } 359 SoftEntry<V> entry = memMap.get(key); 360 if (entry != null) { 361 V val = entry.get(); if (val != null) { 363 cacheHit++; 364 return val; 365 } 366 expungeStaleEntry(entry); 369 } 370 371 V v = diskMapGet(key); 373 if (v != null) { 374 diskHit++; 375 memMap.put(key, new SoftEntry<V>(key, v, refQueue)); 376 } 377 return v; 378 } 379 380 383 private void logCacheSummary() { 384 if (!logger.isLoggable((Level.FINE))) { 385 return; 386 } 387 try { 388 long cacheHitPercent = (cacheHit * 100) / (cacheHit + diskHit); 389 logger.fine("DB name: " + this.db.getDatabaseName() 390 + ", Cache Hit: " + cacheHitPercent 391 + "%, Not in map: " + (countOfGets - (cacheHit + diskHit)) 392 + ", Total number of gets: " + countOfGets); 393 } catch (DatabaseException e) { 394 } 396 } 397 398 public synchronized V put(K key, V value) { 399 V prevVal = get(key); 400 memMap.put(key, new SoftEntry<V>(key, value, refQueue)); 401 diskMap.put(key,value); if(prevVal==null) { 403 diskMapSize++; 404 } 405 return prevVal; 406 } 407 408 414 public synchronized void clear() { 415 this.memMap.clear(); 416 this.diskMap.clear(); 417 this.diskMapSize = 0; 418 try { 419 close(); 420 } catch (DatabaseException e) { 421 e.printStackTrace(); 422 } 423 } 424 425 public synchronized V remove(final Object key) { 426 V prevValue = get(key); 427 memMap.remove(key); 428 expungeStaleEntries(); 429 diskMap.remove(key); 430 diskMapSize--; 431 return prevValue; 432 } 433 434 public synchronized boolean containsKey(Object key) { 435 if (quickContainsKey(key)) { 436 return true; 437 } 438 return diskMap.containsKey(key); 439 } 440 441 public synchronized boolean quickContainsKey(Object key) { 442 expungeStaleEntries(); 443 return memMap.containsKey(key); 444 } 445 446 public synchronized boolean containsValue(Object value) { 447 if (quickContainsValue(value)) { 448 return true; 449 } 450 return diskMap.containsValue(value); 451 } 452 453 public synchronized boolean quickContainsValue(Object value) { 454 expungeStaleEntries(); 455 return memMap.containsValue(value); 457 } 458 459 public int size() { 460 return diskMapSize; 461 } 462 463 protected String getDatabaseName() { 464 String name = "DbName-Lookup-Failed"; 465 try { 466 if (this.db != null) { 467 name = this.db.getDatabaseName(); 468 } 469 } catch (DatabaseException e) { 470 } 472 return name; 473 } 474 475 480 public synchronized void sync() { 481 String dbName = null; 482 long startTime = 0; 484 if (logger.isLoggable(Level.INFO)) { 485 dbName = getDatabaseName(); 486 startTime = System.currentTimeMillis(); 487 logger.info(dbName + " start sizes: disk " + this.diskMapSize + 488 ", mem " + this.memMap.size()); 489 } 490 expungeStaleEntries(); 491 LinkedList <SoftEntry> stale = new LinkedList <SoftEntry>(); 492 for (Iterator i = this.memMap.keySet().iterator(); i.hasNext();) { 493 Object key = i.next(); 494 SoftEntry entry = (SoftEntry) memMap.get(key); 495 if (entry != null) { 496 Object value = entry.get(); 498 if (value != null) { 499 this.diskMap.put(key, value); 500 } else { 501 stale.add(entry); 502 } 503 } 504 } 505 for (SoftEntry entry : stale) { 507 expungeStaleEntry(entry); 508 } 509 510 if (logger.isLoggable(Level.INFO)) { 511 logger.info(dbName + " sync took " + 512 (System.currentTimeMillis() - startTime) + "ms. " + 513 "Finish sizes: disk " + 514 this.diskMapSize + ", mem " + this.memMap.size()); 515 } 516 } 517 518 private void expungeStaleEntries() { 519 int c = 0; 520 for(SoftEntry entry; (entry = refQueuePoll()) != null;) { 521 expungeStaleEntry(entry); 522 c++; 523 } 524 if (c > 0 && logger.isLoggable(Level.FINER)) { 525 try { 526 logger.finer("DB: " + db.getDatabaseName() + ", Expunged: " 527 + c + ", Diskmap size: " + diskMapSize 528 + ", Cache size: " + memMap.size()); 529 } catch (DatabaseException e) { 530 } 532 } 533 } 534 535 private void expungeStaleEntry(SoftEntry entry) { 536 if (entry.getPhantom() == null) { 542 return; 543 } 544 if (memMap.get(entry.getPhantom().getKey()) == entry) { 547 memMap.remove(entry.getPhantom().getKey()); 548 diskMap.put(entry.getPhantom().getKey(), 549 entry.getPhantom().doctoredGet()); 550 } 551 entry.clearPhantom(); 552 } 553 554 private class PhantomEntry<T> extends PhantomReference <T> { 555 private final Object key; 556 557 public PhantomEntry(Object key, T referent) { 558 super(referent, null); 559 this.key = key; 560 } 561 562 568 public Object doctoredGet() { 569 try { 570 return referentField.get(this); 574 } catch (IllegalAccessException e) { 575 throw new RuntimeException (e); 576 } 577 } 578 579 582 public Object getKey() { 583 return this.key; 584 } 585 } 586 587 private class SoftEntry<T> extends SoftReference <T> { 588 private PhantomEntry<T> phantom; 589 590 public SoftEntry(Object key, T referent, ReferenceQueue <T> q) { 591 super(referent, q); 592 this.phantom = new PhantomEntry<T>(key, referent); 593 } 594 595 598 public PhantomEntry getPhantom() { 599 return this.phantom; 600 } 601 602 public void clearPhantom() { 603 this.phantom.clear(); 604 this.phantom = null; 605 super.clear(); 606 } 607 } 608 609 private void readObject(java.io.ObjectInputStream stream) 610 throws IOException , ClassNotFoundException { 611 stream.defaultReadObject(); 612 initializeInstance(); 613 if (logger.isLoggable(Level.FINE)) { 614 logger.fine(getDatabaseName() + " diskMapSize: " + diskMapSize); 615 } 616 } 617 618 619 620 @SuppressWarnings ("unchecked") 621 private K toKey(Object o) { 622 return (K)o; 623 } 624 625 @SuppressWarnings ("unchecked") 626 private V diskMapGet(K k) { 627 return (V)diskMap.get(k); 628 } 629 630 @SuppressWarnings ("unchecked") 631 private SoftEntry<V> refQueuePoll() { 632 return (SoftEntry)refQueue.poll(); 633 } 634 } 635 | Popular Tags |