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 494d4348ab4a075c5ffd0246450eb417ce11544a..487b46f41f119af738c89a63adb8a1d8f82e0be1 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 0aedfd5c198b5fbb3c8feeb71a9d1a64932542cb..658299bde0c287a4f69e933a27f3bcb515d47e6b 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 74183385cd6ec1732bd8e93d047f5fd826dc95f7..6a22f40daeee4e11aec43261c35e594a37bfd1c3 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 eaa90e788e2ad1ca477dbf2ae40decaefe9199d0..4b20c25c52f4b92113abdf8dc678e91928137abd 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 38b435b8a75eea6a7dec47c709a1f7107abda8b5..6eeb01f72d27ad3d87d7afbce4ccf610ba653663 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 991979b056b8ab9b19ce3266980ce2e4b09ea645..43821c22b4de6877a9fc768e27146b0c82dd3ce0 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 f3e36314bb62ddc10613521a5721f3f893a0799c..82cbcbfafdb9ab5d0356d07322c2738ccafec49f 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 ba183c3281470cec6efac069c6a1a7c00906b690..4993b274dfae9d239e28b593fff998d1df591939 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) + { + } }