[jboss-user] [JBossCache] - Re: TreeCache/Hibernate/JBossAS optimistic locking problem
steve.ebersole@jboss.com
do-not-reply at jboss.com
Wed Jul 12 23:07:39 EDT 2006
| //$Id: OptimisticTreeCache.java 9965 2006-05-30 13:00:28 -0500 (Tue, 30 May 2006) steve.ebersole at jboss.com $
| package org.hibernate.cache;
|
| import java.util.HashMap;
| import java.util.Iterator;
| import java.util.Map;
| import java.util.Set;
| import java.util.Comparator;
|
|
| import org.apache.commons.logging.Log;
| import org.apache.commons.logging.LogFactory;
| import org.jboss.cache.Fqn;
| import org.jboss.cache.optimistic.DataVersion;
| import org.jboss.cache.config.Option;
| import org.jboss.cache.lock.TimeoutException;
|
| /**
| * Represents a particular region within the given JBossCache TreeCache
| * utilizing TreeCache's optimistic locking capabilities.
| *
| * @see OptimisticTreeCacheProvider for more details
| *
| * @author Steve Ebersole
| */
| public class OptimisticTreeCache implements OptimisticCache {
|
| // todo : eventually merge this with TreeCache and just add optional opt-lock support there.
|
| private static final Log log = LogFactory.getLog( OptimisticTreeCache.class);
|
| private static final String ITEM = "item";
|
| private org.jboss.cache.TreeCache cache;
| private final String regionName;
| private final Fqn regionFqn;
| private OptimisticCacheSource source;
|
| public OptimisticTreeCache(org.jboss.cache.TreeCache cache, String regionName)
| throws CacheException {
| this.cache = cache;
| this.regionName = regionName;
| this.regionFqn = Fqn.fromString( regionName.replace( '.', '/' ) );
| }
|
|
| // OptimisticCache impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| public void setSource(OptimisticCacheSource source) {
| this.source = source;
| }
|
| public void writeInsert(Object key, Object value, Object currentVersion) {
| writeUpdate( key, value, currentVersion, null );
| }
|
| public void writeUpdate(Object key, Object value, Object currentVersion, Object previousVersion) {
| try {
| Option option = new Option();
| DataVersion dv = ( source != null && source.isVersioned() )
| ? new DataVersionAdapter( currentVersion, previousVersion, source.getVersionComparator(), source.toString() )
| : NonLockingDataVersion.INSTANCE;
| option.setDataVersion( dv );
| cache.put( new Fqn( regionFqn, key ), ITEM, value, option );
| }
| catch ( Exception e ) {
| throw new CacheException( e );
| }
| }
|
| public void writeLoad(Object key, Object value, Object currentVersion) {
| try {
| Option option = new Option();
| option.setFailSilently( true );
| option.setDataVersion( NonLockingDataVersion.INSTANCE );
| cache.remove( new Fqn( regionFqn, key ), "ITEM", option );
|
| option = new Option();
| option.setFailSilently( true );
| DataVersion dv = ( source != null && source.isVersioned() )
| ? new DataVersionAdapter( currentVersion, currentVersion, source.getVersionComparator(), source.toString() )
| : NonLockingDataVersion.INSTANCE;
| option.setDataVersion( dv );
| cache.put( new Fqn( regionFqn, key ), ITEM, value, option );
| }
| catch (Exception e) {
| throw new CacheException(e);
| }
| }
|
|
| // Cache impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| public Object get(Object key) throws CacheException {
| try {
| Option option = new Option();
| option.setFailSilently( true );
| // option.setDataVersion( NonLockingDataVersion.INSTANCE );
| return cache.get( new Fqn( regionFqn, key ), ITEM, option );
| }
| catch (Exception e) {
| throw new CacheException(e);
| }
| }
|
| public Object read(Object key) throws CacheException {
| try {
| return cache.get( new Fqn( regionFqn, key ), ITEM );
| }
| catch (Exception e) {
| throw new CacheException(e);
| }
| }
|
| public void update(Object key, Object value) throws CacheException {
| try {
| Option option = new Option();
| option.setDataVersion( NonLockingDataVersion.INSTANCE );
| cache.put( new Fqn( regionFqn, key ), ITEM, value, option );
| }
| catch (Exception e) {
| throw new CacheException(e);
| }
| }
|
| public void put(Object key, Object value) throws CacheException {
| try {
| log.trace( "performing put() into region [" + regionName + "]" );
| // do the put outside the scope of the JTA txn
| Option option = new Option();
| option.setFailSilently( true );
| option.setDataVersion( NonLockingDataVersion.INSTANCE );
| cache.put( new Fqn( regionFqn, key ), ITEM, value, option );
| }
| catch (TimeoutException te) {
| //ignore!
| log.debug("ignoring write lock acquisition failure");
| }
| catch (Exception e) {
| throw new CacheException(e);
| }
| }
|
| public void remove(Object key) throws CacheException {
| try {
| // tree cache in optimistic mode seems to have as very difficult
| // time with remove calls on non-existent nodes (NPEs)...
| if ( cache.get( new Fqn( regionFqn, key ), ITEM ) != null ) {
| Option option = new Option();
| option.setDataVersion( NonLockingDataVersion.INSTANCE );
| cache.remove( new Fqn( regionFqn, key ), option );
| }
| else {
| log.trace( "skipping remove() call as the underlying node did not seem to exist" );
| }
| }
| catch (Exception e) {
| throw new CacheException(e);
| }
| }
|
| public void clear() throws CacheException {
| try {
| Option option = new Option();
| option.setDataVersion( NonLockingDataVersion.INSTANCE );
| cache.remove( regionFqn, option );
| }
| catch (Exception e) {
| throw new CacheException(e);
| }
| }
|
| public void destroy() throws CacheException {
| try {
| Option option = new Option();
| option.setCacheModeLocal( true );
| option.setFailSilently( true );
| option.setDataVersion( NonLockingDataVersion.INSTANCE );
| cache.remove( regionFqn, option );
| }
| catch( Exception e ) {
| throw new CacheException( e );
| }
| }
|
| public void lock(Object key) throws CacheException {
| throw new UnsupportedOperationException( "TreeCache is a fully transactional cache" + regionName );
| }
|
| public void unlock(Object key) throws CacheException {
| throw new UnsupportedOperationException( "TreeCache is a fully transactional cache: " + regionName );
| }
|
| public long nextTimestamp() {
| return System.currentTimeMillis() / 100;
| }
|
| public int getTimeout() {
| return 600; //60 seconds
| }
|
| public String getRegionName() {
| return regionName;
| }
|
| public long getSizeInMemory() {
| return -1;
| }
|
| public long getElementCountInMemory() {
| try {
| Set children = cache.getChildrenNames( regionFqn );
| return children == null ? 0 : children.size();
| }
| catch (Exception e) {
| throw new CacheException(e);
| }
| }
|
| public long getElementCountOnDisk() {
| return 0;
| }
|
| public Map toMap() {
| try {
| Map result = new HashMap();
| Set childrenNames = cache.getChildrenNames( regionFqn );
| if (childrenNames != null) {
| Iterator iter = childrenNames.iterator();
| while ( iter.hasNext() ) {
| Object key = iter.next();
| result.put(
| key,
| cache.get( new Fqn( regionFqn, key ), ITEM )
| );
| }
| }
| return result;
| }
| catch (Exception e) {
| throw new CacheException(e);
| }
| }
|
| public String toString() {
| return "OptimisticTreeCache(" + regionName + ')';
| }
|
| public static class DataVersionAdapter implements DataVersion {
| private final Object currentVersion;
| private final Object previousVersion;
| private final Comparator versionComparator;
| private final String sourceIdentifer;
|
| public DataVersionAdapter(Object currentVersion, Object previousVersion, Comparator versionComparator, String sourceIdentifer) {
| this.currentVersion = currentVersion;
| this.previousVersion = previousVersion;
| this.versionComparator = versionComparator;
| this.sourceIdentifer = sourceIdentifer;
| log.trace( "created " + this );
| }
|
| /**
| * newerThan() call is dispatched against the DataVersion currently
| * associated with the node; the passed dataVersion param is the
| * DataVersion associated with the data we are trying to put into
| * the node.
| * <p/>
| * we are expected to return true in the case where we (the current
| * node DataVersion) are newer that then incoming value. Returning
| * true here essentially means that a optimistic lock failure has
| * occured (because conversely, the value we are trying to put into
| * the node is "older than" the value already there...)
| */
| public boolean newerThan(DataVersion dataVersion) {
| log.trace( "checking [" + this + "] against [" + dataVersion + "]" );
| if ( dataVersion instanceof CircumventChecksDataVersion ) {
| log.trace( "skipping lock checks..." );
| return false;
| }
| else if ( dataVersion instanceof NonLockingDataVersion ) {
| // can happen because of the multiple ways Cache.remove()
| // can be invoked :(
| log.trace( "skipping lock checks..." );
| return false;
| }
| DataVersionAdapter other = ( DataVersionAdapter ) dataVersion;
| if ( other.previousVersion == null ) {
| log.warn( "Unexpected optimistic lock check on inserting data" );
| // work around the "feature" where tree cache is validating the
| // inserted node during the next transaction. no idea...
| if ( this == dataVersion ) {
| log.trace( "skipping lock checks due to same DV instance" );
| return false;
| }
| }
| return versionComparator.compare( currentVersion, other.previousVersion ) >= 1;
| }
|
| public String toString() {
| return super.toString() + " [current=" + currentVersion + ", previous=" + previousVersion + ", src=" + sourceIdentifer + "]";
| }
| }
|
| /**
| * Used in regions where no locking should ever occur. This includes query-caches,
| * update-timestamps caches, collection caches, and entity caches where the entity
| * is not versioned.
| */
| public static class NonLockingDataVersion implements DataVersion {
| public static final DataVersion INSTANCE = new NonLockingDataVersion();
| public boolean newerThan(DataVersion dataVersion) {
| log.trace( "non locking lock check...");
| return false;
| }
| }
|
| /**
| * Used to signal to a DataVersionAdapter to simply not perform any checks. This
| * is currently needed for proper handling of remove() calls for entity cache regions
| * (we do not know the version info...).
| */
| public static class CircumventChecksDataVersion implements DataVersion {
| public static final DataVersion INSTANCE = new CircumventChecksDataVersion();
| public boolean newerThan(DataVersion dataVersion) {
| throw new CacheException( "optimistic locking checks should never happen on CircumventChecksDataVersion" );
| }
| }
| }
|
|
View the original post : http://www.jboss.com/index.html?module=bb&op=viewtopic&p=3957608#3957608
Reply to the post : http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&p=3957608
More information about the jboss-user
mailing list