From 9d1922b6d0277dc97e77d17ef68a3130a51b1c45 Mon Sep 17 00:00:00 2001
From: tpylak <tpylak>
Date: Tue, 10 Nov 2009 09:07:34 +0000
Subject: [PATCH] LMS-1253 refactoring of the caching mechanism of the custom
 grid

SVN: 13286
---
 .../application/MatchingEntitiesPanel.java    |   2 +-
 .../ui/data/DataSetReporterGrid.java          |   9 +-
 .../ui/grid/AbstractBrowserGrid.java          | 135 +++++++++++-------
 .../ui/grid/AbstractEntityBrowserGrid.java    |   9 +-
 .../ui/grid/AbstractSimpleBrowserGrid.java    |   2 +-
 .../client/dto/DefaultResultSetConfig.java    |  19 +--
 .../web/client/dto/IResultSetConfig.java      |   5 +-
 .../web/client/dto/IResultSetKeyHolder.java   |  30 ----
 .../web/client/dto/ResultSetFetchConfig.java  | 103 +++++++++++++
 .../web/client/dto/TableExportCriteria.java   |   6 +-
 .../web/server/AbstractClientService.java     |   3 +-
 .../web/server/CommonClientService.java       |   4 +-
 .../resultset/CachedResultSetManager.java     |  92 +++++++++---
 .../web/server/resultset/IResultSet.java      |   8 +-
 .../resultset/CachedResultSetManagerTest.java |  17 +--
 .../ProteinByExperimentBrowserGrid.java       |   2 +-
 16 files changed, 306 insertions(+), 140 deletions(-)
 delete mode 100644 openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/IResultSetKeyHolder.java
 create mode 100644 openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/ResultSetFetchConfig.java

diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/MatchingEntitiesPanel.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/MatchingEntitiesPanel.java
index 5182b853824..2cbfcb33c35 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/MatchingEntitiesPanel.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/MatchingEntitiesPanel.java
@@ -136,7 +136,7 @@ final class MatchingEntitiesPanel extends AbstractBrowserGrid<MatchingEntity, Ma
     @Override
     protected final void refresh()
     {
-        super.refresh(null, null, false);
+        super.refresh(false);
     }
 
     /** used to make a first data refresh, but can be also called many times */
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/data/DataSetReporterGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/data/DataSetReporterGrid.java
index d0628574bc6..29e29ae16b6 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/data/DataSetReporterGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/data/DataSetReporterGrid.java
@@ -35,6 +35,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.grid.Co
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.grid.IDisposableComponent;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.DefaultResultSetConfig;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSet;
+import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSetFetchConfig;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.TableExportCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.TableModelReference;
 import ch.systemsx.cisd.openbis.generic.shared.basic.GridRowModel;
@@ -146,8 +147,10 @@ public class DataSetReporterGrid extends
     protected void listEntities(DefaultResultSetConfig<String, TableModelRow> resultSetConfig,
             AbstractAsyncCallback<ResultSet<TableModelRow>> callback)
     {
-        // in all cases the data should be taken from the cache, and we know the key already
-        resultSetConfig.setResultSetKey(resultSetKey);
+        // In all cases the data should be taken from the cache, and we know the key already.
+        // The custom columns should be recomputed.
+        resultSetConfig.setCacheConfig(ResultSetFetchConfig
+                .createFetchFromCacheAndRecompute(resultSetKey));
         viewContext.getService().listDatasetReport(resultSetConfig, callback);
     }
 
@@ -187,7 +190,7 @@ public class DataSetReporterGrid extends
     @Override
     protected void refresh()
     {
-        refresh(null, false);
+        refresh(false);
     }
 
     @Override
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractBrowserGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractBrowserGrid.java
index beb6ceedc21..a333433393e 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractBrowserGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractBrowserGrid.java
@@ -94,6 +94,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridFilters;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridRowModels;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.RelatedDataSetCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSet;
+import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSetFetchConfig;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.TableExportCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.GridRowModel;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IColumnDefinition;
@@ -195,8 +196,12 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
 
     private CustomColumnsMetadataProvider customColumnsMetadataProvider;
 
-    // result set key of the last refreshed data
-    private String resultSetKey;
+	// result set key of the last refreshed data
+    private String resultSetKeyOrNull;
+
+    // not null only if there is a pending request. No new request can be issued until this one is
+    // finished.
+    private ResultSetFetchConfig<String> pendingFetchConfigOrNull;
 
     private IDataRefreshCallback refreshCallback;
 
@@ -342,13 +347,15 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
             {
                 public void execute()
                 {
-                    if (isHardRefreshNeeded() == false)
+                    if (resultSetKeyOrNull != null)
                     {
                         // TODO 2009-09-16, Piotr Buczek: reload of data is not needed in some cases
                         // - when filters with no text are removed,
                         // - when new filters are created,
                         // but when filter with text is removed it is needed.
-                        reloadData();
+                        ResultSetFetchConfig<String> fetchConfig =
+                                ResultSetFetchConfig.createFetchFromCache(resultSetKeyOrNull);
+                        reloadData(fetchConfig);
                     }
                 }
             };
@@ -456,30 +463,32 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
 
     private PagingLoader<PagingLoadConfig> createPagingLoader()
     {
-        final RpcProxy<PagingLoadConfig, PagingLoadResult<M>> proxy = createDataLoaderProxy();
+        final RpcProxy<PagingLoadConfig, PagingLoadResult<M>> proxy =
+                new RpcProxy<PagingLoadConfig, PagingLoadResult<M>>()
+                    {
+                        @Override
+                        public final void load(final PagingLoadConfig loadConfig,
+                                final AsyncCallback<PagingLoadResult<M>> callback)
+                        {
+                            loadData(loadConfig, callback);
+                        }
+                    };
         final BasePagingLoader<PagingLoadConfig, PagingLoadResult<M>> newPagingLoader =
                 new BasePagingLoader<PagingLoadConfig, PagingLoadResult<M>>(proxy);
         newPagingLoader.setRemoteSort(true);
         return newPagingLoader;
     }
 
-    private RpcProxy<PagingLoadConfig, PagingLoadResult<M>> createDataLoaderProxy()
+    private void loadData(final PagingLoadConfig loadConfig,
+            final AsyncCallback<PagingLoadResult<M>> callback)
     {
-        return new RpcProxy<PagingLoadConfig, PagingLoadResult<M>>()
-            {
-                @Override
-                public final void load(final PagingLoadConfig loadConfig,
-                        final AsyncCallback<PagingLoadResult<M>> callback)
-                {
-                    DefaultResultSetConfig<String, T> resultSetConfig =
-                            createPagingConfig(loadConfig, columnDefinitions, filterToolbar
-                                    .getFilters(), resultSetKey, getGridDisplayTypeID());
-                    debug("create a refresh callback");
-                    ListEntitiesCallback listCallback =
-                            new ListEntitiesCallback(viewContext, callback, resultSetConfig);
-                    listEntities(resultSetConfig, listCallback);
-                }
-            };
+        DefaultResultSetConfig<String, T> resultSetConfig =
+                createPagingConfig(loadConfig, columnDefinitions, filterToolbar.getFilters(),
+                        pendingFetchConfigOrNull, getGridDisplayTypeID());
+        debug("create a refresh callback " + pendingFetchConfigOrNull);
+        ListEntitiesCallback listCallback =
+                new ListEntitiesCallback(viewContext, callback, resultSetConfig);
+        listEntities(resultSetConfig, listCallback);
     }
 
     private void debug(String msg)
@@ -487,7 +496,8 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
         if (DEBUG)
         {
             String text =
-                    "[grid: " + getGridDisplayTypeID() + ", cache: " + resultSetKey + "] " + msg;
+                    "[grid: " + getGridDisplayTypeID() + ", cache: " + resultSetKeyOrNull + "] "
+                            + msg;
             System.out.println(text);
         }
     }
@@ -513,7 +523,7 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
 
     private static <T> DefaultResultSetConfig<String, T> createPagingConfig(
             PagingLoadConfig loadConfig, Set<IColumnDefinition<T>> availableColumns,
-            GridFilters<T> filters, String resultSetKey, String gridDisplayId)
+            GridFilters<T> filters, ResultSetFetchConfig<String> cacheConfig, String gridDisplayId)
     {
         int limit = loadConfig.getLimit();
         int offset = loadConfig.getOffset();
@@ -526,7 +536,7 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
         resultSetConfig.setAvailableColumns(availableColumns);
         resultSetConfig.setSortInfo(translatedSortInfo);
         resultSetConfig.setFilters(filters);
-        resultSetConfig.setResultSetKey(resultSetKey);
+        resultSetConfig.setCacheConfig(cacheConfig);
         resultSetConfig.setGridDisplayId(gridDisplayId);
         return resultSetConfig;
     }
@@ -634,6 +644,7 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
         // notify that the refresh is done
         private void onComplete(boolean wasSuccessful)
         {
+            pendingFetchConfigOrNull = null;
             refreshCallback.postRefresh(wasSuccessful);
         }
 
@@ -884,9 +895,9 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
      * Note that, doing so, the result set associated on the server side will be removed.
      * </p>
      */
-    protected final void refresh(String headerOrNull, boolean refreshColumnsDefinition)
+    protected final void refresh(boolean refreshColumnsDefinition)
     {
-        refresh(null, headerOrNull, refreshColumnsDefinition);
+        refresh(null, null, refreshColumnsDefinition);
     }
 
     /**
@@ -898,17 +909,27 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
     {
         pagingToolbar.updateDefaultRefreshButton(false);
         debug("clean cache for refresh");
-        disposeCache();
         this.refreshCallback = createRefreshCallback(externalRefreshCallbackOrNull);
         setHeader(headerOrNull);
         if (columnDefinitions == null || refreshColumnsDefinition)
         {
             recreateColumnModelAndRefreshColumnsWithFilters();
         }
-        reloadData();
+        reloadData(createDisposeAndRefreshFetchMode());
         refreshColumnHeaderWidths();
     }
 
+    private ResultSetFetchConfig<String> createDisposeAndRefreshFetchMode()
+    {
+        if (resultSetKeyOrNull != null)
+        {
+            return ResultSetFetchConfig.createClearComputeAndCache(resultSetKeyOrNull);
+        } else
+        {
+            return ResultSetFetchConfig.createComputeAndCache();
+        }
+    }
+
     private void refreshColumnHeaderWidths()
     {
         // Workaround for the problem of incorrect column header widths if column header is very
@@ -1002,8 +1023,15 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
     }
 
     // Refreshes the data, does not clear the cache. Does not change the column model.
-    private void reloadData()
+    private void reloadData(ResultSetFetchConfig<String> resultSetFetchConfig)
     {
+        if (pendingFetchConfigOrNull != null)
+        {
+            System.err.println("Cannot reload the data with the mode '" + resultSetFetchConfig
+                    + "', there is an unfinished request already: " + pendingFetchConfigOrNull);
+            return;
+        }
+        pendingFetchConfigOrNull = resultSetFetchConfig;
         pagingLoader.load(0, PAGE_SIZE);
     }
 
@@ -1129,22 +1157,16 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
 
     private void saveCacheKey(final String newResultSetKey)
     {
-        String oldResultSetKey = resultSetKey;
-        resultSetKey = newResultSetKey;
+        resultSetKeyOrNull = newResultSetKey;
         debug("saving new cache key");
-        if (oldResultSetKey != null && oldResultSetKey.equals(newResultSetKey) == false)
-        {
-            debug("cleaning old cache " + oldResultSetKey);
-            removeResultSet(oldResultSetKey);
-        }
     }
 
     private void disposeCache()
     {
-        if (resultSetKey != null)
+        if (resultSetKeyOrNull != null)
         {
-            removeResultSet(resultSetKey);
-            resultSetKey = null;
+            removeResultSet(resultSetKeyOrNull);
+            resultSetKeyOrNull = null;
         }
     }
 
@@ -1154,11 +1176,6 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
                 new VoidAsyncCallback<Void>(viewContext));
     }
 
-    private boolean isHardRefreshNeeded()
-    {
-        return resultSetKey == null;
-    }
-
     /** Export always deals with data from the previous refresh operation */
     private void export()
     {
@@ -1203,8 +1220,9 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
 
                             if (customColumnsChanged || filtersChanged)
                             {
-                                debug("refreshing the custom columns or filters");
-                                refresh();
+                                debug("refreshing the custom columns or filters in "
+                                        + pendingFetchConfigOrNull + " mode");
+                                reloadData(createRefreshSettingsFetchConfig());
                             } else
                             {
                                 if (filtersChanged)
@@ -1220,6 +1238,24 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
                                 filterToolbar.refresh();
                             }
                         }
+
+                        private ResultSetFetchConfig<String> createRefreshSettingsFetchConfig()
+                        {
+                            if (pendingFetchConfigOrNull == null)
+                            {
+                                if (resultSetKeyOrNull == null)
+                                {
+                                    return ResultSetFetchConfig.createComputeAndCache();
+                                } else
+                                {
+                                    return ResultSetFetchConfig
+                                            .createFetchFromCacheAndRecompute(resultSetKeyOrNull);
+                                }
+                            } else
+                            {
+                                return pendingFetchConfigOrNull;
+                            }
+                        }
                     };
         ColumnSettingsDialog.show(viewContext, provider, getGridDisplayTypeID());
     }
@@ -1241,13 +1277,14 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod
     protected final TableExportCriteria<T> createTableExportCriteria()
     {
         assert columnDefinitions != null : "refresh before exporting!";
-        assert resultSetKey != null : "refresh before exporting, resultSetKey is null!";
+        assert resultSetKeyOrNull != null : "refresh before exporting, resultSetKey is null!";
 
         final List<IColumnDefinition<T>> columnDefs = getVisibleColumns(columnDefinitions);
         SortInfo<T> sortInfo = getGridSortInfo();
         final TableExportCriteria<T> exportCriteria =
-                new TableExportCriteria<T>(resultSetKey, sortInfo, filterToolbar.getFilters(),
-                        columnDefs, columnDefinitions, getGridDisplayTypeID());
+                new TableExportCriteria<T>(resultSetKeyOrNull, sortInfo,
+                        filterToolbar.getFilters(), columnDefs, columnDefinitions,
+                        getGridDisplayTypeID());
         return exportCriteria;
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractEntityBrowserGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractEntityBrowserGrid.java
index ed188696bd8..d19f4eac158 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractEntityBrowserGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractEntityBrowserGrid.java
@@ -76,8 +76,6 @@ public abstract class AbstractEntityBrowserGrid<T extends IEntityPropertiesHolde
 
     abstract protected ICriteriaProvider<K> getCriteriaProvider();
 
-    private IDataRefreshCallback externalRefreshCallbackOrNull;
-
     // criteria used in the previous refresh operation or null if it has not occurred yet
     protected K criteria;
 
@@ -147,7 +145,7 @@ public abstract class AbstractEntityBrowserGrid<T extends IEntityPropertiesHolde
             return;
         }
         String newHeader = createHeader();
-        super.refresh(externalRefreshCallbackOrNull, newHeader, refreshColumnsNeeded);
+        super.refresh(null, newHeader, refreshColumnsNeeded);
     }
 
     protected final void refreshColumnsSettingsIfNecessary()
@@ -175,11 +173,6 @@ public abstract class AbstractEntityBrowserGrid<T extends IEntityPropertiesHolde
         return refreshColumnsDefinition;
     }
 
-    protected final void setExternalRefreshCallback(IDataRefreshCallback externalRefreshCallback)
-    {
-        this.externalRefreshCallbackOrNull = externalRefreshCallback;
-    }
-
     @Override
     protected boolean isRefreshEnabled()
     {
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractSimpleBrowserGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractSimpleBrowserGrid.java
index a9d15418ce5..1377db478d1 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractSimpleBrowserGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractSimpleBrowserGrid.java
@@ -66,7 +66,7 @@ abstract public class AbstractSimpleBrowserGrid<T/* Entity */> extends
     @Override
     protected void refresh()
     {
-        super.refresh(null, false);
+        super.refresh(false);
     }
 
     @Override
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/DefaultResultSetConfig.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/DefaultResultSetConfig.java
index ec9f3f42871..c3b73e9ec9c 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/DefaultResultSetConfig.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/DefaultResultSetConfig.java
@@ -36,13 +36,7 @@ public class DefaultResultSetConfig<K, T> implements IResultSetConfig<K, T>, IsS
 
     private SortInfo<T> sortInfo = new SortInfo<T>();
 
-    /**
-     * The result set key.
-     * <p>
-     * Can be <code>null</code> if unknown.
-     * </p>
-     */
-    private K resultSetKeyOrNull;
+    private ResultSetFetchConfig<K> cacheConfig = ResultSetFetchConfig.createComputeAndCache();
 
     private Set<IColumnDefinition<T>> availableColumns;
 
@@ -71,9 +65,9 @@ public class DefaultResultSetConfig<K, T> implements IResultSetConfig<K, T>, IsS
         this.sortInfo = sortInfo;
     }
 
-    public final void setResultSetKey(final K resultSetKey)
+    public final void setCacheConfig(final ResultSetFetchConfig<K> cacheConfig)
     {
-        this.resultSetKeyOrNull = resultSetKey;
+        this.cacheConfig = cacheConfig;
     }
 
     public GridFilters<T> getFilters()
@@ -93,7 +87,7 @@ public class DefaultResultSetConfig<K, T> implements IResultSetConfig<K, T>, IsS
         setAvailableColumns(resultSetConfig.getAvailableColumns());
         setSortInfo(resultSetConfig.getSortInfo());
         setFilters(resultSetConfig.getFilters());
-        setResultSetKey(resultSetConfig.getResultSetKey());
+        setCacheConfig(resultSetConfig.getCacheConfig());
         setGridDisplayId(resultSetConfig.tryGetGridDisplayId());
     }
 
@@ -121,10 +115,9 @@ public class DefaultResultSetConfig<K, T> implements IResultSetConfig<K, T>, IsS
         return sortInfo;
     }
 
-    /** Can be <code>null</code> if unknown. */
-    public final K getResultSetKey()
+    public final ResultSetFetchConfig<K> getCacheConfig()
     {
-        return resultSetKeyOrNull;
+        return cacheConfig;
     }
 
     public void setFilters(GridFilters<T> filters)
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/IResultSetConfig.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/IResultSetConfig.java
index 0ea906383ca..c6d9c0919e1 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/IResultSetConfig.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/IResultSetConfig.java
@@ -30,10 +30,13 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SortInfo;
  * 
  * @author Christian Ribeaud
  */
-public interface IResultSetConfig<K, T> extends IResultSetKeyHolder<K>
+public interface IResultSetConfig<K, T>
 {
     public static final int NO_LIMIT = -1;
 
+    /** Instructions how to use the server side cache when computing the results. */
+    public ResultSetFetchConfig<K> getCacheConfig();
+
     /**
      * The offset for the first record to retrieve.
      */
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/IResultSetKeyHolder.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/IResultSetKeyHolder.java
deleted file mode 100644
index 4f17ecb47be..00000000000
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/IResultSetKeyHolder.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2008 ETH Zuerich, CISD
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ch.systemsx.cisd.openbis.generic.client.web.client.dto;
-
-/**
- * A result set key holder.
- * 
- * @author Christian Ribeaud
- */
-public interface IResultSetKeyHolder<K>
-{
-    /**
-     * Uniquely identifies a result set on the server side.
-     */
-    public K getResultSetKey();
-}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/ResultSetFetchConfig.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/ResultSetFetchConfig.java
new file mode 100644
index 00000000000..2c0198c6127
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/ResultSetFetchConfig.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2009 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.generic.client.web.client.dto;
+
+import com.google.gwt.user.client.rpc.IsSerializable;
+
+/**
+ * Describes the way in which cache should be used when computing the result set.
+ * 
+ * @author Tomasz Pylak
+ */
+public class ResultSetFetchConfig<K> implements IsSerializable
+{
+    public enum ResultSetFetchMode implements IsSerializable
+    {
+        COMPUTE_AND_CACHE, CLEAR_COMPUTE_AND_CACHE, FETCH_FROM_CACHE,
+        FETCH_FROM_CACHE_AND_RECOMPUTE
+    }
+
+    private ResultSetFetchConfig.ResultSetFetchMode mode;
+
+    private K resultSetKeyOrNull;
+
+    /**
+     * Instruction to compute a new result and to cache it. Should be used only if the result is
+     * computed for the first time and has not been cached yet.
+     */
+    public static <K> ResultSetFetchConfig<K> createComputeAndCache()
+    {
+        return new ResultSetFetchConfig<K>(ResultSetFetchMode.COMPUTE_AND_CACHE, null);
+    }
+
+    /**
+     * Instruction to clear the cache at the specified key, recompute the result and cache it again.
+     */
+    public static <K> ResultSetFetchConfig<K> createClearComputeAndCache(K resultSetKey)
+    {
+        return new ResultSetFetchConfig<K>(ResultSetFetchMode.CLEAR_COMPUTE_AND_CACHE, resultSetKey);
+    }
+
+    /** Instruction to fetch the result at the specified key in the cache. */
+    public static <K> ResultSetFetchConfig<K> createFetchFromCache(K resultSetKey)
+    {
+        return new ResultSetFetchConfig<K>(ResultSetFetchMode.FETCH_FROM_CACHE, resultSetKey);
+    }
+
+    /**
+     * Instruction to fetch the result at the specified key in the cache an then recompute the
+     * custom columns and distinct filter values.
+     */
+    public static <K> ResultSetFetchConfig<K> createFetchFromCacheAndRecompute(K resultSetKey)
+    {
+        return new ResultSetFetchConfig<K>(ResultSetFetchMode.FETCH_FROM_CACHE_AND_RECOMPUTE,
+                resultSetKey);
+    }
+
+    private ResultSetFetchConfig(ResultSetFetchConfig.ResultSetFetchMode mode, K resultSetKeyOrNull)
+    {
+        this.mode = mode;
+        this.resultSetKeyOrNull = resultSetKeyOrNull;
+    }
+
+    private ResultSetFetchConfig()
+    {
+    }
+
+    public ResultSetFetchConfig.ResultSetFetchMode getMode()
+    {
+        return mode;
+    }
+
+    /**
+     * If mode is COMPUTE_AND_CACHE, returns null.<br>
+     * If mode is FETCH_FROM_CACHE, returns a key which uniquely identifies a result set in the
+     * cache on the server side.<br>
+     * If mode is CLEAR_CACHE_AND_RECOMPUTE, returns a key to the item in the cache which should be
+     * removed.<br>
+     */
+    public K tryGetResultSetKey()
+    {
+        return resultSetKeyOrNull;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "(mode = " + mode + ", resultSetKey = " + resultSetKeyOrNull + ")";
+    }
+}
\ No newline at end of file
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/TableExportCriteria.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/TableExportCriteria.java
index 30b27797d43..94c2baea853 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/TableExportCriteria.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/TableExportCriteria.java
@@ -30,8 +30,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SortInfo;
  * 
  * @author Tomasz Pylak
  */
-public class TableExportCriteria<T/* exported entity */> implements IResultSetKeyHolder<String>,
-        IsSerializable
+public class TableExportCriteria<T/* exported entity */> implements IsSerializable
 {
     // a key at which data are stored in the server cache
     private String resultSetKey;
@@ -70,6 +69,9 @@ public class TableExportCriteria<T/* exported entity */> implements IResultSetKe
         this.gridDisplayId = gridDisplayId;
     }
 
+    /**
+     * Uniquely identifies a result set on the server side.
+     */
     public String getResultSetKey()
     {
         return resultSetKey;
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 e206e46515f..ac505b631c9 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
@@ -35,6 +35,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ApplicationInfo;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.DefaultResultSetConfig;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.IResultSetConfig;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSet;
+import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSetFetchConfig;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSetWithEntityTypes;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.SessionContext;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.TableExportCriteria;
@@ -129,7 +130,7 @@ public abstract class AbstractClientService implements IClientService
             IOriginalDataProvider<T> dataProvider, String resultSetKey)
     {
         DefaultResultSetConfig<String, T> criteria = DefaultResultSetConfig.createFetchAll();
-        criteria.setResultSetKey(resultSetKey);
+        criteria.setCacheConfig(ResultSetFetchConfig.createFetchFromCache(resultSetKey));
         final IResultSet<String, T> allData = getResultSet(criteria, dataProvider);
         Set<BasicEntityType> result = new HashSet<BasicEntityType>();
         for (T row : allData.getList().extractOriginalObjects())
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 eca8c3e1ad2..c34950b848d 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
@@ -56,6 +56,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListPersonsCriteri
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListSampleDisplayCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.RelatedDataSetCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSet;
+import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSetFetchConfig;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSetWithEntityTypes;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.SearchableEntity;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.TableExportCriteria;
@@ -214,7 +215,8 @@ public final class CommonClientService extends AbstractClientService implements
         final DefaultResultSetConfig<String, T> criteria = DefaultResultSetConfig.createFetchAll();
         criteria.setSortInfo(exportCriteria.getSortInfo());
         criteria.setFilters(exportCriteria.getFilters());
-        criteria.setResultSetKey(exportCriteria.getResultSetKey());
+        criteria.setCacheConfig(ResultSetFetchConfig.createFetchFromCache(exportCriteria
+                .getResultSetKey()));
         criteria.setAvailableColumns(exportCriteria.getAvailableColumns());
         criteria.setGridDisplayId(exportCriteria.getGridDisplayId());
         return criteria;
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 9c48e640ea1..f5e39cba1a8 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
@@ -36,11 +36,13 @@ import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ColumnDistinctValues;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.CustomFilterInfo;
-import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridCustomColumnInfo;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridColumnFilterInfo;
+import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridCustomColumnInfo;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridFilters;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridRowModels;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.IResultSetConfig;
+import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSetFetchConfig;
+import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSetFetchConfig.ResultSetFetchMode;
 import ch.systemsx.cisd.openbis.generic.client.web.server.calculator.GridExpressionUtils;
 import ch.systemsx.cisd.openbis.generic.shared.basic.GridRowModel;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IColumnDefinition;
@@ -317,34 +319,86 @@ public final class CachedResultSetManager<K> implements IResultSetManager<K>, Se
     {
         assert resultConfig != null : "Unspecified result configuration";
         assert dataProvider != null : "Unspecified data retriever";
-        GridRowModels<T> data;
-        K dataKey = resultConfig.getResultSetKey();
-        if (dataKey == null)
+        ResultSetFetchConfig<K> cacheConfig = resultConfig.getCacheConfig();
+        ResultSetFetchMode mode = cacheConfig.getMode();
+        debug("getResultSet(cache config = " + cacheConfig + ")");
+
+        if (mode == ResultSetFetchMode.CLEAR_COMPUTE_AND_CACHE)
         {
-            dataKey = resultSetKeyProvider.createKey();
-            debug("Unknown result set key: retrieving the data with a new key " + dataKey);
+            removeResultSet(cacheConfig.tryGetResultSetKey());
+        }
+        if (mode == ResultSetFetchMode.COMPUTE_AND_CACHE
+                || mode == ResultSetFetchMode.CLEAR_COMPUTE_AND_CACHE)
+        {
+            K dataKey = resultSetKeyProvider.createKey();
+            debug("retrieving the data with a new key " + dataKey);
             List<T> rows = dataProvider.getOriginalData();
-            data = calculateRowModels(sessionToken, rows, resultConfig);
-            results.put(dataKey, data);
+            return calculateResult(sessionToken, resultConfig, dataKey, rows);
         } else
         {
-            debug(String.format("Fetching the result from the specifed result set key '%s'.",
-                    dataKey));
-            data = cast(results.get(dataKey));
-            if (data == null)
+            K dataKey = cacheConfig.tryGetResultSetKey();
+            GridRowModels<T> data = fetchCachedData(dataKey);
+
+            if (mode == ResultSetFetchMode.FETCH_FROM_CACHE)
+            {
+                return filterLimitAndSort(resultConfig, data, dataKey);
+            } else if (mode == ResultSetFetchMode.FETCH_FROM_CACHE_AND_RECOMPUTE)
             {
-                debug(String
-                        .format("Invalid result set key '%s'. This should not happen.", dataKey));
+                List<T> rows = extractRows(data);
+                return calculateResult(sessionToken, resultConfig, dataKey, rows);
+            } else
+            {
+                throw new IllegalStateException("unexpected mode " + mode);
             }
         }
-        assert data != null : "Unspecified data";
-        data = filterData(data, resultConfig.getAvailableColumns(), resultConfig.getFilters());
-        final int size = data.size();
+    }
+
+    private <T> IResultSet<K, T> calculateResult(final String sessionToken,
+            final IResultSetConfig<K, T> resultConfig, K dataKey, List<T> rows)
+    {
+        GridRowModels<T> data =
+                calculateRowModelsAndSave(rows, sessionToken, resultConfig, dataKey);
+        return filterLimitAndSort(resultConfig, data, dataKey);
+    }
+
+    private <T> GridRowModels<T> fetchCachedData(K dataKey)
+    {
+        debug(String.format("Fetching the result from the specifed result set key '%s'.", dataKey));
+        GridRowModels<T> data = cast(results.get(dataKey));
+        assert data != null : String.format("Invalid result set key '%s'. This should not happen.",
+                dataKey);
+        return data;
+    }
+
+    private <T> GridRowModels<T> calculateRowModelsAndSave(List<T> rows, final String sessionToken,
+            final IResultSetConfig<K, T> resultConfig, K dataKey)
+    {
+        GridRowModels<T> data = calculateRowModels(sessionToken, rows, resultConfig);
+        results.put(dataKey, data);
+        return data;
+    }
+
+    private static <T> List<T> extractRows(ArrayList<GridRowModel<T>> rowModels)
+    {
+        List<T> result = new ArrayList<T>();
+        for (GridRowModel<T> rowModel : rowModels)
+        {
+            result.add(rowModel.getOriginalObject());
+        }
+        return result;
+    }
+
+    private <T> IResultSet<K, T> filterLimitAndSort(final IResultSetConfig<K, T> resultConfig,
+            GridRowModels<T> data, K dataKey)
+    {
+        GridRowModels<T> filteredData =
+                filterData(data, resultConfig.getAvailableColumns(), resultConfig.getFilters());
+        final int size = filteredData.size();
         final int offset = getOffset(size, resultConfig.getOffset());
         final int limit = getLimit(size, resultConfig.getLimit(), offset);
         final SortInfo<T> sortInfo = resultConfig.getSortInfo();
-        sortData(data, sortInfo);
-        final GridRowModels<T> list = subList(data, offset, limit);
+        sortData(filteredData, sortInfo);
+        final GridRowModels<T> list = subList(filteredData, offset, limit);
         return new DefaultResultSet<K, T>(dataKey, list, size);
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/IResultSet.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/IResultSet.java
index bd6668553c3..e6d8355718e 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/IResultSet.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/IResultSet.java
@@ -18,15 +18,19 @@ package ch.systemsx.cisd.openbis.generic.client.web.server.resultset;
 
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridRowModels;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.IResultSetConfig;
-import ch.systemsx.cisd.openbis.generic.client.web.client.dto.IResultSetKeyHolder;
 
 /**
  * A result set that is returned to the client.
  * 
  * @author Christian Ribeaud
  */
-public interface IResultSet<K, T> extends IResultSetKeyHolder<K>
+public interface IResultSet<K, T>
 {
+    /**
+     * Uniquely identifies a result set on the server side.
+     */
+    public K getResultSetKey();
+
     /**
      * Returns the list produced by a given {@link IResultSetConfig}.
      */
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/CachedResultSetManagerTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/CachedResultSetManagerTest.java
index 13fa0d97e47..1a2c8e0eab8 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/CachedResultSetManagerTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/CachedResultSetManagerTest.java
@@ -39,6 +39,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridColumnFilterIn
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridFilters;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridRowModels;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.IResultSetConfig;
+import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSetFetchConfig;
 import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.CacheManager.TokenBasedResultSetKeyGenerator;
 import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.CachedResultSetManager.ICustomColumnsProvider;
 import ch.systemsx.cisd.openbis.generic.client.web.server.util.TSVRendererTest;
@@ -192,8 +193,8 @@ public final class CachedResultSetManagerTest
         context.checking(new Expectations()
             {
                 {
-                    one(resultSetConfig).getResultSetKey();
-                    will(returnValue(null));
+                    one(resultSetConfig).getCacheConfig();
+                    will(returnValue(ResultSetFetchConfig.createComputeAndCache()));
 
                     allowing(resultSetConfig).getAvailableColumns();
                     will(returnValue(null));
@@ -221,8 +222,8 @@ public final class CachedResultSetManagerTest
         context.checking(new Expectations()
             {
                 {
-                    one(resultSetConfig).getResultSetKey();
-                    will(returnValue("1"));
+                    one(resultSetConfig).getCacheConfig();
+                    will(returnValue(ResultSetFetchConfig.createFetchFromCache("1")));
 
                     allowing(resultSetConfig).getAvailableColumns();
                     will(returnValue(null));
@@ -245,8 +246,8 @@ public final class CachedResultSetManagerTest
         context.checking(new Expectations()
             {
                 {
-                    one(resultSetConfig).getResultSetKey();
-                    will(returnValue(null));
+                    one(resultSetConfig).getCacheConfig();
+                    will(returnValue(ResultSetFetchConfig.createComputeAndCache()));
 
                     allowing(resultSetConfig).getAvailableColumns();
                     will(returnValue(null));
@@ -308,8 +309,8 @@ public final class CachedResultSetManagerTest
         assertEquals("1", values.get(1));
     }
 
-    private static List<GridColumnFilterInfo<String>> createFilterList(IColumnDefinition<String> c1,
-            IColumnDefinition<String> c2)
+    private static List<GridColumnFilterInfo<String>> createFilterList(
+            IColumnDefinition<String> c1, IColumnDefinition<String> c2)
     {
         List<GridColumnFilterInfo<String>> result = new ArrayList<GridColumnFilterInfo<String>>();
         result.add(new GridColumnFilterInfo<String>(c1, null));
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinByExperimentBrowserGrid.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinByExperimentBrowserGrid.java
index c787284116c..13a014286a8 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinByExperimentBrowserGrid.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/client/application/ProteinByExperimentBrowserGrid.java
@@ -110,7 +110,7 @@ class ProteinByExperimentBrowserGrid extends AbstractSimpleBrowserGrid<ProteinIn
         criteria.setTreatmentTypeCode(treatmentTypeCode);
         criteria.setAggregateOriginal(aggregateOriginal);
         abundanceColumnDefinitions = definitions;
-        refresh(null, true);
+        refresh(true);
     }
 
     @Override
-- 
GitLab