1 22 package org.jboss.ejb.plugins.cmp.jdbc; 23 24 import org.jboss.ejb.EntityEnterpriseContext; 25 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMPFieldBridge; 26 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMRFieldBridge; 27 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCFieldBridge; 28 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCEntityBridge; 29 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCReadAheadMetaData; 30 import org.jboss.logging.Logger; 31 import org.jboss.tm.TransactionLocal; 32 33 import javax.transaction.Transaction ; 34 import javax.transaction.SystemException ; 35 import java.lang.ref.SoftReference ; 36 import java.util.Collection ; 37 import java.util.Collections ; 38 import java.util.HashMap ; 39 import java.util.HashSet ; 40 import java.util.Iterator ; 41 import java.util.LinkedList ; 42 import java.util.List ; 43 import java.util.Map ; 44 45 46 54 public final class ReadAheadCache 55 { 56 60 private static final Object NULL_VALUE = new Object (); 61 62 private final JDBCStoreManager manager; 63 private final Logger log; 64 65 private final TransactionLocal listMapTxLocal = new TransactionLocal() 66 { 67 protected Object initialValue() 68 { 69 return new HashMap (); 70 } 71 72 public Transaction getTransaction() 73 { 74 try 75 { 76 return transactionManager.getTransaction(); 77 } 78 catch(SystemException e) 79 { 80 throw new IllegalStateException ("An error occured while getting the " + 81 "transaction associated with the current thread: " + e); 82 } 83 } 84 }; 85 86 private ListCache listCache; 87 private int listCacheMax; 88 89 public ReadAheadCache(JDBCStoreManager manager) 90 { 91 this.manager = manager; 92 93 log = Logger.getLogger( 95 this.getClass().getName() + 96 "." + 97 manager.getMetaData().getName()); 98 } 99 100 public void create() 101 { 102 listCacheMax = ((JDBCEntityBridge)manager.getEntityBridge()).getListCacheMax(); 104 listCache = new ListCache(listCacheMax); 105 } 106 107 public void start() 108 { 109 } 110 111 public void stop() 112 { 113 listCache.clear(); 114 } 115 116 public void destroy() 117 { 118 listCache = null; 119 } 120 121 public void addFinderResults(List results, JDBCReadAheadMetaData readahead) 122 { 123 if(listCacheMax == 0 || results.size() < 2) 124 { 125 return; 127 } 128 129 Map listMap = getListMap(); 130 if(listMap == null) 131 { 132 return; 134 } 135 136 if(log.isTraceEnabled()) 137 { 138 log.trace("Add finder results:" + 139 " entity=" + manager.getEntityBridge().getEntityName() + 140 " results=" + results + 141 " readahead=" + readahead); 142 } 143 144 if(!readahead.isNone()) 146 { 147 listCache.add(results); 148 } 149 150 HashSet dereferencedResults = new HashSet (); 156 Iterator iter = results.iterator(); 157 for(int i = 0; iter.hasNext(); i++) 158 { 159 Object pk = iter.next(); 160 161 EntityMapEntry entry; 163 if(readahead.isNone()) 164 { 165 entry = new EntityMapEntry(0, Collections.singletonList(pk), readahead); 166 } 167 else 168 { 169 entry = new EntityMapEntry(i, results, readahead); 170 } 171 172 EntityMapEntry oldInfo = (EntityMapEntry) listMap.put(pk, entry); 175 if(oldInfo != null) 176 { 177 dereferencedResults.add(oldInfo.results); 178 } 179 } 180 181 186 if(dereferencedResults.isEmpty()) 188 { 189 return; 190 } 191 192 iter = dereferencedResults.iterator(); 199 while(iter.hasNext()) 200 { 201 List dereferencedList = (List ) iter.next(); 202 203 boolean listHasReference = false; 204 Iterator iter2 = dereferencedList.iterator(); 205 while(!listHasReference && 206 iter2.hasNext()) 207 { 208 EntityMapEntry entry = (EntityMapEntry) listMap.get(iter2.next()); 209 if(entry != null && entry.results == dereferencedList) 210 { 211 listHasReference = true; 212 } 213 } 214 215 if(listHasReference) 216 { 217 iter.remove(); 219 } 220 } 221 222 if(dereferencedResults.isEmpty()) 224 { 225 return; 226 } 227 228 iter = dereferencedResults.iterator(); 230 while(iter.hasNext()) 231 { 232 List list = (List ) iter.next(); 233 if(log.isTraceEnabled()) 234 { 235 log.trace("Removing dereferenced results: " + list); 236 } 237 listCache.remove(list); 238 } 239 } 240 241 private void removeFinderResult(List results) 242 { 243 Map listMap = getListMap(); 244 if(listMap == null) 245 { 246 return; 248 } 249 250 listCache.remove(results); 252 253 if(!listMap.isEmpty()) 255 { 256 Iterator iter = listMap.values().iterator(); 257 while(iter.hasNext()) 258 { 259 EntityMapEntry entry = (EntityMapEntry) iter.next(); 260 261 if(entry.results == results) 263 { 264 iter.remove(); 265 } 266 } 267 } 268 } 269 270 public EntityReadAheadInfo getEntityReadAheadInfo(Object pk) 271 { 272 Map listMap = getListMap(); 273 if(listMap == null) 274 { 275 return new EntityReadAheadInfo(Collections.singletonList(pk)); 277 } 278 279 EntityMapEntry entry = (EntityMapEntry) getListMap().get(pk); 280 if(entry != null) 281 { 282 if(!entry.readahead.isNone()) 285 { 286 listCache.promote(entry.results); 287 } 288 289 JDBCReadAheadMetaData readahead = entry.readahead; 291 if(readahead == null) 292 { 293 readahead = manager.getMetaData().getReadAhead(); 294 } 295 296 int from = entry.index; 297 int to = Math.min(entry.results.size(), entry.index + readahead.getPageSize()); 298 List loadKeys = entry.results.subList(from, to); 299 300 return new EntityReadAheadInfo(loadKeys, readahead); 301 } 302 else 303 { 304 return new EntityReadAheadInfo(Collections.singletonList(pk)); 305 } 306 } 307 308 313 public boolean load(EntityEnterpriseContext ctx) 314 { 315 if(log.isTraceEnabled()) 316 { 317 log.trace("load data:" + 318 " entity=" + manager.getEntityBridge().getEntityName() + 319 " pk=" + ctx.getId()); 320 } 321 322 Map preloadDataMap = getPreloadDataMap(ctx.getId(), false); 324 if(preloadDataMap == null || preloadDataMap.isEmpty()) 325 { 326 if(log.isTraceEnabled()) 328 { 329 log.trace("No preload data found:" + 330 " entity=" + manager.getEntityBridge().getEntityName() + 331 " pk=" + ctx.getId()); 332 } 333 return false; 334 } 335 336 boolean cleanReadAhead = manager.getMetaData().isCleanReadAheadOnLoad(); 337 338 boolean loaded = false; 339 JDBCCMRFieldBridge onlyOneSingleValuedCMR = null; 340 341 Iterator iter = preloadDataMap.entrySet().iterator(); 343 while(iter.hasNext()) 344 { 345 Map.Entry entry = (Map.Entry ) iter.next(); 346 Object field = entry.getKey(); 347 348 Object value = entry.getValue(); 350 351 if(value == null) 353 { 354 throw new IllegalStateException ("Preloaded value not found"); 355 } 356 357 if(cleanReadAhead) 358 { 359 iter.remove(); 361 } 362 363 if(value == NULL_VALUE) 365 { 366 value = null; 367 } 368 369 if(field instanceof JDBCCMPFieldBridge) 370 { 371 JDBCCMPFieldBridge cmpField = (JDBCCMPFieldBridge) field; 372 373 if(!cmpField.isLoaded(ctx)) 374 { 375 if(log.isTraceEnabled()) 376 { 377 log.trace("Preloading data:" + 378 " entity=" + manager.getEntityBridge().getEntityName() + 379 " pk=" + ctx.getId() + 380 " cmpField=" + cmpField.getFieldName()); 381 } 382 383 cmpField.setInstanceValue(ctx, value); 385 386 cmpField.setClean(ctx); 388 389 loaded = true; 390 } 391 else 392 { 393 if(log.isTraceEnabled()) 394 { 395 log.trace("CMPField already loaded:" + 396 " entity=" + manager.getEntityBridge().getEntityName() + 397 " pk=" + ctx.getId() + 398 " cmpField=" + cmpField.getFieldName()); 399 } 400 } 401 } 402 else if(field instanceof JDBCCMRFieldBridge) 403 { 404 JDBCCMRFieldBridge cmrField = (JDBCCMRFieldBridge) field; 405 406 if(!cmrField.isLoaded(ctx)) 407 { 408 if(log.isTraceEnabled()) 409 { 410 log.trace("Preloading data:" + 411 " entity=" + manager.getEntityBridge().getEntityName() + 412 " pk=" + ctx.getId() + 413 " cmrField=" + cmrField.getFieldName()); 414 } 415 416 cmrField.load(ctx, (List ) value); 418 419 JDBCStoreManager relatedManager = (JDBCStoreManager) cmrField.getRelatedCMRField().getManager(); 421 ReadAheadCache relatedReadAheadCache = 422 relatedManager.getReadAheadCache(); 423 relatedReadAheadCache.addFinderResults( 424 (List ) value, cmrField.getReadAhead()); 425 426 if(!loaded) 427 { 428 if(cmrField.isSingleValued() && onlyOneSingleValuedCMR == null) 430 { 431 onlyOneSingleValuedCMR = cmrField; 432 } 433 else 434 { 435 loaded = true; 436 } 437 } 438 } 439 else 440 { 441 if(log.isTraceEnabled()) 442 { 443 log.trace("CMRField already loaded:" + 444 " entity=" + manager.getEntityBridge().getEntityName() + 445 " pk=" + ctx.getId() + 446 " cmrField=" + cmrField.getFieldName()); 447 } 448 } 449 } 450 } 451 452 if(cleanReadAhead) 453 { 454 manager.removeEntityTxData(new PreloadKey(ctx.getId())); 456 } 457 458 return loaded; 459 } 460 461 467 public Collection getCachedCMRValue(Object pk, JDBCCMRFieldBridge cmrField) 468 { 469 Map preloadDataMap = getPreloadDataMap(pk, true); 470 return (Collection )preloadDataMap.get(cmrField); 471 } 472 473 476 public void addPreloadData(Object pk, 477 JDBCFieldBridge field, 478 Object fieldValue) 479 { 480 if(field instanceof JDBCCMRFieldBridge) 481 { 482 if(fieldValue == null) 483 { 484 fieldValue = Collections.EMPTY_LIST; 485 } 486 else if(!(fieldValue instanceof Collection )) 487 { 488 fieldValue = Collections.singletonList(fieldValue); 489 } 490 } 491 492 if(log.isTraceEnabled()) 493 { 494 log.trace("Add preload data:" + 495 " entity=" + manager.getEntityBridge().getEntityName() + 496 " pk=" + pk + 497 " field=" + field.getFieldName()); 498 } 499 500 if(fieldValue == null) 502 { 503 fieldValue = NULL_VALUE; 504 } 505 506 Map preloadDataMap = getPreloadDataMap(pk, true); 508 Object overriden = preloadDataMap.put(field, fieldValue); 509 510 if(log.isTraceEnabled() && overriden != null) 511 { 512 log.trace( 513 "Overriding cached value " + overriden + 514 " with " + (fieldValue == NULL_VALUE ? null : fieldValue) + 515 ". pk=" + pk + 516 ", field=" + field.getFieldName() 517 ); 518 } 519 } 520 521 public void removeCachedData(Object primaryKey) 522 { 523 if(log.isTraceEnabled()) 524 { 525 log.trace("Removing cached data for " + primaryKey); 526 } 527 528 Map listMap = getListMap(); 529 if(listMap == null) 530 { 531 return; 533 } 534 535 manager.removeEntityTxData(new PreloadKey(primaryKey)); 537 538 EntityMapEntry oldInfo = (EntityMapEntry) listMap.remove(primaryKey); 541 if(oldInfo == null || oldInfo.readahead.isNone()) 542 { 543 return; 544 } 545 546 Iterator iter = listMap.values().iterator(); 548 while(iter.hasNext()) 549 { 550 EntityMapEntry entry = (EntityMapEntry) iter.next(); 551 552 if(entry.results == oldInfo.results) 554 { 555 return; 557 } 558 } 559 560 if(log.isTraceEnabled()) 562 { 563 log.trace("Removing dereferenced finder results: " + 564 oldInfo.results); 565 } 566 listCache.remove(oldInfo.results); 567 } 568 569 575 public Map getPreloadDataMap(Object entityPrimaryKey, boolean create) 576 { 577 583 PreloadKey preloadKey = new PreloadKey(entityPrimaryKey); 585 586 SoftReference ref = (SoftReference ) manager.getEntityTxData(preloadKey); 588 589 if(ref != null) 591 { 592 Map preloadDataMap = (Map ) ref.get(); 594 595 if(preloadDataMap != null) 597 { 598 return preloadDataMap; 599 } 600 } 601 602 if(ref != null) 607 { 608 manager.removeEntityTxData(preloadKey); 610 } 611 612 if(!create) 614 { 615 return null; 616 } 617 618 Map preloadDataMap = new HashMap (); 620 621 ref = new SoftReference (preloadDataMap); 623 624 manager.putEntityTxData(preloadKey, ref); 626 627 return preloadDataMap; 629 } 630 631 private Map getListMap() 632 { 633 return (Map ) listMapTxLocal.get(); 634 } 635 636 private final class ListCache 637 { 638 private final TransactionLocal cacheTxLocal = new TransactionLocal() 639 { 640 protected Object initialValue() 641 { 642 return new LinkedList (); 643 } 644 645 public Transaction getTransaction() 646 { 647 try 648 { 649 return transactionManager.getTransaction(); 650 } 651 catch(SystemException e) 652 { 653 throw new IllegalStateException ("An error occured while getting the " + 654 "transaction associated with the current thread: " + e); 655 } 656 } 657 }; 658 private int max; 659 660 public ListCache(int max) 661 { 662 if(max < 0) 663 throw new IllegalArgumentException ("list-cache-max is negative: " + max); 664 this.max = max; 665 } 666 667 public void add(List list) 668 { 669 if(max == 0) 670 { 671 return; 673 } 674 675 LinkedList cache = getCache(); 676 if(cache == null) 677 return; 678 cache.addFirst(new IdentityObject(list)); 679 680 while(cache.size() > max) 682 { 683 IdentityObject object = (IdentityObject) cache.removeLast(); 684 ageOut((List ) object.getObject()); 685 } 686 } 687 688 public void promote(List list) 689 { 690 if(max == 0) 691 { 692 return; 694 } 695 696 LinkedList cache = getCache(); 697 if(cache == null) 698 return; 699 700 IdentityObject object = new IdentityObject(list); 701 if(cache.remove(object)) 702 { 703 cache.addFirst(object); 705 } 706 } 707 708 public void remove(List list) 709 { 710 if(max == 0) 711 { 712 return; 714 } 715 LinkedList cache = getCache(); 716 if(cache != null) 717 cache.remove(new IdentityObject(list)); 718 } 719 720 public void clear() 721 { 722 if(max == 0) 723 { 724 return; 726 } 727 } 728 729 private void ageOut(List list) 730 { 731 removeFinderResult(list); 732 } 733 734 private LinkedList getCache() 735 { 736 return (LinkedList ) cacheTxLocal.get(); 737 } 738 } 739 740 744 private static final class PreloadKey 745 { 746 private final Object entityPrimaryKey; 747 748 public PreloadKey(Object entityPrimaryKey) 749 { 750 if(entityPrimaryKey == null) 751 { 752 throw new IllegalArgumentException ("Entity primary key is null"); 753 } 754 this.entityPrimaryKey = entityPrimaryKey; 755 } 756 757 public boolean equals(Object object) 758 { 759 if(object instanceof PreloadKey) 760 { 761 PreloadKey preloadKey = (PreloadKey) object; 762 return preloadKey.entityPrimaryKey.equals(entityPrimaryKey); 763 } 764 return false; 765 } 766 767 public int hashCode() 768 { 769 return entityPrimaryKey.hashCode(); 770 } 771 772 public String toString() 773 { 774 return "PreloadKey: entityId=" + entityPrimaryKey; 775 } 776 } 777 778 private static final class EntityMapEntry 779 { 780 public final int index; 781 public final List results; 782 public final JDBCReadAheadMetaData readahead; 783 784 private EntityMapEntry( 785 int index, 786 List results, 787 JDBCReadAheadMetaData readahead) 788 { 789 790 this.index = index; 791 this.results = results; 792 this.readahead = readahead; 793 } 794 } 795 796 public final static class EntityReadAheadInfo 797 { 798 private final List loadKeys; 799 private final JDBCReadAheadMetaData readahead; 800 801 private EntityReadAheadInfo(List loadKeys) 802 { 803 this(loadKeys, null); 804 } 805 806 private EntityReadAheadInfo(List loadKeys, JDBCReadAheadMetaData r) 807 { 808 this.loadKeys = loadKeys; 809 this.readahead = r; 810 } 811 812 public List getLoadKeys() 813 { 814 return loadKeys; 815 } 816 817 public JDBCReadAheadMetaData getReadAhead() 818 { 819 return readahead; 820 } 821 } 822 823 826 private static final class IdentityObject 827 { 828 private final Object object; 829 830 public IdentityObject(Object object) 831 { 832 if(object == null) 833 { 834 throw new IllegalArgumentException ("Object is null"); 835 } 836 this.object = object; 837 } 838 839 public Object getObject() 840 { 841 return object; 842 } 843 844 public boolean equals(Object object) 845 { 846 return this.object == object; 847 } 848 849 public int hashCode() 850 { 851 return object.hashCode(); 852 } 853 854 public String toString() 855 { 856 return object.toString(); 857 } 858 } 859 } 860 | Popular Tags |