From 5df1156e1220d66c173bc00ba862e483beb3fb42 Mon Sep 17 00:00:00 2001
From: felmer <felmer>
Date: Tue, 5 Mar 2013 08:51:37 +0000
Subject: [PATCH] SP-280, BIS-192: Locking for table cell editing established.

SVN: 28517
---
 .../client/application/ui/TypedTableGrid.java |  9 +++---
 .../ui/grid/AbstractEntityGrid.java           |  6 ++--
 .../web/client/dto/EntityPropertyUpdates.java | 15 +++++++++-
 .../web/server/AbstractClientService.java     |  4 +--
 .../web/server/CommonClientService.java       |  3 ++
 .../resultset/CachedResultSetManager.java     | 30 +++++++++++++++++++
 .../server/resultset/IResultSetManager.java   | 11 ++++++-
 .../server/PhosphoNetXClientServiceTest.java  |  5 ++++
 8 files changed, 72 insertions(+), 11 deletions(-)

diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/TypedTableGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/TypedTableGrid.java
index 494d4348ab4..487b46f41f1 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/TypedTableGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/TypedTableGrid.java
@@ -2334,9 +2334,8 @@ public abstract class TypedTableGrid<T extends Serializable> extends LayoutConta
      * implementation does nothing.
      */
     protected void applyModifications(BaseEntityModel<TableModelRowWithObject<T>> model,
-            List<IModification> modifications, AsyncCallback<IUpdateResult> callBack)
+            String resultSetKey, List<IModification> modifications, AsyncCallback<IUpdateResult> callBack)
     {
-
     }
 
     private class TableModificationsManager
@@ -2360,12 +2359,12 @@ public abstract class TypedTableGrid<T extends Serializable> extends LayoutConta
                 {
                     @Override
                     public void applyModifications(
-                            BaseEntityModel<TableModelRowWithObject<T>> model,
-                            List<IModification> modifications)
+                            BaseEntityModel<TableModelRowWithObject<T>> model, List<IModification> modifications)
                     {
                         AsyncCallback<IUpdateResult> callBack =
                                 createApplyModificationsCallback(model, modifications);
-                        TypedTableGrid.this.applyModifications(model, modifications, callBack);
+                        TypedTableGrid.this.applyModifications(model, resultSetKeyOrNull,
+                                modifications, callBack);
                     }
                 });
         }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractEntityGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractEntityGrid.java
index 0aedfd5c198..658299bde0c 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractEntityGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractEntityGrid.java
@@ -109,11 +109,13 @@ public abstract class AbstractEntityGrid<E extends IEntityInformationHolderWithP
 
     @Override
     protected void applyModifications(BaseEntityModel<TableModelRowWithObject<E>> model,
-            List<IModification> modifications, AsyncCallback<IUpdateResult> callBack)
+            String resultSetKeyOrNull, List<IModification> modifications,
+            AsyncCallback<IUpdateResult> callBack)
     {
         final EntityKind entityKind = getEntityKindOrNull();
         final TechId entityId = new TechId(model.getBaseObject().getId());
-        final EntityPropertyUpdates updates = new EntityPropertyUpdates(entityKind, entityId);
+        final EntityPropertyUpdates updates =
+                new EntityPropertyUpdates(resultSetKeyOrNull, entityKind, entityId);
         for (IModification modification : modifications)
         {
             String propertyCode =
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/EntityPropertyUpdates.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/EntityPropertyUpdates.java
index 74183385cd6..6a22f40daee 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/EntityPropertyUpdates.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/EntityPropertyUpdates.java
@@ -40,16 +40,29 @@ public class EntityPropertyUpdates implements Serializable
 
     private List<PropertyUpdates> modifiedProperties = new ArrayList<PropertyUpdates>();
 
+    private String resultSetKey;
+
     public EntityPropertyUpdates()
     {
     }
 
-    public EntityPropertyUpdates(EntityKind entityKind, TechId entityId)
+    public EntityPropertyUpdates(String resultSetKey, EntityKind entityKind, TechId entityId)
     {
+        this.resultSetKey = resultSetKey;
         this.entityKind = entityKind;
         this.entityId = entityId;
     }
 
+    public String getResultSetKey()
+    {
+        return resultSetKey;
+    }
+
+    public void setResultSetKey(String resultSetKey)
+    {
+        this.resultSetKey = resultSetKey;
+    }
+
     public EntityKind getEntityKind()
     {
         return entityKind;
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/AbstractClientService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/AbstractClientService.java
index eaa90e788e2..4b20c25c52f 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/AbstractClientService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/AbstractClientService.java
@@ -152,7 +152,7 @@ public abstract class AbstractClientService implements IClientService,
     }
 
     @SuppressWarnings("unchecked")
-    protected final <K> IResultSetManager<K> getResultSetManager()
+    protected final IResultSetManager<String> getResultSetManager()
     {
         HttpSession httpSession = getHttpSession();
         if (httpSession == null)
@@ -160,7 +160,7 @@ public abstract class AbstractClientService implements IClientService,
             throw new ch.systemsx.cisd.openbis.generic.client.web.client.exception.UserFailureException(
                     "Your session has expired, please log in again.");
         }
-        return (IResultSetManager<K>) httpSession
+        return (IResultSetManager<String>) httpSession
                 .getAttribute(SessionConstants.OPENBIS_RESULT_SET_MANAGER);
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientService.java
index 38b435b8a75..6eeb01f72d2 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientService.java
@@ -90,6 +90,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.FileFormatTy
 import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.GridCustomFilterProvider;
 import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.IOriginalDataProvider;
 import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.IResultSet;
+import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.IResultSetManager;
 import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.MatchingEntitiesProvider;
 import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.MetaprojectProvider;
 import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.PersonsProvider;
@@ -2571,6 +2572,8 @@ public final class CommonClientService extends AbstractClientService implements
     {
         final String sessionToken = getSessionToken();
 
+        IResultSetManager<String> resultSetManager = getResultSetManager();
+        resultSetManager.lockResultSet(updates.getResultSetKey());
         final EntityKind entityKind = updates.getEntityKind();
         final TechId entityId = updates.getEntityId();
         final List<PropertyUpdates> modifiedProperties = updates.getModifiedProperties();
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/CachedResultSetManager.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/CachedResultSetManager.java
index 991979b056b..43821c22b4d 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/CachedResultSetManager.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/CachedResultSetManager.java
@@ -469,6 +469,8 @@ public final class CachedResultSetManager<K> implements IResultSetManager<K>, Se
     private final Map<K, Future<?>> cache = Collections
             .synchronizedMap(new HashMap<K, Future<?>>());
 
+    private final Set<K> lockedResultSets = Collections.synchronizedSet(new HashSet<K>());
+
     private final ThreadPoolExecutor executor = new NamingThreadPoolExecutor(
             "Background Table Loader").corePoolSize(10).daemonize();
 
@@ -974,6 +976,7 @@ public final class CachedResultSetManager<K> implements IResultSetManager<K>, Se
 
     private <T> void addToCache(K dataKey, Future<TableData<T>> tableData)
     {
+        unlockResultSet(dataKey);
         cache.put(dataKey, tableData);
         debug(cache.size() + " keys in cache: " + cache.keySet());
     }
@@ -988,6 +991,7 @@ public final class CachedResultSetManager<K> implements IResultSetManager<K>, Se
 
     private <T> TableData<T> tryGetCachedTableData(K dataKey)
     {
+        waitUntilUnlocked(dataKey);
         Future<TableData<T>> tableData = cast(cache.get(dataKey));
         if (tableData == null)
         {
@@ -1024,6 +1028,7 @@ public final class CachedResultSetManager<K> implements IResultSetManager<K>, Se
     @Override
     public final void removeResultSet(final K resultSetKey)
     {
+        unlockResultSet(resultSetKey);
         assert resultSetKey != null : "Unspecified data key holder.";
         if (cache.remove(resultSetKey) != null)
         {
@@ -1035,6 +1040,31 @@ public final class CachedResultSetManager<K> implements IResultSetManager<K>, Se
         }
     }
 
+    @Override
+    public void lockResultSet(K resultSetKey)
+    {
+        lockedResultSets.add(resultSetKey);
+    }
+
+    private void unlockResultSet(K resultSetKey)
+    {
+        lockedResultSets.remove(resultSetKey);
+    }
+
+    private void waitUntilUnlocked(K resultSetKey)
+    {
+        while (lockedResultSets.contains(resultSetKey))
+        {
+            try
+            {
+                Thread.sleep(100);
+            } catch (Exception ex)
+            {
+                operationLog.error("Interrupted while waiting on unlocking " + resultSetKey, ex);
+            }
+        }
+    }
+
     private void debug(String msg)
     {
         if (operationLog.isDebugEnabled())
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/IResultSetManager.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/IResultSetManager.java
index f3e36314bb6..82cbcbfafdb 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/IResultSetManager.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/IResultSetManager.java
@@ -43,4 +43,13 @@ public interface IResultSetManager<K>
      * Remove the data mapped to given <var>resultSetKey</var>.
      */
     public void removeResultSet(final K resultSetKey) throws UserFailureException;
-}
+
+    /**
+     * Locks specified result set. Lock will be released after invocation of
+     * {@link #removeResultSet(Object)} and after reloading of the result set.
+     * {@link #getResultSet(String, IResultSetConfig, IOriginalDataProvider)} waits until lock is
+     * released.
+     */
+    public void lockResultSet(K resultSetKey);
+
+}
\ No newline at end of file
diff --git a/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/proteomics/client/web/server/PhosphoNetXClientServiceTest.java b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/proteomics/client/web/server/PhosphoNetXClientServiceTest.java
index ba183c32814..4993b274dfa 100644
--- a/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/proteomics/client/web/server/PhosphoNetXClientServiceTest.java
+++ b/rtd_phosphonetx/sourceTest/java/ch/systemsx/cisd/openbis/plugin/proteomics/client/web/server/PhosphoNetXClientServiceTest.java
@@ -97,6 +97,11 @@ public class PhosphoNetXClientServiceTest extends AbstractFileSystemTestCase
         public void removeResultSet(K resultSetKey) throws UserFailureException
         {
         }
+
+        @Override
+        public void lockResultSet(K resultSetKey)
+        {
+        }
         
     }
     
-- 
GitLab