You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2013/09/15 16:45:16 UTC

git commit: ISIS-536: thread local as a means of temporarily disabling concurrency checking.

Updated Branches:
  refs/heads/master 3aacb262a -> 4bdc6adb4


ISIS-536: thread local as a means of temporarily disabling concurrency checking.


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/4bdc6adb
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/4bdc6adb
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/4bdc6adb

Branch: refs/heads/master
Commit: 4bdc6adb4988adbb64010aa2e5c03b44134f6f9c
Parents: 3aacb26
Author: Dan Haywood <da...@apache.org>
Authored: Sun Sep 15 15:45:03 2013 +0100
Committer: Dan Haywood <da...@apache.org>
Committed: Sun Sep 15 15:45:03 2013 +0100

----------------------------------------------------------------------
 .../persistence/FrameworkSynchronizer.java      | 72 +++++++++-----------
 .../scimpi/dispatcher/action/ActionAction.java  |  4 +-
 .../metamodel/adapter/mgr/AdapterManager.java   |  3 +
 .../core/metamodel/adapter/oid/RootOid.java     |  2 -
 .../metamodel/adapter/oid/RootOidDefault.java   | 14 ----
 .../adapter/version/ConcurrencyException.java   | 21 ++++++
 .../persistence/adapter/PojoAdapter.java        | 21 ++++--
 .../adaptermanager/AdapterManagerDefault.java   | 27 ++++++--
 8 files changed, 99 insertions(+), 65 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/4bdc6adb/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/persistence/FrameworkSynchronizer.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/persistence/FrameworkSynchronizer.java b/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/persistence/FrameworkSynchronizer.java
index 8046643..edbeec0 100644
--- a/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/persistence/FrameworkSynchronizer.java
+++ b/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/persistence/FrameworkSynchronizer.java
@@ -19,19 +19,15 @@
 package org.apache.isis.objectstore.jdo.datanucleus.persistence;
 
 import java.text.MessageFormat;
-import java.util.Arrays;
-import java.util.List;
 import java.util.concurrent.Callable;
 
 import javax.jdo.JDOHelper;
 import javax.jdo.PersistenceManager;
 import javax.jdo.spi.PersistenceCapable;
 
-import org.datanucleus.api.jdo.NucleusJDOHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import org.apache.isis.applib.filter.Filter;
 import org.apache.isis.core.commons.authentication.AuthenticationSession;
 import org.apache.isis.core.commons.exceptions.IsisException;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
@@ -45,7 +41,6 @@ import org.apache.isis.core.metamodel.facets.object.callbacks.CallbackFacet;
 import org.apache.isis.core.metamodel.facets.object.callbacks.CallbackUtils;
 import org.apache.isis.core.metamodel.facets.object.callbacks.PersistedCallbackFacet;
 import org.apache.isis.core.metamodel.facets.object.callbacks.UpdatedCallbackFacet;
-import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.isis.core.runtime.persistence.PersistorUtil;
 import org.apache.isis.core.runtime.system.context.IsisContext;
 import org.apache.isis.core.runtime.system.persistence.OidGenerator;
@@ -75,43 +70,57 @@ public class FrameworkSynchronizer {
             public void run() {
                 final Version datastoreVersion = getVersionIfAny(pojo);
                 
-                final RootOid oid ;
-                ObjectAdapter adapter = getAdapterManager().getAdapterFor(pojo);
-                if(adapter != null) {
+                final RootOid originalOid ;
+                ObjectAdapter originalAdapter = getAdapterManager().getAdapterFor(pojo);
+                if(originalAdapter != null) {
                     ensureRootObject(pojo);
-                    oid = (RootOid) adapter.getOid();
+                    originalOid = (RootOid) originalAdapter.getOid();
 
-                    final Version previousVersion = adapter.getVersion();
+                    final Version originalVersion = originalAdapter.getVersion();
 
                     // sync the pojo held by the adapter with that just loaded
-                    getPersistenceSession().remapRecreatedPojo(adapter, pojo);
-
+                    getPersistenceSession().remapRecreatedPojo(originalAdapter, pojo);
+                    
                     // since there was already an adapter, do concurrency check
-                    if(previousVersion != null && datastoreVersion != null) {
-                        if(previousVersion.different(datastoreVersion)) {
-                            getCurrentTransaction().setAbortCause(new ConcurrencyException(getAuthenticationSession().getUserName(), oid, previousVersion, datastoreVersion));
+                    // (but don't set abort cause if checking is suppressed through thread-local)
+                    final RootOid thisOid = originalOid;
+                    final Version thisVersion = originalVersion;
+                    final Version otherVersion = datastoreVersion;
+                    
+                    if(thisVersion != null && 
+                       otherVersion != null && 
+                       thisVersion.different(otherVersion)) {
+
+                        if(ConcurrencyException.concurrencyChecking.get().isChecking()) {
+                            LOG.info("concurrency conflict detected on " + thisOid + " (" + otherVersion + ")");
+                            final String currentUser = getAuthenticationSession().getUserName();
+                            final ConcurrencyException abortCause = new ConcurrencyException(currentUser, thisOid, thisVersion, otherVersion);
+                            getCurrentTransaction().setAbortCause(abortCause);
+
+                        } else {
+                            LOG.warn("concurrency conflict detected but suppressed, on " + thisOid + " (" + otherVersion + ")");
                         }
                     }
                 } else {
                     final OidGenerator oidGenerator = getOidGenerator();
-                    oid = oidGenerator.createPersistent(pojo, null);
+                    originalOid = oidGenerator.createPersistent(pojo, null);
                     
                     // it appears to be possible that there is already an adapter for this Oid, 
                     // ie from ObjectStore#resolveImmediately()
-                    adapter = getAdapterManager().getAdapterFor(oid);
-                    if(adapter != null) {
-                        getPersistenceSession().remapRecreatedPojo(adapter, pojo);
+                    originalAdapter = getAdapterManager().getAdapterFor(originalOid);
+                    if(originalAdapter != null) {
+                        getPersistenceSession().remapRecreatedPojo(originalAdapter, pojo);
                     } else {
-                        adapter = getPersistenceSession().mapRecreatedPojo(oid, pojo);
+                        originalAdapter = getPersistenceSession().mapRecreatedPojo(originalOid, pojo);
                     }
                 }
-                if(!adapter.isResolved()) {
-                    PersistorUtil.startResolving(adapter);
-                    PersistorUtil.toEndState(adapter);
+                if(!originalAdapter.isResolved()) {
+                    PersistorUtil.startResolving(originalAdapter);
+                    PersistorUtil.toEndState(originalAdapter);
                 }
-                adapter.setVersion(datastoreVersion);
+                originalAdapter.setVersion(datastoreVersion);
                 if(pojo.jdoIsDeleted()) {
-                    adapter.changeState(ResolveState.DESTROYED);
+                    originalAdapter.changeState(ResolveState.DESTROYED);
                 }
 
                 ensureFrameworksInAgreement(pojo);
@@ -347,19 +356,6 @@ public class FrameworkSynchronizer {
     }
 
     @SuppressWarnings("unused")
-    private static Filter<ObjectAssociation> dirtyFieldFilterFor(final PersistenceCapable pojo) {
-        String[] dirtyFields = NucleusJDOHelper.getDirtyFields(pojo, JDOHelper.getPersistenceManager(pojo));
-        final List<String> dirtyFieldList = Arrays.asList(dirtyFields);
-        Filter<ObjectAssociation> dirtyFieldsFilter = new Filter<ObjectAssociation>() {
-            @Override
-            public boolean accept(final ObjectAssociation t) {
-                String id = t.getId();
-                return dirtyFieldList.contains(id);
-            }};
-        return dirtyFieldsFilter;
-    }
-
-    @SuppressWarnings("unused")
     private void ensureObjectNotLoaded(final PersistenceCapable pojo) {
         final ObjectAdapter adapter = getAdapterManager().getAdapterFor(pojo);
         if(adapter != null) {

http://git-wip-us.apache.org/repos/asf/isis/blob/4bdc6adb/component/viewer/scimpi/dispatcher/src/main/java/org/apache/isis/viewer/scimpi/dispatcher/action/ActionAction.java
----------------------------------------------------------------------
diff --git a/component/viewer/scimpi/dispatcher/src/main/java/org/apache/isis/viewer/scimpi/dispatcher/action/ActionAction.java b/component/viewer/scimpi/dispatcher/src/main/java/org/apache/isis/viewer/scimpi/dispatcher/action/ActionAction.java
index 9ef481d..f181cc9 100644
--- a/component/viewer/scimpi/dispatcher/src/main/java/org/apache/isis/viewer/scimpi/dispatcher/action/ActionAction.java
+++ b/component/viewer/scimpi/dispatcher/src/main/java/org/apache/isis/viewer/scimpi/dispatcher/action/ActionAction.java
@@ -30,6 +30,7 @@ import org.apache.isis.core.commons.debug.DebugBuilder;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
 import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException;
+import org.apache.isis.core.metamodel.adapter.version.Version;
 import org.apache.isis.core.metamodel.consent.Consent;
 import org.apache.isis.core.metamodel.consent.Veto;
 import org.apache.isis.core.metamodel.facets.object.parseable.ParseableFacet;
@@ -91,7 +92,8 @@ public class ActionAction implements Action {
                 session = new AnonymousSession();
             }
 
-            object.checkLock(context.getVersion(version));
+            final Version originalVersion = context.getVersion(version);
+            object.checkLock(originalVersion);
             if (entryState.isValid()) {
                 final boolean hasResult = invokeMethod(context, resultName, object, action, entryState);
                 String view = context.getParameter(hasResult ? "_" + VIEW : "_" + VOID);

http://git-wip-us.apache.org/repos/asf/isis/blob/4bdc6adb/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/mgr/AdapterManager.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/mgr/AdapterManager.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/mgr/AdapterManager.java
index d3978aa..0ff8a82 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/mgr/AdapterManager.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/mgr/AdapterManager.java
@@ -79,6 +79,9 @@ public interface AdapterManager extends Injectable {
                     : ConcurrencyChecking.CHECK;
         }
 
+        public boolean isChecking() {
+            return this == CHECK;
+        }
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/isis/blob/4bdc6adb/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/oid/RootOid.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/oid/RootOid.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/oid/RootOid.java
index a112f3e..a701b22 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/oid/RootOid.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/oid/RootOid.java
@@ -50,8 +50,6 @@ public interface RootOid extends TypedOid {
     
     void setVersion(Version version);
 
-    void checkLock(String currentUser, RootOid oid);
-
 
     /**
      * Returns a new RootOid for the same {@link #getObjectSpecId()}, but persistent and with the specified {@link #getIdentifier() identifier}.

http://git-wip-us.apache.org/repos/asf/isis/blob/4bdc6adb/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/oid/RootOidDefault.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/oid/RootOidDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/oid/RootOidDefault.java
index 2f1b4b7..9aa4533 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/oid/RootOidDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/oid/RootOidDefault.java
@@ -249,20 +249,6 @@ public final class RootOidDefault implements Serializable, RootOid {
                 : Comparison.EQUIVALENT_BUT_CHANGED;
     }
 
-    @Override
-    public void checkLock(String currentUser, RootOid otherOid) {
-        Version otherVersion = otherOid.getVersion();
-        if(version == null || otherVersion == null) {
-            return;
-        }
-        if (version.different(otherVersion)) {
-            LOG.info("concurrency conflict on " + this + " (" + otherVersion + ")");
-            // reset this Oid to latest
-            throw new ConcurrencyException(currentUser, this, version, otherVersion);
-        }
-    }
-
-    
     // ////////////////////////////////////////////
     // bookmark
     // ////////////////////////////////////////////

http://git-wip-us.apache.org/repos/asf/isis/blob/4bdc6adb/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/version/ConcurrencyException.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/version/ConcurrencyException.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/version/ConcurrencyException.java
index 2197550..af3fdfc 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/version/ConcurrencyException.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/version/ConcurrencyException.java
@@ -20,6 +20,7 @@
 package org.apache.isis.core.metamodel.adapter.version;
 
 import org.apache.isis.core.commons.exceptions.IsisException;
+import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager.ConcurrencyChecking;
 import org.apache.isis.core.metamodel.adapter.oid.Oid;
 import org.apache.isis.core.metamodel.adapter.oid.OidMarshaller;
 
@@ -27,6 +28,26 @@ public class ConcurrencyException extends IsisException {
     
     private static final long serialVersionUID = 1L;
 
+    /**
+     * Provides a mechanism to temporarily disable concurrency checking.
+     * 
+     * <p>
+     * This thread-local is not used by this class, but is defined here as a central point for other methods
+     * to read/write.  The idea is that if concurrency checking is to be temporarily disabled, then the caller can
+     * set this threadlocal to {@link ConcurrencyChecking#NO_CHECK no-check}, and then the code that would normally
+     * detect the concurrency problem and throw this exception would instead consult this thread local and suppress
+     * the exception being raised.
+
+     * <p>
+     * In this design, it is the responsibility of the caller that is disabling concurrency exception handling to reinstate it
+     * afterwards.  This should normally be done using a try...finally.
+     */
+    public static ThreadLocal<ConcurrencyChecking> concurrencyChecking = new ThreadLocal<ConcurrencyChecking>(){
+        protected ConcurrencyChecking initialValue() {
+            return ConcurrencyChecking.CHECK;
+        };
+    };
+    
     private static String buildMessage(String currentUser, Oid oid, Version staleVersion, Version datastoreVersion) {
         
         final StringBuilder buf = new StringBuilder();

http://git-wip-us.apache.org/repos/asf/isis/blob/4bdc6adb/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/adapter/PojoAdapter.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/adapter/PojoAdapter.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/adapter/PojoAdapter.java
index b3da0cb..5128cb8 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/adapter/PojoAdapter.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/adapter/PojoAdapter.java
@@ -331,14 +331,25 @@ public class PojoAdapter extends InstanceAbstract implements ObjectAdapter {
             getAggregateRoot().checkLock(otherVersion);
             return;
         }
-        final Version version = getOid().getVersion();
-        if (otherVersion != null && version != null && version.different(otherVersion)) {
-            LOG.info("concurrency conflict on " + this + " (" + otherVersion + ")");
-            throw new ConcurrencyException(getAuthenticationSession().getUserName(), getOid(), version, otherVersion);
+        
+        Oid thisOid = getOid();
+        final Version thisVersion = thisOid.getVersion();
+        
+        // check for exception, but don't throw if suppressed through thread-local
+        if(thisVersion != null && 
+           otherVersion != null && 
+           thisVersion.different(otherVersion)) {
+            
+            if(ConcurrencyException.concurrencyChecking.get().isChecking()) {
+                LOG.info("concurrency conflict detected on " + thisOid + " (" + otherVersion + ")");
+                final String currentUser = getAuthenticationSession().getUserName();
+                throw new ConcurrencyException(currentUser, thisOid, thisVersion, otherVersion);
+            } else {
+                LOG.warn("concurrency conflict detected but suppressed, on " + thisOid + " (" + otherVersion + ")");
+            }
         }
     }
 
-
     @Override
     public void setVersion(final Version version) {
         if(isParented()) {

http://git-wip-us.apache.org/repos/asf/isis/blob/4bdc6adb/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/adaptermanager/AdapterManagerDefault.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/adaptermanager/AdapterManagerDefault.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/adaptermanager/AdapterManagerDefault.java
index 644ca16..0e561b1 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/adaptermanager/AdapterManagerDefault.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/adaptermanager/AdapterManagerDefault.java
@@ -45,6 +45,7 @@ import org.apache.isis.core.metamodel.adapter.oid.Oid;
 import org.apache.isis.core.metamodel.adapter.oid.OidMarshaller;
 import org.apache.isis.core.metamodel.adapter.oid.RootOid;
 import org.apache.isis.core.metamodel.adapter.oid.TypedOid;
+import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException;
 import org.apache.isis.core.metamodel.adapter.version.Version;
 import org.apache.isis.core.metamodel.facets.accessor.PropertyOrCollectionAccessorFacet;
 import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacet;
@@ -61,6 +62,7 @@ import org.apache.isis.core.metamodel.spec.feature.Contributed;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.core.runtime.persistence.ObjectNotFoundException;
+import org.apache.isis.core.runtime.persistence.adapter.PojoAdapter;
 import org.apache.isis.core.runtime.system.context.IsisContext;
 import org.apache.isis.core.runtime.system.persistence.AdapterManagerSpi;
 import org.apache.isis.core.runtime.system.persistence.OidGenerator;
@@ -312,20 +314,35 @@ public class AdapterManagerDefault implements AdapterManagerSpi {
         if(adapterOid instanceof RootOid) {
             final RootOid recreatedOid = (RootOid) adapterOid;
             final RootOid originalOid = (RootOid) typedOid;
+            
             try {
-                if(concurrencyChecking == ConcurrencyChecking.CHECK) {
-                    recreatedOid.checkLock(getAuthenticationSession().getUserName(), originalOid);
+                if(concurrencyChecking.isChecking()) {
+                    
+                    // check for exception, but don't throw if suppressed through thread-local
+                    final Version otherVersion = originalOid.getVersion();
+                    final Version thisVersion = recreatedOid.getVersion();
+                    if(thisVersion != null && 
+                       otherVersion != null && 
+                       thisVersion.different(otherVersion)) {
+                        
+                        if(ConcurrencyException.concurrencyChecking.get().isChecking()) {
+                            LOG.info("concurrency conflict detected on " + recreatedOid + " (" + otherVersion + ")");
+                            final String currentUser = getAuthenticationSession().getUserName();
+                            throw new ConcurrencyException(currentUser, recreatedOid, thisVersion, otherVersion);
+                        } else {
+                            LOG.warn("concurrency conflict detected but suppressed, on " + recreatedOid + " (" + otherVersion + ")");
+                        }
+                    }
                 }
             } finally {
-                originalOid.setVersion(recreatedOid.getVersion());
+                final Version recreatedVersion = recreatedOid.getVersion();
+                originalOid.setVersion(recreatedVersion);
             }
         }
 
         return adapter;
     }
 
-    
-
     @Override
     public void remapRecreatedPojo(ObjectAdapter adapter, final Object pojo) {
         removeAdapter(adapter);