1 package org.jboss.ejb3.entity; 2 3 import java.util.Comparator ; 4 import java.util.HashMap ; 5 import java.util.Iterator ; 6 import java.util.Map ; 7 import java.util.Set ; 8 9 import org.apache.commons.logging.Log; 10 import org.apache.commons.logging.LogFactory; 11 import org.hibernate.cache.CacheException; 12 import org.hibernate.cache.OptimisticCache; 13 import org.hibernate.cache.OptimisticCacheSource; 14 import org.jboss.cache.Fqn; 15 import org.jboss.cache.InvocationContext; 16 import org.jboss.cache.Node; 17 import org.jboss.cache.config.Option; 18 import org.jboss.cache.lock.TimeoutException; 19 import org.jboss.cache.optimistic.DataVersion; 20 21 public class OptimisticJBCCache implements OptimisticCache 22 { 23 25 private static final Log log = LogFactory.getLog( OptimisticJBCCache.class); 26 27 private static final String ITEM = "item"; 28 29 private static final Option NONLOCKING_OPTION = new Option(); 30 private static final Option FAIL_SILENT_OPTION = new Option(); 31 private static final Option FAIL_SILENT_NONLOCKING_OPTION = new Option(); 32 33 static 34 { 35 FAIL_SILENT_OPTION.setFailSilently( true ); 36 37 FAIL_SILENT_NONLOCKING_OPTION.setFailSilently( true ); 38 FAIL_SILENT_NONLOCKING_OPTION.setDataVersion( NonLockingDataVersion.INSTANCE ); 39 NONLOCKING_OPTION.setDataVersion( NonLockingDataVersion.INSTANCE ); 40 } 41 42 private org.jboss.cache.Cache cache; 43 private final String regionName; 44 private final Fqn regionFqn; 45 private OptimisticCacheSource source; 46 47 public OptimisticJBCCache(org.jboss.cache.Cache cache, String regionName) 48 throws CacheException { 49 this.cache = cache; 50 this.regionName = regionName; 51 this.regionFqn = Fqn.fromString( regionName.replace( '.', '/' ) ); 52 } 53 54 55 57 public void setSource(OptimisticCacheSource source) { 58 this.source = source; 59 } 60 61 public void writeInsert(Object key, Object value, Object currentVersion) { 62 writeUpdate( key, value, currentVersion, null ); 63 } 64 65 public void writeUpdate(Object key, Object value, Object currentVersion, Object previousVersion) 66 { 67 InvocationContext ctx = cache.getInvocationContext(); 68 Option existing = ctx.getOptionOverrides(); 69 try { 70 Option option = new Option(); 71 DataVersion dv = ( source != null && source.isVersioned() ) 72 ? new DataVersionAdapter( currentVersion, previousVersion, source.getVersionComparator(), source.toString() ) 73 : NonLockingDataVersion.INSTANCE; 74 option.setDataVersion( dv ); 75 76 ctx.setOptionOverrides(option); 77 cache.put( new Fqn( regionFqn, key ), ITEM, value); 78 } 79 catch ( Exception e ) { 80 throw new CacheException( e ); 81 } 82 finally 83 { 84 ctx.setOptionOverrides(existing); 85 } 86 } 87 88 public void writeLoad(Object key, Object value, Object currentVersion) 89 { 90 InvocationContext ctx = cache.getInvocationContext(); 91 Option existing = ctx.getOptionOverrides(); 92 try { 93 94 ctx.setOptionOverrides(FAIL_SILENT_NONLOCKING_OPTION); 95 cache.remove( new Fqn( regionFqn, key ), "ITEM"); 96 97 Option option = new Option(); 98 option.setFailSilently( true ); 99 DataVersion dv = ( source != null && source.isVersioned() ) 100 ? new DataVersionAdapter( currentVersion, currentVersion, source.getVersionComparator(), source.toString() ) 101 : NonLockingDataVersion.INSTANCE; 102 option.setDataVersion( dv ); 103 104 ctx = cache.getInvocationContext(); 105 ctx.setOptionOverrides(option); 106 cache.put( new Fqn( regionFqn, key ), ITEM, value); 107 } 108 catch (Exception e) { 109 throw new CacheException(e); 110 } 111 finally 112 { 113 ctx.setOptionOverrides(existing); 114 } 115 } 116 117 118 120 public Object get(Object key) throws CacheException 121 { 122 InvocationContext ctx = cache.getInvocationContext(); 123 Option existing = ctx.getOptionOverrides(); 124 try { 125 126 ctx.setOptionOverrides(FAIL_SILENT_OPTION); 128 return cache.get( new Fqn( regionFqn, key ), ITEM); 129 } 130 catch (Exception e) { 131 throw new CacheException(e); 132 } 133 finally 134 { 135 ctx.setOptionOverrides(existing); 136 } 137 } 138 139 public Object read(Object key) throws CacheException { 140 try { 141 return cache.get( new Fqn( regionFqn, key ), ITEM ); 142 } 143 catch (Exception e) { 144 throw new CacheException(e); 145 } 146 } 147 148 public void update(Object key, Object value) throws CacheException 149 { 150 InvocationContext ctx = cache.getInvocationContext(); 151 Option existing = ctx.getOptionOverrides(); 152 try { 153 ctx.setOptionOverrides(NONLOCKING_OPTION); 154 cache.put( new Fqn( regionFqn, key ), ITEM, value ); 155 } 156 catch (Exception e) { 157 throw new CacheException(e); 158 } 159 finally 160 { 161 ctx.setOptionOverrides(existing); 162 } 163 } 164 165 public void put(Object key, Object value) throws CacheException 166 { 167 InvocationContext ctx = cache.getInvocationContext(); 168 Option existing = ctx.getOptionOverrides(); 169 try { 170 log.trace( "performing put() into region [" + regionName + "]" ); 171 ctx.setOptionOverrides(FAIL_SILENT_NONLOCKING_OPTION); 173 cache.put( new Fqn( regionFqn, key ), ITEM, value ); 174 } 175 catch (TimeoutException te) { 176 log.debug("ignoring write lock acquisition failure"); 178 } 179 catch (Exception e) { 180 throw new CacheException(e); 181 } 182 finally 183 { 184 ctx.setOptionOverrides(existing); 185 } 186 } 187 188 public void remove(Object key) throws CacheException 189 { 190 InvocationContext ctx = cache.getInvocationContext(); 191 Option existing = ctx.getOptionOverrides(); 192 try { 193 if ( cache.get( new Fqn( regionFqn, key ), ITEM ) != null ) { 196 Option option = new Option(); 197 option.setDataVersion( NonLockingDataVersion.INSTANCE ); 198 ctx.setOptionOverrides(option); 199 cache.removeNode( new Fqn( regionFqn, key ) ); 200 } 201 else { 202 log.trace( "skipping remove() call as the underlying node did not seem to exist" ); 203 } 204 } 205 catch (Exception e) { 206 throw new CacheException(e); 207 } 208 finally 209 { 210 ctx.setOptionOverrides(existing); 211 } 212 } 213 214 public void clear() throws CacheException 215 { 216 InvocationContext ctx = cache.getInvocationContext(); 217 Option existing = ctx.getOptionOverrides(); 218 try { 219 Option option = new Option(); 220 option.setDataVersion( NonLockingDataVersion.INSTANCE ); 221 ctx.setOptionOverrides(option); 222 cache.removeNode( regionFqn ); 223 } 224 catch (Exception e) { 225 throw new CacheException(e); 226 } 227 finally 228 { 229 ctx.setOptionOverrides(existing); 230 } 231 } 232 233 public void destroy() throws CacheException 234 { 235 InvocationContext ctx = cache.getInvocationContext(); 236 Option existing = ctx.getOptionOverrides(); 237 try { 238 Option option = new Option(); 239 option.setCacheModeLocal( true ); 240 option.setFailSilently( true ); 241 option.setDataVersion( NonLockingDataVersion.INSTANCE ); 242 ctx.setOptionOverrides(option); 243 cache.removeNode( regionFqn ); 244 } 245 catch( Exception e ) { 246 throw new CacheException( e ); 247 } 248 finally 249 { 250 ctx.setOptionOverrides(existing); 251 } 252 } 253 254 public void lock(Object key) throws CacheException { 255 throw new UnsupportedOperationException ( "TreeCache is a fully transactional cache" + regionName ); 256 } 257 258 public void unlock(Object key) throws CacheException { 259 throw new UnsupportedOperationException ( "TreeCache is a fully transactional cache: " + regionName ); 260 } 261 262 public long nextTimestamp() { 263 return System.currentTimeMillis() / 100; 264 } 265 266 public int getTimeout() { 267 return 600; } 269 270 public String getRegionName() { 271 return regionName; 272 } 273 274 public long getSizeInMemory() { 275 return -1; 276 } 277 278 public long getElementCountInMemory() { 279 try { 280 Set children = getChildrenNames(); 281 return children == null ? 0 : children.size(); 282 } 283 catch (Exception e) { 284 throw new CacheException(e); 285 } 286 } 287 288 public long getElementCountOnDisk() { 289 return 0; 290 } 291 292 public Map toMap() { 293 try { 294 Map result = new HashMap (); 295 Set childrenNames = getChildrenNames(); 296 if (childrenNames != null) { 297 Iterator iter = childrenNames.iterator(); 298 while ( iter.hasNext() ) { 299 Object key = iter.next(); 300 result.put( 301 key, 302 cache.get( new Fqn( regionFqn, key ), ITEM ) 303 ); 304 } 305 } 306 return result; 307 } 308 catch (Exception e) { 309 throw new CacheException(e); 310 } 311 } 312 313 private Set getChildrenNames() 314 { 315 try { 316 Node base = cache.getChild( regionFqn ); 317 return base == null ? null : base.getChildrenNames(); 318 } 319 catch (Exception e) { 320 throw new CacheException(e); 321 } 322 } 323 324 public String toString() { 325 return "OptimisticTreeCache(" + regionName + ')'; 326 } 327 328 public static class DataVersionAdapter implements DataVersion { 329 private final Object currentVersion; 330 private final Object previousVersion; 331 private final Comparator versionComparator; 332 private final String sourceIdentifer; 333 334 public DataVersionAdapter(Object currentVersion, Object previousVersion, Comparator versionComparator, String sourceIdentifer) { 335 this.currentVersion = currentVersion; 336 this.previousVersion = previousVersion; 337 this.versionComparator = versionComparator; 338 this.sourceIdentifer = sourceIdentifer; 339 log.trace( "created " + this ); 340 } 341 342 354 public boolean newerThan(DataVersion dataVersion) { 355 log.trace( "checking [" + this + "] against [" + dataVersion + "]" ); 356 if ( dataVersion instanceof CircumventChecksDataVersion ) { 357 log.trace( "skipping lock checks..." ); 358 return false; 359 } 360 else if ( dataVersion instanceof NonLockingDataVersion ) { 361 log.trace( "skipping lock checks..." ); 364 return false; 365 } 366 DataVersionAdapter other = ( DataVersionAdapter ) dataVersion; 367 if ( other.previousVersion == null ) { 368 log.warn( "Unexpected optimistic lock check on inserting data" ); 369 if ( this == dataVersion ) { 372 log.trace( "skipping lock checks due to same DV instance" ); 373 return false; 374 } 375 } 376 return versionComparator.compare( currentVersion, other.previousVersion ) >= 1; 377 } 378 379 public String toString() { 380 return super.toString() + " [current=" + currentVersion + ", previous=" + previousVersion + ", SRC=" + sourceIdentifer + "]"; 381 } 382 } 383 384 389 public static class NonLockingDataVersion implements DataVersion { 390 public static final DataVersion INSTANCE = new NonLockingDataVersion(); 391 public boolean newerThan(DataVersion dataVersion) { 392 log.trace( "non locking lock check..."); 393 return false; 394 } 395 } 396 397 402 public static class CircumventChecksDataVersion implements DataVersion { 403 public static final DataVersion INSTANCE = new CircumventChecksDataVersion(); 404 public boolean newerThan(DataVersion dataVersion) { 405 throw new CacheException( "optimistic locking checks should never happen on CircumventChecksDataVersion" ); 406 } 407 } 408 409 } 410 | Popular Tags |