1 package org.apache.ojb.broker.cache; 2 3 17 18 import java.lang.ref.ReferenceQueue ; 19 import java.lang.ref.SoftReference ; 20 import java.util.ArrayList ; 21 import java.util.Hashtable ; 22 import java.util.Iterator ; 23 import java.util.List ; 24 import java.util.Map ; 25 import java.util.Properties ; 26 27 import org.apache.commons.lang.builder.ToStringBuilder; 28 import org.apache.commons.lang.builder.ToStringStyle; 29 import org.apache.ojb.broker.Identity; 30 import org.apache.ojb.broker.OJBRuntimeException; 31 import org.apache.ojb.broker.PBStateEvent; 32 import org.apache.ojb.broker.PBStateListener; 33 import org.apache.ojb.broker.PersistenceBroker; 34 import org.apache.ojb.broker.util.logging.Logger; 35 import org.apache.ojb.broker.util.logging.LoggerFactory; 36 37 129 public class ObjectCacheDefaultImpl implements ObjectCacheInternal, PBStateListener 130 { 131 private Logger log = LoggerFactory.getLogger(ObjectCacheDefaultImpl.class); 132 133 public static final String TIMEOUT_PROP = "timeout"; 134 public static final String AUTOSYNC_PROP = "autoSync"; 135 public static final String CACHING_KEY_TYPE_PROP = "cachingKeyType"; 136 public static final String SOFT_REFERENCES_PROP = "useSoftReferences"; 137 140 protected static final Map objectTable = new Hashtable (); 141 private static final ReferenceQueue queue = new ReferenceQueue (); 142 143 private static long hitCount = 0; 144 private static long failCount = 0; 145 private static long gcCount = 0; 146 147 protected PersistenceBroker broker; 148 private List identitiesInWork; 149 152 private long timeout = 1000 * 60 * 15; 153 private boolean useAutoSync = false; 154 162 private int cachingKeyType; 163 private boolean useSoftReferences = true; 164 165 public ObjectCacheDefaultImpl(PersistenceBroker broker, Properties prop) 166 { 167 this.broker = broker; 168 timeout = prop == null ? timeout : (Long.parseLong(prop.getProperty(TIMEOUT_PROP, "" + (60 * 15))) * 1000); 169 useSoftReferences = prop != null && (Boolean.valueOf((prop.getProperty(SOFT_REFERENCES_PROP, "true")).trim())).booleanValue(); 170 cachingKeyType = prop == null ? 0 : (Integer.parseInt(prop.getProperty(CACHING_KEY_TYPE_PROP, "0"))); 171 useAutoSync = prop != null && (Boolean.valueOf((prop.getProperty(AUTOSYNC_PROP, "false")).trim())).booleanValue(); 172 if(useAutoSync) 173 { 174 if(broker != null) 175 { 176 broker.addListener(this, true); 178 } 179 else 180 { 181 log.info("Can't enable property '" + AUTOSYNC_PROP + "', because given PB instance is null"); 182 } 183 } 184 identitiesInWork = new ArrayList (); 185 if(log.isEnabledFor(Logger.INFO)) 186 { 187 ToStringBuilder buf = new ToStringBuilder(this); 188 buf.append("timeout", timeout) 189 .append("useSoftReferences", useSoftReferences) 190 .append("cachingKeyType", cachingKeyType) 191 .append("useAutoSync", useAutoSync); 192 log.info("Setup cache: " + buf.toString()); 193 } 194 } 195 196 199 public void clear() 200 { 201 objectTable.clear(); 203 identitiesInWork.clear(); 204 } 205 206 public void doInternalCache(Identity oid, Object obj, int type) 207 { 208 if((obj != null)) 210 { 211 traceIdentity(oid); 212 synchronized(objectTable) 213 { 214 if(log.isDebugEnabled()) log.debug("Cache object " + oid); 215 objectTable.put(buildKey(oid), buildEntry(obj, oid)); 216 } 217 } 218 } 219 220 225 public void cache(Identity oid, Object obj) 226 { 227 doInternalCache(oid, obj, ObjectCacheInternal.TYPE_UNKNOWN); 228 } 229 230 public boolean cacheIfNew(Identity oid, Object obj) 231 { 232 boolean result = false; 234 Object key = buildKey(oid); 235 if((obj != null)) 236 { 237 synchronized(objectTable) 238 { 239 if(!objectTable.containsKey(key)) 240 { 241 objectTable.put(key, buildEntry(obj, oid)); 242 result = true; 243 } 244 } 245 if(result) traceIdentity(oid); 246 } 247 return result; 248 } 249 250 254 public Object lookup(Identity oid) 255 { 256 processQueue(); 257 hitCount++; 258 Object result = null; 259 260 CacheEntry entry = (CacheEntry) objectTable.get(buildKey(oid)); 261 if(entry != null) 262 { 263 result = entry.get(); 264 if(result == null || entry.getLifetime() < System.currentTimeMillis()) 265 { 266 270 gcCount++; 271 remove(oid); 272 result = null; 274 } 275 else 276 { 277 281 traceIdentity(oid); 282 if(log.isDebugEnabled()) log.debug("Object match " + oid); 283 } 284 } 285 else 286 { 287 failCount++; 288 } 289 return result; 290 } 291 292 295 public void remove(Identity oid) 296 { 297 if(oid != null) 299 { 300 removeTracedIdentity(oid); 301 objectTable.remove(buildKey(oid)); 302 if(log.isDebugEnabled()) log.debug("Remove object " + oid); 303 } 304 } 305 306 public String toString() 307 { 308 ToStringBuilder buf = new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE); 309 buf.append("Count of cached objects", objectTable.keySet().size()); 310 buf.append("Lookup hits", hitCount); 311 buf.append("Failures", failCount); 312 buf.append("Reclaimed", gcCount); 313 return buf.toString(); 314 } 315 316 private void traceIdentity(Identity oid) 317 { 318 if(useAutoSync && (broker != null) && broker.isInTransaction()) 319 { 320 identitiesInWork.add(oid); 321 } 322 } 323 324 private void removeTracedIdentity(Identity oid) 325 { 326 identitiesInWork.remove(oid); 327 } 328 329 private void synchronizeWithTracedObjects() 330 { 331 Identity oid; 332 log.info("tx was aborted," + 333 " remove " + identitiesInWork.size() + " traced (potentially modified) objects from cache"); 334 for(Iterator iterator = identitiesInWork.iterator(); iterator.hasNext();) 335 { 336 oid = (Identity) iterator.next(); 337 objectTable.remove(buildKey(oid)); 338 } 339 } 340 341 public void beforeRollback(PBStateEvent event) 342 { 343 synchronizeWithTracedObjects(); 344 identitiesInWork.clear(); 345 } 346 347 public void beforeCommit(PBStateEvent event) 348 { 349 } 351 352 public void beforeClose(PBStateEvent event) 353 { 354 359 if(!broker.isInTransaction()) 360 { 361 identitiesInWork.clear(); 362 } 363 } 364 365 public void afterRollback(PBStateEvent event) 366 { 367 } 368 369 public void afterCommit(PBStateEvent event) 370 { 371 identitiesInWork.clear(); 372 } 373 374 public void afterBegin(PBStateEvent event) 375 { 376 } 377 378 public void beforeBegin(PBStateEvent event) 379 { 380 } 381 382 public void afterOpen(PBStateEvent event) 383 { 384 } 385 386 private CacheEntry buildEntry(Object obj, Identity oid) 387 { 388 if(useSoftReferences) 389 { 390 return new CacheEntrySoft(obj, oid, queue, timeout); 391 } 392 else 393 { 394 return new CacheEntryHard(obj, oid, timeout); 395 } 396 } 397 398 private void processQueue() 399 { 400 CacheEntry sv; 401 while((sv = (CacheEntry) queue.poll()) != null) 402 { 403 removeTracedIdentity(sv.getOid()); 404 objectTable.remove(buildKey(sv.getOid())); 405 } 406 } 407 408 private Object buildKey(Identity oid) 409 { 410 Object key; 411 switch(cachingKeyType) 412 { 413 case 0: 414 key = oid; 415 break; 416 case 1: 417 key = new OrderedTuple(oid, broker.getPBKey().getAlias()); 418 break; 419 case 2: 420 425 key = new OrderedTuple(oid, 426 new Integer (broker.getDescriptorRepository().hashCode())); 427 break; 428 case 3: 429 key = new OrderedTuple(oid, broker.getPBKey().getAlias(), 430 new Integer (broker.getDescriptorRepository().hashCode())); 431 break; 432 default: 433 throw new OJBRuntimeException("Unexpected error, 'cacheType =" + cachingKeyType + "' was not supported"); 434 } 435 return key; 436 } 437 438 439 449 static final class OrderedTuple 450 { 451 private static int[] multipliers = 452 new int[]{13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 51}; 453 454 private Object [] elements; 455 private int hashCode; 456 457 public OrderedTuple(Object element) 458 { 459 elements = new Object [1]; 460 elements[0] = element; 461 hashCode = calcHashCode(); 462 } 463 464 public OrderedTuple(Object element1, Object element2) 465 { 466 elements = new Object [2]; 467 elements[0] = element1; 468 elements[1] = element2; 469 hashCode = calcHashCode(); 470 } 471 472 public OrderedTuple(Object element1, Object element2, Object element3) 473 { 474 elements = new Object [3]; 475 elements[0] = element1; 476 elements[1] = element2; 477 elements[2] = element3; 478 hashCode = calcHashCode(); 479 } 480 481 public OrderedTuple(Object [] elements) 482 { 483 this.elements = elements; 484 this.hashCode = calcHashCode(); 485 } 486 487 private int calcHashCode() 488 { 489 int code = 7; 490 for(int i = 0; i < elements.length; i++) 491 { 492 int m = i % multipliers.length; 493 code += elements[i].hashCode() * multipliers[m]; 494 } 495 return code; 496 } 497 498 public boolean equals(Object obj) 499 { 500 if(!(obj instanceof OrderedTuple)) 501 { 502 return false; 503 } 504 else 505 { 506 OrderedTuple other = (OrderedTuple) obj; 507 if(this.hashCode != other.hashCode) 508 { 509 return false; 510 } 511 else if(this.elements.length != other.elements.length) 512 { 513 return false; 514 } 515 else 516 { 517 for(int i = 0; i < elements.length; i++) 518 { 519 if(!this.elements[i].equals(other.elements[i])) 520 { 521 return false; 522 } 523 } 524 return true; 525 } 526 } 527 } 528 529 public int hashCode() 530 { 531 return hashCode; 532 } 533 534 public String toString() 535 { 536 StringBuffer s = new StringBuffer (); 537 s.append('{'); 538 for(int i = 0; i < elements.length; i++) 539 { 540 s.append(elements[i]).append('#').append(elements[i].hashCode()).append(','); 541 } 542 s.setCharAt(s.length() - 1, '}'); 543 s.append("#").append(hashCode); 544 return s.toString(); 545 } 546 } 547 548 interface CacheEntry 552 { 553 Object get(); 554 Identity getOid(); 555 long getLifetime(); 556 } 557 558 final static class CacheEntrySoft extends SoftReference implements CacheEntry 559 { 560 private final long lifetime; 561 private final Identity oid; 562 563 CacheEntrySoft(Object object, final Identity k, final ReferenceQueue q, long timeout) 564 { 565 super(object, q); 566 oid = k; 567 if(timeout < 0) 569 { 570 lifetime = Long.MAX_VALUE; 571 } 572 else 573 { 574 lifetime = System.currentTimeMillis() + timeout; 575 } 576 } 577 578 public Identity getOid() 579 { 580 return oid; 581 } 582 583 public long getLifetime() 584 { 585 return lifetime; 586 } 587 } 588 589 final static class CacheEntryHard implements CacheEntry 590 { 591 private final long lifetime; 592 private final Identity oid; 593 private Object obj; 594 595 CacheEntryHard(Object object, final Identity k, long timeout) 596 { 597 obj = object; 598 oid = k; 599 if(timeout < 0) 601 { 602 lifetime = Long.MAX_VALUE; 603 } 604 else 605 { 606 lifetime = System.currentTimeMillis() + timeout; 607 } 608 } 609 610 public Object get() 611 { 612 return obj; 613 } 614 615 public Identity getOid() 616 { 617 return oid; 618 } 619 620 public long getLifetime() 621 { 622 return lifetime; 623 } 624 } 625 } 626 | Popular Tags |