1 2 12 package com.versant.core.jdo; 13 14 import com.versant.core.common.*; 15 16 import java.lang.ref.ReferenceQueue ; 17 import java.util.Set ; 18 import java.util.Iterator ; 19 20 24 public final class LocalPMCache { 25 28 static final int DEFAULT_INITIAL_CAPACITY = 16; 29 32 static final float DEFAULT_LOAD_FACTOR = 0.75f; 33 36 final float loadFactor; 37 40 int threshold; 41 46 static final int MAXIMUM_CAPACITY = 1 << 30; 47 48 51 public ReferenceQueue queue = new ReferenceQueue (); 52 private VersantPersistenceManagerImp pm; 53 private final TransactionalList processList = new TransactionalList(); 54 private boolean overWriteMode; 55 56 private int currentRefType = VersantPersistenceManager.PM_CACHE_REF_TYPE_SOFT; 57 58 61 private PMCacheEntry[] m_keyTable; 62 63 66 transient int size; 67 68 private final int createdSize; 69 70 public LocalPMCache() { 71 this.loadFactor = DEFAULT_LOAD_FACTOR; 72 threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); 73 m_keyTable = new PMCacheEntry[DEFAULT_INITIAL_CAPACITY]; 74 createdSize = DEFAULT_INITIAL_CAPACITY; 75 } 76 77 public LocalPMCache(int initialCapacity) { 78 this(initialCapacity, DEFAULT_LOAD_FACTOR); 79 } 80 81 public LocalPMCache(int initialCapacity, float loadFactor) { 82 if (initialCapacity < 0) 83 throw new IllegalArgumentException ("Illegal initial capacity: " + 84 initialCapacity); 85 if (initialCapacity > MAXIMUM_CAPACITY) 86 initialCapacity = MAXIMUM_CAPACITY; 87 if (loadFactor <= 0 || Float.isNaN(loadFactor)) 88 throw new IllegalArgumentException ("Illegal load factor: " + 89 loadFactor); 90 91 int capacity = 1; 93 while (capacity < initialCapacity) 94 capacity <<= 1; 95 96 this.loadFactor = loadFactor; 97 threshold = (int)(capacity * loadFactor); 98 m_keyTable = new PMCacheEntry[capacity]; 99 createdSize = initialCapacity; 100 } 101 102 public int getCurrentRefType() { 103 return currentRefType; 104 } 105 106 public void setCurrentRefType(int currentRefType) { 107 checkRefType(currentRefType); 108 this.currentRefType = currentRefType; 109 } 110 111 public static void checkRefType(int currentRefType) { 112 if (currentRefType != VersantPersistenceManager.PM_CACHE_REF_TYPE_WEAK 113 && currentRefType != VersantPersistenceManager.PM_CACHE_REF_TYPE_SOFT 114 && currentRefType != VersantPersistenceManager.PM_CACHE_REF_TYPE_STRONG) { 115 throw BindingSupportImpl.getInstance().invalidOperation("The option '" 116 + currentRefType + "' is not a valid choice for a PMCacheRefType."); 117 } 118 } 119 120 public boolean isOverWriteMode() { 121 return overWriteMode; 122 } 123 124 public void setOverWriteMode(boolean overWriteMode) { 125 this.overWriteMode = overWriteMode; 126 } 127 128 public void setPm(VersantPersistenceManagerImp pm) { 129 this.pm = pm; 130 } 131 132 135 public PCStateMan add(PCStateMan sm) { 136 sm.getRealOIDIfAppId(); 138 addSm(sm.oid.getAvailableOID(), sm); 139 addForProcessing(sm); 140 if (sm.isTx()) pm.addTxStateObject(sm); 141 return sm; 142 } 143 144 public PCStateMan add(OID oid, State state, PCStateMan[] sms) { 145 return addState(oid, state, true, sms); 146 } 147 148 152 public void addStateOnly(OID oid, State state) { 153 addState(oid, state, false, null); 154 } 155 156 159 public PMCacheEntry createCacheKey(PCStateMan sm) { 160 if (sm.cacheEntry != null) { 161 throw BindingSupportImpl.getInstance().internal("StateManager already has a PMCacheEntry"); 162 } 163 return sm.cacheEntry = new PMCacheEntry(currentRefType, sm, queue); 164 } 165 166 169 public PMCacheEntry createCacheKey(OID oid, State state) { 170 return new PMCacheEntry(currentRefType, oid, state, queue); 171 } 172 173 176 public PCStateMan addForProcessing(PCStateMan sm) { 177 if (!pm.isActive()) return sm; 178 processList.add(sm); 179 return sm; 180 } 181 182 PCStateMan updateSm(State value, PCStateMan sm, OID key) { 183 if (value == NULLState.NULL_STATE) { 184 187 throw BindingSupportImpl.getInstance().objectNotFound("No row for " + 188 sm.getClassMetaData().storeClass + " " + key.toSString()); 189 } 190 191 if (sm != null) { 192 sm.updateWith(value, pm, overWriteMode); 193 addForProcessing(sm); 194 } 195 return sm; 196 } 197 198 201 public void processReferenceQueue() { 202 processReferenceQueueImp(); 203 } 204 205 212 private void processReferenceQueueImp() { 213 PMCacheEntryOwnerRef ref; 214 while ((ref = (PMCacheEntryOwnerRef)queue.poll()) != null) { 215 PMCacheEntry ce = ref.getOwner(); 216 if (!ce.hasReference(ref)) 217 221 continue; 222 removeImp(ce, m_keyTable, indexFor(ce.mappedOID.hashCode(), m_keyTable.length)); 223 } 224 } 225 226 public void doCommit(boolean retainValues) { 227 processReferenceQueueImp(); 228 Iterator iter = processList.iterator(); 230 while (iter.hasNext()) { 231 PMCacheEntry ce = (PMCacheEntry) iter.next(); 232 PCStateMan sm = (PCStateMan) ce.get(); 233 if (sm != null) { 234 sm.commit(pm); 235 } 236 } 237 processList.clear(); 238 } 239 240 public void doRollback(boolean retainValues) { 241 processReferenceQueueImp(); 242 Iterator iter = processList.iterator(); 243 while (iter.hasNext()) { 244 PMCacheEntry ce = (PMCacheEntry) iter.next(); 245 PCStateMan sm = (PCStateMan) ce.get(); 246 if (sm != null) { 247 sm.rollback(); 248 } 249 } 250 processList.clear(); 251 } 252 253 public void doRefresh(boolean strict) { 254 Iterator iter = processList.iterator(); 255 while (iter.hasNext()) { 256 PMCacheEntry ce = (PMCacheEntry) iter.next(); 257 PCStateMan sm = (PCStateMan) ce.get(); 258 if (sm != null) { 259 sm.refresh(); 260 } 261 } 262 } 263 264 267 public void addRealOID(PCStateMan sm) { 268 reMapWithRealOID(sm); 269 } 270 271 private void reMapWithRealOID(PCStateMan sm) { 272 if (Debug.DEBUG) { 273 validate(); 274 if (!sm.oid.isNew()) { 275 throw BindingSupportImpl.getInstance().internal("The instance is not new"); 276 } 277 if (sm.cacheEntry.mappedOID != sm.oid) { 278 throw BindingSupportImpl.getInstance().internal("The instance is not mapped with its newOID"); 279 } 280 if (sm.oid.getRealOID() == null) { 281 throw BindingSupportImpl.getInstance().internal("The realOID may not be null: " + sm.oid); 282 } 283 } 284 285 if (sm.cacheEntry.mappedOID == sm.oid.getRealOID()) return; 286 final int currentIndex = indexFor(sm.cacheEntry.hash, m_keyTable.length); 287 final int newIndex = indexFor(sm.oid.getRealOID().hashCode(), m_keyTable.length); 288 289 if (currentIndex == newIndex) { 290 OID realOID = sm.oid.getRealOID(); 291 sm.cacheEntry.reHash(realOID); 292 realOID.resolve(sm.state); 293 return; 294 } 295 if (sm.cacheEntry.prev == null) { 297 m_keyTable[currentIndex] = sm.cacheEntry.next; 298 } 299 sm.cacheEntry.unlinkNextList(); 300 301 OID realOID = sm.oid.getRealOID(); 302 sm.cacheEntry.reHash(realOID); 303 realOID.resolve(sm.state); 304 305 sm.cacheEntry.setNext(m_keyTable[newIndex]); 306 m_keyTable[newIndex] = sm.cacheEntry; 307 } 308 309 public void checkModelConsistency() { 310 processReferenceQueueImp(); 311 PMCacheEntry[] src = m_keyTable; 312 for (int j = 0; j < src.length; j++) { 313 PMCacheEntry e = src[j]; 314 if (e != null) { 315 Object val = e.get(); 316 if (val != null && (val instanceof PCStateMan)) { 317 ((PCStateMan)val).checkModelConsistency(); 318 } 319 e = e.next; 320 } 321 } 322 } 323 324 329 protected PCStateMan addState(OID key, State value, boolean manage, PCStateMan[] addSm) { 330 if (Debug.DEBUG) validate(); 331 final PMCacheEntry[] m_keyTable = this.m_keyTable; 332 final int hash = key.hashCode(); 333 final int i = indexFor(hash, m_keyTable.length); 334 335 for (PMCacheEntry e = m_keyTable[i]; e != null; e = e.next) { 336 if (e.hashCode() == hash && eq(key, e.mappedOID)) { 337 Object o = e.get(); 338 if (o == null) { 339 removeImp(e, m_keyTable, i); 340 break; 341 } 342 if (o instanceof PCStateMan) { 343 return updateSm(value, (PCStateMan) o, key); 344 } else { 345 State currentState = (State)o; 346 if (value == NULLState.NULL_STATE) { 347 removeImp(e, m_keyTable, i); 348 return null; 349 } else { 350 if (overWriteMode) { 351 currentState.clear(); 352 } 353 if (currentState != null) { 354 value.updateNonFilled(currentState); 355 if (manage) { 356 return e.upgradeToSm(addSm[0] = pm.reManage(key, value), queue); 357 } else { 358 currentState.updateNonFilled(value); 359 return null; 360 } 361 } 362 } 363 } 364 } 365 } 366 if (Debug.DEBUG) validate(); 367 if (value == NULLState.NULL_STATE) { 368 return null; 370 } 371 PMCacheEntry ce; 373 PCStateMan sm = null; 374 if (manage) { 375 ce = createCacheKey(sm = addSm[0] = pm.reManage(key, value)); 377 } else { 378 ce = createCacheKey(key, value); 380 } 381 ce.setNext(m_keyTable[i]); 382 m_keyTable[i] = ce; 383 if (size++ >= threshold) resize(2 * m_keyTable.length); 384 if (Debug.DEBUG) validate(); 385 return sm; 386 } 387 388 391 private void removeImp(PMCacheEntry e, final PMCacheEntry[] m_keyTable, final int i) { 392 if (Debug.DEBUG) validate(); 393 if (m_keyTable[i] == null) return; 394 if (m_keyTable[i] == e) { 395 m_keyTable[i] = e.next; 396 clearCE(e); 397 } else { 398 for (PMCacheEntry ce = m_keyTable[i].next; ce != null; ce = ce.next) { 399 if (ce == e) { 400 clearCE(e); 401 break; 402 } 403 } 404 405 } 406 if (Debug.DEBUG) validate(); 407 } 408 409 private void clearCE(PMCacheEntry e) { 410 e.unlinkNextList(); 411 e.clear(); 412 size--; 413 } 414 415 420 public PCStateMan addSm(OID key, PCStateMan value) { 421 key = key.getAvailableOID(); 422 final PMCacheEntry[] m_keyTable = this.m_keyTable; 423 final int hash = key.hashCode(); 424 final int i = indexFor(hash, m_keyTable.length); 425 426 for (PMCacheEntry e = m_keyTable[i]; e != null; e = e.next) { 427 if (e.hash == hash && eq(key, e.mappedOID)) { 428 Object o = e.get(); 429 if (o != null) { 430 if (o != null && o != value) { 431 throw BindingSupportImpl.getInstance().internal("Inconsistent mapping for id '" + key.toPkString() + "'"); 432 } 433 return value; 434 } 435 remove(e); 436 break; 437 } 438 } 439 PMCacheEntry ce = value.cacheEntry; 441 if (ce == null) { 442 ce = createCacheKey(value); 443 } 444 445 ce.setNext(m_keyTable[i]); 446 m_keyTable[i] = ce; 447 if (size++ >= threshold) resize(2 * m_keyTable.length); 448 449 if (Debug.DEBUG) validate(); 450 return value; 451 } 452 453 public void setInterceptDfgFieldAccess(boolean on) { 454 processReferenceQueueImp(); 455 456 PMCacheEntry[] src = m_keyTable; 457 for (int j = 0; j < src.length; j++) { 458 PMCacheEntry e = src[j]; 459 while (e != null) { 460 Object o = e.get(); 461 if (o != null && (o instanceof PCStateMan)) { 462 ((PCStateMan)o).setInterceptDfgFieldAccess(on); 463 } 464 e = e.next; 465 } 466 } 467 } 468 469 public void evict() { 470 PMCacheEntry[] src = m_keyTable; 471 for (int j = 0; j < src.length; j++) { 472 PMCacheEntry e = src[j]; 473 while (e != null) { 474 Object o = e.get(); 475 if (o != null && (o instanceof PCStateMan)) { 476 ((PCStateMan)o).evict(); 477 } 478 e = e.next; 479 } 480 } 481 processReferenceQueueImp(); 482 } 483 484 488 public State getStateByOID(OID oid) { 489 Object value = getValueByOid(oid); 490 if (value == null) return null; 491 if (value instanceof PCStateMan) { 492 return ((PCStateMan) value).state; 493 } else { 494 return (State) value; 495 } 496 } 497 498 502 public boolean contains(OID oid) { 503 return (getValueByOid(oid) != null); 504 } 505 506 510 public PCStateMan getByOID(OID oid, boolean manage) { 511 PMCacheEntry ce = getByOID(oid.getAvailableOID()); 512 if (ce == null) return null; 513 Object value = ce.get(); 514 if (value == null) { 515 remove(ce); 516 return null; 517 } 518 if (value instanceof PCStateMan) { 519 return (PCStateMan) value; 520 } 521 if (!manage) return null; 522 return ce.upgradeToSm(pm.reManage(oid, (State)value), queue); 523 } 524 525 530 public PCStateMan getByNewObjectOID(NewObjectOID oid) { 531 PMCacheEntry ce = getByOID(oid); 532 if (ce == null) return null; 533 Object value = ce.get(); 534 if (value == null) { 535 remove(ce); 536 return null; 537 } 538 if (value instanceof PCStateMan) { 539 return (PCStateMan) value; 540 } 541 return ce.upgradeToSm(pm.reManage(oid, (State)value), queue); 542 } 543 544 private Object getValueByOid(OID oid) { 545 PMCacheEntry ce = getByOID(oid.getAvailableOID()); 546 if (ce == null) return null; 547 Object value = ce.get(); 548 if (value == null) { 549 remove(ce); 550 return null; 551 } 552 return value; 553 } 554 555 private PMCacheEntry getByOID(OID oid) { 556 final int hash = oid.hashCode(); 557 for (PMCacheEntry e = m_keyTable[indexFor(hash, m_keyTable.length)]; e != null; e = e.next) { 558 if (e.hash == hash && eq(oid, e.mappedOID)) { 559 return e; 560 } 561 } 562 return null; 563 } 564 565 public void clear() { 566 PMCacheEntry[] src = m_keyTable; 567 for (int j = 0; j < src.length; j++) { 568 PMCacheEntry e = src[j]; 569 PMCacheEntry next; 570 while (e != null) { 571 next = e.next; 572 e.reset(); 573 e.next = null; 574 e.prev = null; 575 e = next; 576 } 577 } 578 processList.clear(); 579 size = 0; 580 m_keyTable = new PMCacheEntry[createdSize]; 581 } 582 583 public boolean inProcessList(PCStateMan sm) { 584 return processList.contains(sm.cacheEntry); 585 } 586 587 public void remove(PCStateMan sm) { 588 remove(sm.cacheEntry); 589 } 590 591 595 private void remove(PMCacheEntry ce) { 596 if (Debug.DEBUG) validate(); 597 int hash = ce.mappedOID.hashCode(); 598 int i = indexFor(hash, m_keyTable.length); 599 PMCacheEntry e = m_keyTable[i]; 600 for (;e != null; e = e.next) { 601 if (e.hash == hash && eq(ce.mappedOID, e.mappedOID)) { 602 removeImp(e, m_keyTable, i); 603 } 604 } 605 if (Debug.DEBUG) validate(); 606 } 607 608 611 private Set [] validate() { 612 int count = 0; 613 PMCacheEntry[] src = m_keyTable; 615 for (int j = 0; j < src.length; j++) { 616 PMCacheEntry e = src[j]; 617 while (e != null) { 618 int index = indexFor(e.mappedOID.hashCode(), m_keyTable.length); 619 if (index != j) { 620 throw BindingSupportImpl.getInstance().internal("The entry is not at the correct pos"); 621 } 622 count++; 633 e = e.next; 634 } 635 } 636 637 if (count != size) { 638 throw BindingSupportImpl.getInstance().internal("The counted size == " + count + " but size is " + size); 639 } 640 return null; 642 } 643 644 public void dump() { 645 System.out.println("\n\n\nLocalPMCache.dump: START"); 646 PMCacheEntry[] src = m_keyTable; 647 for (int j = 0; j < src.length; j++) { 648 PMCacheEntry e = src[j]; 649 boolean first = true; 650 while (e != null) { 651 if (first) { 652 System.out.println("j = " + j); 653 first = false; 654 } 655 System.out.println("e = " + e); 656 e = e.next; 657 } 658 } 659 System.out.println("LocalPMCache.dump: END \n\n\n"); 660 } 661 662 private void resize(int newCapacity) { 663 if (Debug.DEBUG) validate(); 664 PMCacheEntry[] oldTable = m_keyTable; 665 int oldCapacity = oldTable.length; 666 if (oldCapacity == MAXIMUM_CAPACITY) { 667 threshold = Integer.MAX_VALUE; 668 return; 669 } 670 671 PMCacheEntry[] newTable = new PMCacheEntry[newCapacity]; 672 transfer(newTable); 673 m_keyTable = newTable; 674 threshold = (int)(newCapacity * loadFactor); 675 if (Debug.DEBUG) validate(); 676 } 677 678 681 private void transfer(PMCacheEntry[] newTable) { 682 PMCacheEntry[] src = m_keyTable; 683 int newCapacity = newTable.length; 684 size = 0; 685 for (int j = 0; j < src.length; j++) { 686 PMCacheEntry e = src[j]; 687 if (e != null) { 688 for (;;) { 690 if (e == null) break; 691 if (e.get() == null) { 692 e.clear(); 693 e = e.next; 694 } else { 695 break; 696 } 697 } 698 if (e == null) continue; 699 src[j] = null; 700 701 do { 702 size++; 703 PMCacheEntry next = e.next; 704 int i = indexFor(e.hash, newCapacity); 705 e.unlinkNextList(); 706 e.setNext(newTable[i]); 707 newTable[i] = e; 708 e = next; 709 } while (e != null); 710 } 711 } 712 } 713 714 717 static boolean eq(OID x, OID y) { 718 return x == y || x.equals(y); 719 } 720 721 724 static int indexFor(int h, int length) { 725 return h & (length-1); 726 } 727 728 731 public int size() { 732 return size; 733 } 734 735 } 736 | Popular Tags |