1 package org.apache.ojb.broker.cache; 2 3 17 18 import java.io.Serializable ; 19 import java.lang.ref.ReferenceQueue ; 20 import java.lang.ref.SoftReference ; 21 import java.util.HashMap ; 22 import java.util.Iterator ; 23 import java.util.Properties ; 24 25 import org.apache.ojb.broker.Identity; 26 import org.apache.ojb.broker.PBStateEvent; 27 import org.apache.ojb.broker.PBStateListener; 28 import org.apache.ojb.broker.PersistenceBroker; 29 import org.apache.ojb.broker.core.DelegatingPersistenceBroker; 30 import org.apache.ojb.broker.core.PersistenceBrokerImpl; 31 import org.apache.ojb.broker.core.proxy.ProxyHelper; 32 import org.apache.ojb.broker.metadata.ClassDescriptor; 33 import org.apache.ojb.broker.metadata.FieldDescriptor; 34 import org.apache.ojb.broker.metadata.MetadataException; 35 import org.apache.ojb.broker.util.ClassHelper; 36 import org.apache.ojb.broker.util.logging.Logger; 37 import org.apache.ojb.broker.util.logging.LoggerFactory; 38 import org.apache.commons.lang.builder.ToStringBuilder; 39 40 107 public class ObjectCacheTwoLevelImpl implements ObjectCacheInternal, PBStateListener 108 { 109 private Logger log = LoggerFactory.getLogger(ObjectCacheTwoLevelImpl.class); 110 111 public static final String APPLICATION_CACHE_PROP = "applicationCache"; 112 public static final String COPY_STRATEGY_PROP = "copyStrategy"; 113 public static final String FORCE_PROXIES = "forceProxies"; 114 private static final String DEF_COPY_STRATEGY = ObjectCacheTwoLevelImpl.CopyStrategyImpl.class.getName(); 115 private static final String DEF_APP_CACHE = ObjectCacheDefaultImpl.class.getName(); 116 117 private HashMap sessionCache; 118 private int invokeCounter; 120 private ReferenceQueue queue = new ReferenceQueue (); 121 private ObjectCacheInternal applicationCache; 122 private CopyStrategy copyStrategy; 123 private PersistenceBrokerImpl broker; 124 private boolean forceProxies = false; 125 126 public ObjectCacheTwoLevelImpl(final PersistenceBroker broker, Properties prop) 127 { 128 if(broker instanceof PersistenceBrokerImpl) 130 { 131 this.broker = (PersistenceBrokerImpl) broker; 132 } 133 else if(broker instanceof DelegatingPersistenceBroker) 134 { 135 this.broker = (PersistenceBrokerImpl) ((DelegatingPersistenceBroker) broker).getInnermostDelegate(); 136 } 137 else 138 { 139 throw new RuntimeCacheException("Can't initialize two level cache, expect instance of" 140 + PersistenceBrokerImpl.class + " or of " + DelegatingPersistenceBroker.class 141 + " to setup application cache, but was " + broker); 142 } 143 this.sessionCache = new HashMap (100); 144 setupApplicationCache(this.broker, prop); 146 broker.addListener(this, true); 148 } 149 150 154 public PersistenceBrokerImpl getBroker() 155 { 156 return broker; 157 } 158 159 private void setupApplicationCache(PersistenceBrokerImpl broker, Properties prop) 160 { 161 if(log.isDebugEnabled()) log.debug("Start setup application cache for broker " + broker); 162 if(prop == null) 163 { 164 prop = new Properties (); 165 } 166 String copyStrategyName = prop.getProperty(COPY_STRATEGY_PROP, DEF_COPY_STRATEGY).trim(); 167 if(copyStrategyName.length() == 0) 168 { 169 copyStrategyName = DEF_COPY_STRATEGY; 170 } 171 String applicationCacheName = prop.getProperty(APPLICATION_CACHE_PROP, DEF_APP_CACHE).trim(); 172 if(applicationCacheName.length() == 0) 173 { 174 applicationCacheName = DEF_APP_CACHE; 175 } 176 177 String forceProxyValue = prop.getProperty(FORCE_PROXIES, "false").trim(); 178 forceProxies = Boolean.valueOf(forceProxyValue).booleanValue(); 179 180 if (forceProxies && broker.getProxyFactory().interfaceRequiredForProxyGeneration()){ 181 log.warn("'" + FORCE_PROXIES + "' is set to true, however a ProxyFactory implementation " + 182 "[" + broker.getProxyFactory().getClass().getName() +"] " + 183 " that requires persistent objects to implement an inteface is being used. Please ensure " + 184 "that all persistent objects implement an interface, or change the ProxyFactory setting to a dynamic " + 185 "proxy generator (like ProxyFactoryCGLIBImpl)."); 186 } 187 188 Class [] type = new Class []{PersistenceBroker.class, Properties .class}; 189 Object [] objects = new Object []{broker, prop}; 190 try 191 { 192 this.copyStrategy = (CopyStrategy) ClassHelper.newInstance(copyStrategyName); 193 Class target = ClassHelper.getClass(applicationCacheName); 194 if(target.equals(ObjectCacheDefaultImpl.class)) 195 { 196 prop.setProperty(ObjectCacheDefaultImpl.AUTOSYNC_PROP, "false"); 198 } 199 ObjectCache temp = (ObjectCache) ClassHelper.newInstance(target, type, objects); 200 if(!(temp instanceof ObjectCacheInternal)) 201 { 202 log.warn("Specified application cache class doesn't implement '" + ObjectCacheInternal.class.getName() 203 + "'. For best interaction only specify caches implementing the internal object cache interface."); 204 temp = new CacheDistributor.ObjectCacheInternalWrapper(temp); 205 } 206 this.applicationCache = (ObjectCacheInternal) temp; 207 } 208 catch(Exception e) 209 { 210 throw new MetadataException("Can't setup application cache. Specified application cache was '" 211 + applicationCacheName + "', copy strategy was '" + copyStrategyName + "'", e); 212 } 213 if(log.isEnabledFor(Logger.INFO)) 214 { 215 ToStringBuilder buf = new ToStringBuilder(this); 216 buf.append("copyStrategy", copyStrategyName) 217 .append("applicationCache", applicationCacheName); 218 log.info("Setup cache: " + buf.toString()); 219 } 220 } 221 222 227 public ObjectCacheInternal getApplicationCache() 228 { 229 return applicationCache; 230 } 231 232 private Object lookupFromApplicationCache(Identity oid) 233 { 234 Object result = null; 235 Object obj = getApplicationCache().lookup(oid); 236 if(obj != null) 237 { 238 result = copyStrategy.read(broker, obj); 239 } 240 return result; 241 } 242 243 private boolean putToApplicationCache(Identity oid, Object obj, boolean cacheIfNew) 244 { 245 249 Object oldTarget = null; 250 if(!cacheIfNew) 251 { 252 oldTarget = getApplicationCache().lookup(oid); 253 } 254 Object target = copyStrategy.write(broker, obj, oldTarget); 255 if(cacheIfNew) 256 { 257 return getApplicationCache().cacheIfNew(oid, target); 258 } 259 else 260 { 261 getApplicationCache().cache(oid, target); 262 return false; 263 } 264 } 265 266 270 public void resetSessionCache() 271 { 272 sessionCache.clear(); 273 invokeCounter = 0; 274 } 275 276 280 private void pushToApplicationCache(int typeToProcess, int typeAfterProcess) 281 { 282 for(Iterator iter = sessionCache.values().iterator(); iter.hasNext();) 283 { 284 CacheEntry entry = (CacheEntry) iter.next(); 285 Object result = entry.get(); 287 if(result == null) 288 { 289 if(log.isDebugEnabled()) 290 log.debug("Object in session cache was gc, nothing to push to application cache"); 291 } 292 else 293 { 294 if(entry.type == typeToProcess) 296 { 297 if(log.isDebugEnabled()) 298 { 299 log.debug("Move obj from session cache --> application cache : " + entry.oid); 300 } 301 305 if(ProxyHelper.isMaterialized(result)) 306 { 307 putToApplicationCache(entry.oid, ProxyHelper.getRealObject(result), false); 308 entry.type = typeAfterProcess; 310 } 311 } 312 } 313 } 314 } 315 316 322 public void doInternalCache(Identity oid, Object obj, int type) 323 { 324 processQueue(); 325 if(type == TYPE_NEW_MATERIALIZED) 327 { 328 boolean result = putToApplicationCache(oid, obj, true); 329 CacheEntry entry = new CacheEntry(oid, obj, TYPE_CACHED_READ, queue); 330 if(result) 331 { 332 putToSessionCache(oid, entry, false); 335 } 336 else 337 { 338 putToSessionCache(oid, entry, true); 341 if(log.isDebugEnabled()) 342 { 343 log.debug("The 'new' materialized object was already in cache," + 344 " will not push it to application cache: " + oid); 345 } 346 } 347 } 348 else 349 { 350 CacheEntry entry = new CacheEntry(oid, obj, type, queue); 353 putToSessionCache(oid, entry, false); 354 } 355 } 356 357 362 public Object lookup(Identity oid) 363 { 364 Object result = null; 365 CacheEntry entry = (CacheEntry) sessionCache.get(oid); 367 if(entry != null) 368 { 369 result = entry.get(); 370 } 371 if(result == null) 372 { 373 result = lookupFromApplicationCache(oid); 374 if(result != null) 377 { 378 doInternalCache(oid, result, TYPE_CACHED_READ); 379 materializeFullObject(result); 380 if(log.isDebugEnabled()) log.debug("Materialized object from second level cache: " + oid); 381 } 382 } 383 if(result != null && log.isDebugEnabled()) 384 { 385 log.debug("Match for: " + oid); 386 } 387 return result; 388 } 389 390 402 public void materializeFullObject(Object target) 403 { 404 ClassDescriptor cld = broker.getClassDescriptor(target.getClass()); 405 final boolean forced = false; 407 if (forceProxies){ 408 broker.getReferenceBroker().retrieveProxyReferences(target, cld, forced); 409 broker.getReferenceBroker().retrieveProxyCollections(target, cld, forced); 410 }else{ 411 broker.getReferenceBroker().retrieveReferences(target, cld, forced); 412 broker.getReferenceBroker().retrieveCollections(target, cld, forced); 413 } 414 } 415 416 419 public void remove(Identity oid) 420 { 421 if(log.isDebugEnabled()) log.debug("Remove object " + oid); 422 sessionCache.remove(oid); 423 getApplicationCache().remove(oid); 424 } 425 426 429 public void clear() 430 { 431 sessionCache.clear(); 432 getApplicationCache().clear(); 433 } 434 435 438 public void cache(Identity oid, Object obj) 439 { 440 doInternalCache(oid, obj, TYPE_UNKNOWN); 441 } 442 443 public boolean cacheIfNew(Identity oid, Object obj) 444 { 445 boolean result = putToApplicationCache(oid, obj, true); 446 if(result) 447 { 448 CacheEntry entry = new CacheEntry(oid, obj, TYPE_CACHED_READ, queue); 449 putToSessionCache(oid, entry, true); 450 } 451 return result; 452 } 453 454 461 private void putToSessionCache(Identity oid, CacheEntry entry, boolean onlyIfNew) 462 { 463 if(onlyIfNew) 464 { 465 if(!sessionCache.containsKey(oid)) sessionCache.put(oid, entry); 467 } 468 else 469 { 470 sessionCache.put(oid, entry); 471 } 472 } 473 474 478 private void processQueue() 479 { 480 CacheEntry sv; 481 while((sv = (CacheEntry) queue.poll()) != null) 482 { 483 sessionCache.remove(sv.oid); 484 } 485 } 486 487 495 public void afterCommit(PBStateEvent event) 496 { 497 if(log.isDebugEnabled()) log.debug("afterCommit() call, push objects to application cache"); 498 if(invokeCounter != 0) 499 { 500 log.error("** Please check method calls of ObjectCacheTwoLevelImpl#enableMaterialization and" + 501 " ObjectCacheTwoLevelImpl#disableMaterialization, number of calls have to be equals **"); 502 } 503 try 504 { 505 pushToApplicationCache(TYPE_WRITE, TYPE_CACHED_READ); 507 } 508 finally 509 { 510 resetSessionCache(); 511 } 512 } 513 514 518 public void beforeClose(PBStateEvent event) 519 { 520 531 if(!broker.isInTransaction()) 532 { 533 if(log.isDebugEnabled()) log.debug("Clearing the session cache"); 534 resetSessionCache(); 535 } 536 } 537 538 541 public void beforeRollback(PBStateEvent event) 542 { 543 if(log.isDebugEnabled()) log.debug("beforeRollback()"); 544 resetSessionCache(); 545 } 546 547 public void afterOpen(PBStateEvent event) 548 { 549 } 550 551 public void beforeBegin(PBStateEvent event) 552 { 553 } 554 555 public void afterBegin(PBStateEvent event) 556 { 557 } 558 559 public void beforeCommit(PBStateEvent event) 560 { 561 } 562 563 public void afterRollback(PBStateEvent event) 564 { 565 } 566 568 572 576 static final class CacheEntry extends SoftReference implements Serializable 577 { 578 private int type; 579 private Identity oid; 580 581 public CacheEntry(Identity oid, Object obj, int type, final ReferenceQueue q) 582 { 583 super(obj, q); 584 this.oid = oid; 585 this.type = type; 586 } 587 } 588 589 590 public interface CopyStrategy 591 { 592 600 public Object read(PersistenceBroker broker, Object obj); 601 602 610 public Object write(PersistenceBroker broker, Object obj, Object oldObject); 611 } 612 613 public static class CopyStrategyImpl implements CopyStrategy 614 { 615 static final String CLASS_NAME_STR = "ojbClassName11"; 616 617 public CopyStrategyImpl() 618 { 619 } 620 621 public Object read(PersistenceBroker broker, Object obj) 622 { 623 HashMap source = (HashMap ) obj; 624 String className = (String ) source.get(CLASS_NAME_STR); 625 ClassDescriptor cld = broker.getDescriptorRepository().getDescriptorFor(className); 626 Object target = ClassHelper.buildNewObjectInstance(cld); 627 FieldDescriptor[] flds = cld.getFieldDescriptor(true); 629 FieldDescriptor fld; 630 int length = flds.length; 631 for(int i = 0; i < length; i++) 632 { 633 fld = flds[i]; 634 Object value = source.get(fld.getPersistentField().getName()); 636 if(value != null) value = fld.getJdbcType().getFieldType().copy(value); 638 value = fld.getFieldConversion().sqlToJava(value); 641 fld.getPersistentField().set(target, value); 643 } 644 return target; 645 } 646 647 public Object write(PersistenceBroker broker, Object obj, Object oldObject) 648 { 649 ClassDescriptor cld = broker.getClassDescriptor(obj.getClass()); 650 HashMap target = oldObject != null ? (HashMap ) oldObject : new HashMap (); 652 FieldDescriptor[] flds = cld.getFieldDescriptor(true); 654 FieldDescriptor fld; 655 int length = flds.length; 656 for(int i = 0; i < length; i++) 657 { 658 fld = flds[i]; 659 Object value = fld.getPersistentField().get(obj); 661 value = fld.getFieldConversion().javaToSql(value); 663 value = fld.getJdbcType().getFieldType().copy(value); 665 target.put(fld.getPersistentField().getName(), value); 666 } 667 target.put(CLASS_NAME_STR, obj.getClass().getName()); 668 return target; 669 } 670 } 671 } 672 | Popular Tags |