1 package org.jboss.cache.loader.jdbm; 2 3 import jdbm.RecordManager; 4 import jdbm.RecordManagerFactory; 5 import jdbm.btree.BTree; 6 import jdbm.helper.Tuple; 7 import jdbm.helper.TupleBrowser; 8 import org.apache.commons.logging.Log; 9 import org.apache.commons.logging.LogFactory; 10 import org.jboss.cache.CacheSPI; 11 import org.jboss.cache.Fqn; 12 import org.jboss.cache.FqnComparator; 13 import org.jboss.cache.Modification; 14 import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; 15 import org.jboss.cache.loader.AbstractCacheLoader; 16 17 import java.io.File ; 18 import java.io.IOException ; 19 import java.io.Serializable ; 20 import java.util.Collections ; 21 import java.util.HashMap ; 22 import java.util.HashSet ; 23 import java.util.Iterator ; 24 import java.util.List ; 25 import java.util.Map ; 26 import java.util.Properties ; 27 import java.util.Set ; 28 import java.util.concurrent.ConcurrentHashMap ; 29 30 61 public class JdbmCacheLoader extends AbstractCacheLoader 62 { 63 private static final Log log = LogFactory.getLog(JdbmCacheLoader.class); 64 65 private static final String KEYS = "K"; 66 private static final String NODE = "N"; 67 private static final String NAME = "JdbmCacheLoader"; 68 69 private JdbmCacheLoaderConfig config; 70 private String cacheDbName; 71 private RecordManager recman; 72 private BTree tree; 73 private Map <Object , List <Modification>> transactions = new ConcurrentHashMap <Object , List <Modification>>(); 74 75 79 80 public void create() throws Exception 81 { 82 checkNotOpen(); 83 } 84 85 public void destroy() 86 { 87 } 88 89 93 public void start() 94 throws Exception 95 { 96 97 log.trace("Starting JdbmCacheLoader instance."); 98 checkNotOpen(); 99 checkNonNull(cache, "CacheSPI object is required"); 100 101 String locationStr = config.getLocation(); 102 if (locationStr == null) 103 { 104 locationStr = System.getProperty("java.io.tmpdir"); 105 config.setLocation(locationStr); 106 } 107 108 File location = new File (locationStr); 110 if (!location.exists()) 111 { 112 boolean created = location.mkdirs(); 113 if (!created) throw new IOException ("Unable to create cache loader location " + location); 114 115 } 116 if (!location.isDirectory()) 117 { 118 throw new IOException ("Cache loader location [" + location + "] is not a directory!"); 119 } 120 121 122 File homeDir; 123 int offset = locationStr.indexOf('#'); 124 if (offset >= 0 && offset < locationStr.length() - 1) 125 { 126 homeDir = new File (locationStr.substring(0, offset)); 127 cacheDbName = locationStr.substring(offset + 1); 128 } 129 else 130 { 131 homeDir = new File (locationStr); 132 cacheDbName = cache.getClusterName(); 133 } 134 135 try 136 { 137 openDatabase(new File (homeDir, cacheDbName)); 138 } 139 catch (Exception e) 140 { 141 destroy(); 142 throw e; 143 } 144 } 145 146 149 private void openDatabase(File f) 150 throws Exception 151 { 152 Properties props = new Properties (); 153 recman = RecordManagerFactory.createRecordManager(f.toString(), props); 157 long recid = recman.getNamedObject(NAME); 158 log.debug(NAME + " located as " + recid); 159 if (recid == 0) 160 { 161 tree = BTree.createInstance(recman, new JdbmFqnComparator()); 162 recman.setNamedObject(NAME, tree.getRecid()); 163 } 164 else 165 { 166 tree = BTree.load(recman, recid); 167 } 168 169 log.info("JDBM database " + f + " opened with " + tree.size() + " entries"); 170 } 171 172 176 private void closeDatabases() 177 { 178 if (recman != null) 179 { 180 try 181 { 182 recman.close(); 183 } 184 catch (Exception shouldNotOccur) 185 { 186 log.warn("Caught unexpected exception", shouldNotOccur); 187 } 188 } 189 recman = null; 190 tree = null; 191 } 192 193 196 public void stop() 197 { 198 log.debug("stop"); 199 closeDatabases(); 200 } 201 202 205 206 209 public void setConfig(IndividualCacheLoaderConfig base) 210 { 211 checkNotOpen(); 212 213 if (base instanceof JdbmCacheLoaderConfig) 214 { 215 this.config = (JdbmCacheLoaderConfig) base; 216 } 217 else 218 { 219 config = new JdbmCacheLoaderConfig(base); 220 } 221 222 if (log.isTraceEnabled()) log.trace("Configuring cache loader with location = " + config.getLocation()); 223 } 224 225 public IndividualCacheLoaderConfig getConfig() 226 { 227 return config; 228 } 229 230 233 public void setCache(CacheSPI c) 234 { 235 super.setCache(c); 236 checkNotOpen(); 237 } 238 239 242 private Fqn keys(Fqn name) 243 { 244 return new Fqn(name, KEYS); 245 } 246 247 250 private Fqn key(Fqn name, Object key) 251 { 252 return new Fqn(name, KEYS, nullMask(key)); 253 } 254 255 262 public Set <String > getChildrenNames(Fqn name) 263 throws Exception 264 { 265 266 if (log.isTraceEnabled()) 267 { 268 log.trace("getChildrenNames " + name); 269 } 270 271 synchronized (tree) 272 { 273 return getChildrenNames0(name); 274 } 275 } 276 277 private Set <String > getChildrenNames0(Fqn name) throws IOException 278 { 279 TupleBrowser browser = tree.browse(name); 280 Tuple t = new Tuple(); 281 282 if (browser.getNext(t)) 283 { 284 if (!t.getValue().equals(NODE)) 285 { 286 log.trace(" not a node"); 287 return null; 288 } 289 } 290 else 291 { 292 log.trace(" no nodes"); 293 return null; 294 } 295 296 Set <String > set = new HashSet <String >(); 297 298 int depth = name.size() + 1; 300 while (browser.getNext(t)) 301 { 302 Fqn fqn = (Fqn) t.getKey(); 303 int size = fqn.size(); 304 if (size < depth) 305 { 306 break; 307 } 308 if (size == depth && t.getValue().equals(NODE)) 309 { 310 set.add((String ) fqn.getLastElement()); 311 } 312 } 313 314 if (set.isEmpty()) 315 { 316 return null; 317 } 318 319 return Collections.unmodifiableSet(set); 320 } 321 322 328 public Map get(Fqn name) 329 throws Exception 330 { 331 332 checkOpen(); 333 checkNonNull(name, "name"); 334 335 if (tree.find(name) == null) 336 { 337 if (log.isTraceEnabled()) 338 { 339 log.trace("get, no node: " + name); 340 } 341 return null; 342 } 343 344 Fqn keys = keys(name); 345 Tuple t = new Tuple(); 346 Map map = new HashMap (); 347 348 synchronized (tree) 349 { 350 TupleBrowser browser = tree.browse(keys); 351 while (browser.getNext(t)) 352 { 353 Fqn fqn = (Fqn) t.getKey(); 354 if (!fqn.isChildOf(keys)) 355 { 356 break; 357 } 358 Object k = fqn.getLastElement(); 359 Object v = t.getValue(); 360 map.put(nullUnmask(k), nullUnmask(v)); 361 } 362 } 363 364 if (log.isTraceEnabled()) 365 { 366 log.trace("get " + name + " map=" + map); 367 } 368 369 return map; 370 } 371 372 375 public boolean exists(Fqn name) throws IOException 376 { 377 return tree.find(name) != null; 378 } 379 380 private void commit() throws Exception 381 { 382 recman.commit(); 383 } 384 385 390 public Object put(Fqn name, Object key, Object value) throws Exception 391 { 392 try 393 { 394 return put0(name, key, value); 395 } 396 finally 397 { 398 commit(); 399 } 400 } 401 402 private Object put0(Fqn name, Object key, Object value) throws Exception 403 { 404 checkNonNull(name, "name"); 405 makeNode(name); 406 Fqn rec = key(name, key); 407 Object oldValue = insert(rec, value); 408 if (log.isTraceEnabled()) 409 { 410 log.trace("put " + rec + " value=" + value + " old=" + oldValue); 411 } 412 return oldValue; 413 } 414 415 421 public void put(Fqn name, Map values) throws Exception 422 { 423 put0(name, values); 424 commit(); 425 } 426 427 private void put0(Fqn name, Map values) throws Exception 428 { 429 if (log.isTraceEnabled()) 430 { 431 log.trace("put " + name + " values=" + values); 432 } 433 makeNode(name); 434 if (values == null) 435 { 436 return; 437 } 438 Iterator i = values.entrySet().iterator(); 439 while (i.hasNext()) 440 { 441 Map.Entry me = (Map.Entry ) i.next(); 442 Fqn rec = key(name, me.getKey()); 443 insert(rec, nullMask(me.getValue())); 444 } 445 } 446 447 450 private void makeNode(Fqn fqn) throws IOException 451 { 452 if (exists(fqn)) 453 { 454 return; 455 } 456 int size = fqn.size(); 457 for (int i = size; i >= 0; i--) 459 { 460 Fqn child = fqn.getFqnChild(i); 461 Object existing = tree.insert(child, NODE, false); 462 if (existing != null) 463 { 464 break; 465 } 466 } 467 } 468 469 private Object insert(Fqn fqn, Object value) throws IOException 470 { 471 return nullUnmask(tree.insert(fqn, nullMask(value), true)); 472 } 473 474 478 private void erase0(Fqn name) 479 throws IOException 480 { 481 erase0(name, true); 482 } 483 484 private void erase0(Fqn name, boolean self) 485 throws IOException 486 { 487 if (log.isTraceEnabled()) 488 { 489 log.trace("erase " + name + " self=" + self); 490 } 491 synchronized (tree) 492 { 493 TupleBrowser browser = tree.browse(name); 494 Tuple t = new Tuple(); 495 if (browser.getNext(t)) 496 { 497 if (self) 498 { 499 tree.remove(t.getKey()); 500 } 501 } 502 while (browser.getNext(t)) 503 { 504 Fqn fqn = (Fqn) t.getKey(); 505 if (!fqn.isChildOf(name)) 506 { 507 break; 508 } 509 tree.remove(fqn); 510 } 511 } 512 } 513 514 518 private Object eraseKey0(Fqn name, Object key) 519 throws IOException 520 { 521 if (log.isTraceEnabled()) 522 { 523 log.trace("eraseKey " + name + " key " + key); 524 } 525 Fqn fqnKey = key(name, key); 526 try 527 { 528 return tree.remove(fqnKey); 529 } 530 catch (IllegalArgumentException e) 531 { 532 return null; 536 } 537 } 538 539 544 public void put(List <Modification> modifications) 545 throws Exception 546 { 547 548 checkOpen(); 549 checkNonNull(modifications, "modifications"); 550 551 super.put(modifications); 552 commit(); 553 } 554 555 560 public void remove(Fqn name) 561 throws Exception 562 { 563 erase0(name); 564 commit(); 565 } 566 567 572 public Object remove(Fqn name, Object key) 573 throws Exception 574 { 575 576 try 577 { 578 return eraseKey0(name, key); 579 } 580 finally 581 { 582 commit(); 583 } 584 } 585 586 589 public void removeData(Fqn name) 590 throws Exception 591 { 592 erase0(name, false); 593 } 594 595 598 public void prepare(Object tx, List <Modification> modifications, boolean onePhase) 599 throws Exception 600 { 601 if (onePhase) 602 { 603 put(modifications); 604 } 605 else 606 { 607 transactions.put(tx, modifications); 608 } 609 } 610 611 614 public void commit(Object tx) throws Exception 615 { 616 List <Modification> modifications = transactions.remove(tx); 617 if (modifications == null) 618 { 619 throw new IllegalStateException ("transaction " + tx + " not found in transaction table"); 620 } 621 put(modifications); 622 commit(); 623 } 624 625 628 public void rollback(Object tx) 629 { 630 transactions.remove(tx); 631 } 632 633 636 private void checkOpen() 637 { 638 if (tree == null) 639 { 640 throw new IllegalStateException ( 641 "Operation not allowed before calling create()"); 642 } 643 } 644 645 648 private void checkNotOpen() 649 { 650 if (tree != null) 651 { 652 throw new IllegalStateException ( 653 "Operation not allowed after calling create()"); 654 } 655 } 656 657 660 private void checkNonNull(Object param, String paramName) 661 { 662 if (param == null) 663 { 664 throw new NullPointerException ( 665 "Parameter must not be null: " + paramName); 666 } 667 } 668 669 private Object nullMask(Object o) 670 { 671 return (o == null) ? Null.NULL : o; 672 } 673 674 private Object nullUnmask(Object o) 675 { 676 return (o == Null.NULL) ? null : o; 677 } 678 679 682 public void dump() throws IOException 683 { 684 dump(Fqn.ROOT); 685 } 686 687 690 public void dump(Object key) throws IOException 691 { 692 TupleBrowser browser = tree.browse(key); 693 Tuple t = new Tuple(); 694 log.debug("contents: " + key); 695 while (browser.getNext(t)) 696 { 697 log.debug(t.getKey() + "\t" + t.getValue()); 698 } 699 log.debug(""); 700 } 701 702 public String toString() 703 { 704 BTree bt = tree; 705 int size = (bt == null) ? -1 : bt.size(); 706 return "JdbmCacheLoader locationStr=" + config.getLocation() + 707 " size=" + size; 708 } 709 710 } 711 712 class JdbmFqnComparator extends FqnComparator implements Serializable 713 { 714 private static final long serialVersionUID = 1000; 715 } 716 717 | Popular Tags |