From 477fcb0a32b353365b16b88b4e147da5bcfd0287 Mon Sep 17 00:00:00 2001
From: felmer <felmer>
Date: Thu, 7 Mar 2013 09:48:26 +0000
Subject: [PATCH] SP-542, BIS-350: Invoke DSS session cleanup from AS at
 logout/session expiration. AbstractDatasetDownloadServlet asks AS for valid
 session token. Valid session tokens are cached (OpenbisSessionTokenCache)
 until session cleanup.

SVN: 28549
---
 .../AbstractDatasetDownloadServlet.java       |  6 +-
 .../generic/server/ApplicationContext.java    | 11 +++-
 .../dss/generic/server/DataStoreServer.java   |  6 +-
 .../dss/generic/server/DataStoreService.java  | 10 ++-
 .../server/OpenbisSessionTokenCache.java      | 61 +++++++++++++++++++
 .../source/java/dssApplicationContext.xml     |  7 ++-
 .../generic/server/DataStoreServiceTest.java  | 14 +++--
 .../server/DatasetDownloadServletTest.java    | 26 +++++++-
 .../generic/server/AbstractServer.java        |  8 +++
 .../generic/server/SessionFactory.java        | 32 +++++-----
 10 files changed, 153 insertions(+), 28 deletions(-)
 create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/OpenbisSessionTokenCache.java

diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/AbstractDatasetDownloadServlet.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/AbstractDatasetDownloadServlet.java
index bf13f10c9ea..c27c5e289d4 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/AbstractDatasetDownloadServlet.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/AbstractDatasetDownloadServlet.java
@@ -90,7 +90,6 @@ abstract public class AbstractDatasetDownloadServlet extends HttpServlet
         this.applicationContext = applicationContext;
     }
 
-    @SuppressWarnings("unchecked")
     @Override
     public final void init(final ServletConfig servletConfig) throws ServletException
     {
@@ -170,6 +169,10 @@ abstract public class AbstractDatasetDownloadServlet extends HttpServlet
             appendServletSessionTimeout(sb);
             operationLog.info(sb.toString());
         }
+        if (applicationContext.getSessionTokenCache().isValidSessionToken(sessionIdOrNull) == false)
+        {
+            return null;
+        }
         return session;
     }
 
@@ -180,7 +183,6 @@ abstract public class AbstractDatasetDownloadServlet extends HttpServlet
         sb.append(" sec");
     }
 
-    @SuppressWarnings("unchecked")
     private void appendRequestParameters(final HttpServletRequest request, StringBuilder sb)
     {
         Enumeration<String> e = request.getParameterNames();
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ApplicationContext.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ApplicationContext.java
index c874a4e70b7..e22ced917e1 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ApplicationContext.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ApplicationContext.java
@@ -37,11 +37,15 @@ class ApplicationContext
 
     private final IHierarchicalContentProvider hierarchicalContentProvider;
 
-    ApplicationContext(IEncapsulatedOpenBISService service, IShareIdManager shareIdManager,
+    private final OpenbisSessionTokenCache sessionTokenCache;
+
+    ApplicationContext(IEncapsulatedOpenBISService service,
+            OpenbisSessionTokenCache sessionTokenCache, IShareIdManager shareIdManager,
             IHierarchicalContentProvider hierarchicalContentProvider,
             ConfigParameters configParameters)
     {
         this.dataSetService = service;
+        this.sessionTokenCache = sessionTokenCache;
         this.shareIdManager = shareIdManager;
         this.configParameters = configParameters;
         this.hierarchicalContentProvider = hierarchicalContentProvider;
@@ -52,6 +56,11 @@ class ApplicationContext
         return dataSetService;
     }
 
+    public OpenbisSessionTokenCache getSessionTokenCache()
+    {
+        return sessionTokenCache;
+    }
+
     public IShareIdManager getShareIdManager()
     {
         return shareIdManager;
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServer.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServer.java
index 0aab796b0a0..46e3bbe87da 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServer.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServer.java
@@ -145,8 +145,12 @@ public class DataStoreServer
         assert server == null : "Server already started";
         ConfigParameters configParams = getConfigParameters();
         final IEncapsulatedOpenBISService openBISService = ServiceProvider.getOpenBISService();
+        OpenbisSessionTokenCache sessionTokenCache =
+                (OpenbisSessionTokenCache) ServiceProvider.getApplicationContext().getBean(
+                        "as-session-token-cache");
         final ApplicationContext applicationContext =
-                new ApplicationContext(openBISService, ServiceProvider.getShareIdManager(),
+                new ApplicationContext(openBISService, sessionTokenCache,
+                        ServiceProvider.getShareIdManager(),
                         ServiceProvider.getHierarchicalContentProvider(), configParams);
         DssSessionAuthorizationHolder.setAuthorizer(new DatasetSessionAuthorizer(configParams
                 .getAuthCacheExpirationTimeMins(), configParams
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreService.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreService.java
index 0cf6d97e5c5..861f277a405 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreService.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreService.java
@@ -82,6 +82,8 @@ public class DataStoreService extends AbstractServiceWithLogger<IDataStoreServic
 {
     private final SessionTokenManager sessionTokenManager;
 
+    private final OpenbisSessionTokenCache sessionTokenCache;
+    
     private final IDataSetCommandExecutorFactory commandExecutorFactory;
 
     private final MailClientParameters mailClientParameters;
@@ -106,10 +108,10 @@ public class DataStoreService extends AbstractServiceWithLogger<IDataStoreServic
 
     private ConfigProvider config;
 
-    public DataStoreService(SessionTokenManager sessionTokenManager,
+    public DataStoreService(SessionTokenManager sessionTokenManager, OpenbisSessionTokenCache sessionTokenCache,
             MailClientParameters mailClientParameters, IPluginTaskInfoProvider pluginTaskParameters)
     {
-        this(sessionTokenManager, new IDataSetCommandExecutorFactory()
+        this(sessionTokenManager, sessionTokenCache, new IDataSetCommandExecutorFactory()
             {
                 @Override
                 public IDataSetCommandExecutor create(File store, File queueDir)
@@ -120,10 +122,11 @@ public class DataStoreService extends AbstractServiceWithLogger<IDataStoreServic
     }
 
     DataStoreService(SessionTokenManager sessionTokenManager,
-            IDataSetCommandExecutorFactory commandExecutorFactory,
+            OpenbisSessionTokenCache sessionTokenCache, IDataSetCommandExecutorFactory commandExecutorFactory,
             MailClientParameters mailClientParameters, IPluginTaskInfoProvider pluginTaskParameters)
     {
         this.sessionTokenManager = sessionTokenManager;
+        this.sessionTokenCache = sessionTokenCache;
         this.commandExecutorFactory = commandExecutorFactory;
         this.mailClientParameters = mailClientParameters;
         this.pluginTaskInfoProvider = pluginTaskParameters;
@@ -491,6 +494,7 @@ public class DataStoreService extends AbstractServiceWithLogger<IDataStoreServic
     @Override
     public void cleanupSession(String userSessionToken)
     {
+        sessionTokenCache.removeSessionToken(userSessionToken);
         final File sessionWorkspace =
                 new File(pluginTaskInfoProvider.getSessionWorkspaceRootDir(), userSessionToken);
         if (sessionWorkspace.exists())
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/OpenbisSessionTokenCache.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/OpenbisSessionTokenCache.java
new file mode 100644
index 00000000000..985b490779d
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/OpenbisSessionTokenCache.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2013 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.dss.generic.server;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import ch.systemsx.cisd.common.exceptions.InvalidSessionException;
+import ch.systemsx.cisd.openbis.generic.shared.IServiceForDataStoreServer;
+
+/**
+ * Cache of user session tokens for openBIS AS.
+ *
+ * @author Franz-Josef Elmer
+ */
+public class OpenbisSessionTokenCache
+{
+    private final IServiceForDataStoreServer service;
+    private final Set<String> sessionTokens = new HashSet<String>();
+
+    public OpenbisSessionTokenCache(IServiceForDataStoreServer service)
+    {
+        this.service = service;
+    }
+    
+    public boolean isValidSessionToken(String sessionToken)
+    {
+        if (sessionTokens.contains(sessionToken))
+        {
+            return true;
+        }
+        try
+        {
+            service.checkSession(sessionToken);
+            sessionTokens.add(sessionToken);
+            return true;
+        } catch (InvalidSessionException ex)
+        {
+            return false;
+        }
+    }
+    
+    public void removeSessionToken(String sessionToken)
+    {
+        sessionTokens.remove(sessionToken);
+    }
+}
diff --git a/datastore_server/source/java/dssApplicationContext.xml b/datastore_server/source/java/dssApplicationContext.xml
index 83af08f032f..c226cee5246 100644
--- a/datastore_server/source/java/dssApplicationContext.xml
+++ b/datastore_server/source/java/dssApplicationContext.xml
@@ -56,6 +56,10 @@
 	      <property name="dataStoreCode" value="${data-store-server-code}"/>
 	  </bean>
 	  
+	  <bean id="as-session-token-cache" class="ch.systemsx.cisd.openbis.dss.generic.server.OpenbisSessionTokenCache">
+	      <constructor-arg ref="etl-lims-service"/>
+	  </bean>
+   
 	  <bean id="reauthenticateInterceptor" class="ch.systemsx.cisd.openbis.dss.generic.server.openbisauth.OpenBISAuthenticationInterceptor">
 	        <constructor-arg ref="session-token-manager"/>
 	        <constructor-arg ref="etl-lims-service"/>
@@ -73,7 +77,7 @@
 	  <bean id="reauthenticateAdvisor" class="ch.systemsx.cisd.openbis.dss.generic.server.openbisauth.OpenBISAuthenticationAdvisor">
 	      <constructor-arg ref="reauthenticateInterceptor"/>
 	  </bean>
-   
+	  
     <bean id="openBIS-service" class="ch.systemsx.cisd.openbis.dss.generic.server.EncapsulatedOpenBISService">
        <constructor-arg ref="etl-lims-service"/>
        <constructor-arg ref="sessionHolder"/>
@@ -96,6 +100,7 @@
     
     <bean id="data-store-service" class="ch.systemsx.cisd.openbis.dss.generic.server.DataStoreService">
         <constructor-arg ref="session-token-manager" />
+        <constructor-arg ref="as-session-token-cache" />
         <constructor-arg>
             <bean class="ch.systemsx.cisd.common.mail.MailClientParameters">
                 <property name="from" value="${mail.from}"/>
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServiceTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServiceTest.java
index c34b497dc57..e1e23a2158a 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServiceTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServiceTest.java
@@ -38,6 +38,7 @@ import ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.IPluginTaskInfo
 import ch.systemsx.cisd.openbis.dss.generic.shared.IShareIdManager;
 import ch.systemsx.cisd.openbis.dss.generic.shared.utils.PluginUtilTest;
 import ch.systemsx.cisd.openbis.generic.shared.IDataStoreService;
+import ch.systemsx.cisd.openbis.generic.shared.IServiceForDataStoreServer;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.AbstractExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetUploadContext;
 import ch.systemsx.cisd.openbis.generic.shared.dto.builders.DatasetDescriptionBuilder;
@@ -61,13 +62,13 @@ public class DataStoreServiceTest extends AssertJUnit
 
         private final String expectedCIFEXURL;
 
-        MockDataStoreService(SessionTokenManager sessionTokenManager,
+        MockDataStoreService(SessionTokenManager sessionTokenManager, OpenbisSessionTokenCache sessionTokenCache,
                 IDataSetCommandExecutorFactory commandExecutorFactory,
                 MailClientParameters mailClientParameters,
                 ICIFEXRPCServiceFactory cifexServiceFactory, String expectedCIFEXURL,
                 IPluginTaskInfoProvider pluginTaskParameters)
         {
-            super(sessionTokenManager, commandExecutorFactory, mailClientParameters,
+            super(sessionTokenManager, sessionTokenCache, commandExecutorFactory, mailClientParameters,
                     pluginTaskParameters);
             this.cifexServiceFactory = cifexServiceFactory;
             this.expectedCIFEXURL = expectedCIFEXURL;
@@ -101,12 +102,15 @@ public class DataStoreServiceTest extends AssertJUnit
 
     private IShareIdManager shareIdManager;
 
+    private IServiceForDataStoreServer openbisService;
+
     @BeforeMethod
     public void setup()
     {
         context = new Mockery();
         sessionTokenManager = new SessionTokenManager();
         sessionToken = sessionTokenManager.drawSessionToken();
+        openbisService = context.mock(IServiceForDataStoreServer.class);
         commandExecutorFactory = context.mock(IDataSetCommandExecutorFactory.class);
         commandExecutor = context.mock(IDataSetCommandExecutor.class);
         shareIdManager = context.mock(IShareIdManager.class);
@@ -289,9 +293,11 @@ public class DataStoreServiceTest extends AssertJUnit
                     will(returnValue(commandExecutor));
                 }
             });
+        OpenbisSessionTokenCache sessionTokenCache = new OpenbisSessionTokenCache(openbisService);
         MockDataStoreService service =
-                new MockDataStoreService(sessionTokenManager, commandExecutorFactory,
-                        mailClientParameters, cifexServiceFactory, CIFEX_URL, pluginTaskParameters);
+                new MockDataStoreService(sessionTokenManager, sessionTokenCache,
+                        commandExecutorFactory, mailClientParameters, cifexServiceFactory,
+                        CIFEX_URL, pluginTaskParameters);
         service.setShareIdManager(shareIdManager);
         service.afterPropertiesSet();
         return service;
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/DatasetDownloadServletTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/DatasetDownloadServletTest.java
index e841dd9e388..a99cfe04620 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/DatasetDownloadServletTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/DatasetDownloadServletTest.java
@@ -62,6 +62,7 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IHierarchicalContentProvider;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IShareIdManager;
 import ch.systemsx.cisd.openbis.dss.generic.shared.utils.DatasetLocationUtil;
+import ch.systemsx.cisd.openbis.generic.shared.IServiceForDataStoreServer;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.PhysicalDataSet;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataStore;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
@@ -155,6 +156,8 @@ public class DatasetDownloadServletTest
 
     private IHierarchicalContentProvider hierarchicalContentProvider;
 
+    private IServiceForDataStoreServer service;
+
     @BeforeMethod
     public void setUp()
     {
@@ -165,6 +168,7 @@ public class DatasetDownloadServletTest
         response = context.mock(HttpServletResponse.class);
         shareIdManager = context.mock(IShareIdManager.class);
         openbisService = context.mock(IEncapsulatedOpenBISService.class);
+        service = context.mock(IServiceForDataStoreServer.class);
         // test with DefaultFileBasedHierarchicalContentFactory to actually access files
         final IHierarchicalContentFactory fileBasedContentFactory =
                 new DefaultFileBasedHierarchicalContentFactory();
@@ -207,6 +211,7 @@ public class DatasetDownloadServletTest
         final StringWriter writer = new StringWriter();
         final AbstractExternalData externalData = createDataSet();
         prepareParseRequestURL();
+        prepareCheckSession();
         prepareForObtainingDataSetFromServer(externalData);
         prepareForGettingDataSetFromSession(externalData, "");
         prepareLocking();
@@ -274,6 +279,16 @@ public class DatasetDownloadServletTest
                 }
             });
     }
+    
+    private void prepareCheckSession()
+    {
+        context.checking(new Expectations()
+            {
+                {
+                    one(service).checkSession(EXAMPLE_SESSION_ID);
+                }
+            });
+    }
 
     @Test()
     public void testInitialDoGetButDataSetNotFoundInStore() throws Exception
@@ -281,6 +296,7 @@ public class DatasetDownloadServletTest
         final StringWriter writer = new StringWriter();
         final AbstractExternalData externalData = createDataSet();
         prepareParseRequestURL();
+        prepareCheckSession();
         prepareForObtainingDataSetFromServer(externalData);
         prepareLocking();
         context.checking(new Expectations()
@@ -318,6 +334,7 @@ public class DatasetDownloadServletTest
     {
         final StringWriter writer = new StringWriter();
         prepareParseRequestURL();
+        prepareCheckSession();
         prepareForObtainingDataSetFromServer(null);
         context.checking(new Expectations()
             {
@@ -356,6 +373,7 @@ public class DatasetDownloadServletTest
     {
         final StringWriter writer = new StringWriter();
         final AbstractExternalData externalData = createDataSet();
+        prepareCheckSession();
         prepareForObtainingDataSetFromServer(externalData);
         prepareForGettingDataSetFromSession(externalData, ESCAPED_EXAMPLE_DATA_SET_SUB_FOLDER_NAME);
         prepareLocking();
@@ -392,6 +410,7 @@ public class DatasetDownloadServletTest
 
         final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
         prepareParseRequestURL();
+        prepareCheckSession();
         prepareCheckDatasetAccess();
         prepareForObtainingDataSetFromServer(externalData);
         prepareLocking();
@@ -436,6 +455,7 @@ public class DatasetDownloadServletTest
         BufferedImage image = new BufferedImage(100, 200, BufferedImage.TYPE_INT_RGB);
         ImageIO.write(image, "png", EXAMPLE_FILE);
         prepareParseRequestURLForThumbnail(100, 50);
+        prepareCheckSession();
         final AbstractExternalData externalData = createDataSet();
         prepareCheckDatasetAccess();
         prepareForObtainingDataSetFromServer(externalData);
@@ -487,6 +507,7 @@ public class DatasetDownloadServletTest
         final StringWriter writer = new StringWriter();
         final AbstractExternalData externalData = createDataSet();
         prepareParseRequestURL();
+        prepareCheckSession();
         prepareForObtainingDataSetFromServer(externalData);
         prepareLocking();
         context.checking(new Expectations()
@@ -822,8 +843,9 @@ public class DatasetDownloadServletTest
         properties.setProperty(ConfigParameters.KEYSTORE_KEY_PASSWORD_KEY, "y");
         properties.setProperty(ConfigParameters.DOWNLOAD_URL, "http://localhost:8080");
         ConfigParameters configParameters = new ConfigParameters(properties);
-        return new DatasetDownloadServlet(new ApplicationContext(openbisService, shareIdManager,
-                hierarchicalContentProvider, configParameters));
+        OpenbisSessionTokenCache sessionTokenCache = new OpenbisSessionTokenCache(service);
+        return new DatasetDownloadServlet(new ApplicationContext(openbisService, sessionTokenCache,
+                shareIdManager, hierarchicalContentProvider, configParameters));
     }
 
     private String getNormalizedLogContent()
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java
index 1d44689d3ac..2df19bc1267 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/AbstractServer.java
@@ -50,6 +50,7 @@ import ch.systemsx.cisd.openbis.common.spring.AbstractServiceWithLogger;
 import ch.systemsx.cisd.openbis.generic.server.authorization.annotation.ReturnValueFilter;
 import ch.systemsx.cisd.openbis.generic.server.authorization.annotation.RolesAllowed;
 import ch.systemsx.cisd.openbis.generic.server.authorization.validator.ExpressionValidator;
+import ch.systemsx.cisd.openbis.generic.server.business.IDataStoreServiceFactory;
 import ch.systemsx.cisd.openbis.generic.server.business.IPropertiesBatchManager;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.DataSetTable;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IDataSetTable;
@@ -148,6 +149,9 @@ public abstract class AbstractServer<T> extends AbstractServiceWithLogger<T> imp
     @Resource(name = ComponentNames.DAO_FACTORY)
     private IDAOFactory daoFactory;
 
+    @Resource(name = ComponentNames.DSS_FACTORY)
+    private IDataStoreServiceFactory dssFactory;
+
     @Resource(name = ComponentNames.REMOTE_HOST_VALIDATOR)
     private IRemoteHostValidator remoteHostValidator;
 
@@ -447,6 +451,8 @@ public abstract class AbstractServer<T> extends AbstractServiceWithLogger<T> imp
         try
         {
             sessionManager.closeSession(sessionToken);
+            SessionFactory.cleanUpSessionOnDataStoreServers(sessionToken,
+                    daoFactory.getDataStoreDAO(), dssFactory);
         } catch (InvalidSessionException e)
         {
             // ignore the situation when session is not available
@@ -460,6 +466,8 @@ public abstract class AbstractServer<T> extends AbstractServiceWithLogger<T> imp
         try
         {
             sessionManager.expireSession(sessionToken);
+            SessionFactory.cleanUpSessionOnDataStoreServers(sessionToken,
+                    daoFactory.getDataStoreDAO(), dssFactory);
         } catch (InvalidSessionException e)
         {
             // ignore the situation when session is not available
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/SessionFactory.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/SessionFactory.java
index f09df44ecd8..d35190f0fbe 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/SessionFactory.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/SessionFactory.java
@@ -74,23 +74,27 @@ public final class SessionFactory implements ISessionFactory<Session>
                     @Override
                     public void cleanup()
                     {
-                        for (DataStorePE datastore : datastoreDAO.listDataStores())
-                        {
-                            final String remoteUrl = datastore.getRemoteUrl();
-                            if (StringUtils.isBlank(remoteUrl) == false)
-                            {
-                                dssFactory.createMonitored(remoteUrl).cleanupSession(
-                                        sessionToken);
-                            } else
-                            {
-                                operationLog.warn("datastore remoteUrl of datastore "
-                                        + datastore.getCode()
-                                        + " is empty - skipping DSS session cleanup.");
-                            }
-                        }
+                        cleanUpSessionOnDataStoreServers(sessionToken, datastoreDAO, dssFactory);
                     }
                 });
         }
         return session;
     }
+
+    public static void cleanUpSessionOnDataStoreServers(String sessionToken,
+            IDataStoreDAO datastoreDAO, IDataStoreServiceFactory dssFactory)
+    {
+        for (DataStorePE datastore : datastoreDAO.listDataStores())
+        {
+            final String remoteUrl = datastore.getRemoteUrl();
+            if (StringUtils.isBlank(remoteUrl) == false)
+            {
+                dssFactory.createMonitored(remoteUrl).cleanupSession(sessionToken);
+            } else
+            {
+                operationLog.warn("datastore remoteUrl of datastore " + datastore.getCode()
+                        + " is empty - skipping DSS session cleanup.");
+            }
+        }
+    }
 }
-- 
GitLab