diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonBusinessObjectFactory.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonBusinessObjectFactory.java
index fbaea4cd2629722cc57fa5d20bebda84a5f19350..781494c0e1867a05717d5784bd2d17efd1c765de 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonBusinessObjectFactory.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonBusinessObjectFactory.java
@@ -62,15 +62,12 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
 public final class CommonBusinessObjectFactory extends AbstractBusinessObjectFactory implements
         ICommonBusinessObjectFactory
 {
-    public CommonBusinessObjectFactory(final IDAOFactory daoFactory)
+
+    public CommonBusinessObjectFactory(IDAOFactory daoFactory, IDataStoreServiceFactory dssFactory)
     {
-        super(daoFactory);
+        super(daoFactory, dssFactory);
     }
 
-    //
-    // IGenericBusinessObjectFactory
-    //
-
     public final IGroupBO createGroupBO(final Session session)
     {
         return new GroupBO(getDaoFactory(), session);
@@ -98,7 +95,7 @@ public final class CommonBusinessObjectFactory extends AbstractBusinessObjectFac
 
     public final IExternalDataTable createExternalDataTable(final Session session)
     {
-        return new ExternalDataTable(getDaoFactory(), session);
+        return new ExternalDataTable(getDaoFactory(), getDSSFactory(), session);
     }
 
     public IExperimentTable createExperimentTable(final Session session)
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
index 6d655aa1ab9cfcb981e2579f5f1ec8673881dcf6..79a611a3a2de2d058dfe4691fbdd669f98c421ac 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
@@ -31,7 +31,6 @@ import ch.systemsx.cisd.authentication.ISessionManager;
 import ch.systemsx.cisd.authentication.Principal;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
-import ch.systemsx.cisd.openbis.generic.server.business.DataStoreServerSessionManager;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.ICommonBusinessObjectFactory;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IEntityTypeBO;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IEntityTypePropertyTypeBO;
@@ -113,19 +112,15 @@ public final class CommonServer extends AbstractServer<ICommonServer> implements
 
     private final ICommonBusinessObjectFactory businessObjectFactory;
 
-    private final DataStoreServerSessionManager dssSessionManager;
-
     private final LastModificationState lastModificationState;
 
     public CommonServer(final IAuthenticationService authenticationService,
-            final ISessionManager<Session> sessionManager,
-            DataStoreServerSessionManager dssSessionManager, final IDAOFactory daoFactory,
+            final ISessionManager<Session> sessionManager, final IDAOFactory daoFactory,
             final ICommonBusinessObjectFactory businessObjectFactory,
             LastModificationState lastModificationState)
     {
         super(sessionManager, daoFactory);
         this.authenticationService = authenticationService;
-        this.dssSessionManager = dssSessionManager;
         this.businessObjectFactory = businessObjectFactory;
         this.lastModificationState = lastModificationState;
     }
@@ -646,7 +641,7 @@ public final class CommonServer extends AbstractServer<ICommonServer> implements
             {
                 DataSetTypePE dataSetType = entry.getKey();
                 IDataSetTypeSlaveServerPlugin plugin = getDataSetTypeSlaveServerPlugin(dataSetType);
-                plugin.deleteDataSets(session, dssSessionManager, entry.getValue(), reason);
+                plugin.deleteDataSets(session, entry.getValue(), reason);
             }
         } catch (final DataAccessException ex)
         {
@@ -663,7 +658,7 @@ public final class CommonServer extends AbstractServer<ICommonServer> implements
             IExternalDataTable externalDataTable =
                     businessObjectFactory.createExternalDataTable(session);
             externalDataTable.loadByDataSetCodes(dataSetCodes);
-            externalDataTable.uploadLoadedDataSetsToCIFEX(dssSessionManager, uploadContext);
+            externalDataTable.uploadLoadedDataSetsToCIFEX(uploadContext);
         } catch (final DataAccessException ex)
         {
             throw createUserFailureException(ex);
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ComponentNames.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ComponentNames.java
index adf1e4c8c61e2d89ef86412567385f0d8919299e..e54907126cbc3c26692880626b6feae2d20d1a8c 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ComponentNames.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ComponentNames.java
@@ -36,6 +36,8 @@ public final class ComponentNames
     public static final String LOG_INTERCEPTOR = "log-interceptor";
 
     public static final String DAO_FACTORY = "dao-factory";
+    
+    public static final String DSS_FACTORY = "dss-factory";
 
     public static final String COMMON_BUSINESS_OBJECT_FACTORY = "common-business-object-factory";
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/DataStoreServiceFactory.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/DataStoreServiceFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..0087c4223b9749fd1bc160f048ccdda5ac40f081
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/DataStoreServiceFactory.java
@@ -0,0 +1,48 @@
+/*
+ * 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.server;
+
+import static ch.systemsx.cisd.openbis.generic.shared.GenericSharedConstants.DATA_STORE_SERVER_SERVICE_NAME;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import ch.systemsx.cisd.common.spring.HttpInvokerUtils;
+import ch.systemsx.cisd.openbis.generic.shared.IDataStoreService;
+
+/**
+ * Factory of cached {@link IDataStoreService} instances.
+ *
+ * @author Franz-Josef Elmer
+ */
+public class DataStoreServiceFactory implements IDataStoreServiceFactory
+{
+    private final Map<String, IDataStoreService> services = new HashMap<String, IDataStoreService>();
+    
+    public IDataStoreService create(String serverURL)
+    {
+        IDataStoreService service = services.get(serverURL);
+        if (service == null)
+        {
+            service = HttpInvokerUtils.createServiceStub(IDataStoreService.class, serverURL + "/"
+                    + DATA_STORE_SERVER_SERVICE_NAME, 5);
+            services.put(serverURL, service);
+        }
+        return service;
+    }
+
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java
index e75e88a6e376c87a3998f5ec61a62f993049b43f..f1deabd587ba42cc956e31ce6e4dd2ac087da9d2 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java
@@ -16,8 +16,6 @@
 
 package ch.systemsx.cisd.openbis.generic.server;
 
-import static ch.systemsx.cisd.openbis.generic.shared.GenericSharedConstants.DATA_STORE_SERVER_SERVICE_NAME;
-
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.List;
@@ -27,18 +25,18 @@ import ch.systemsx.cisd.authentication.ISessionManager;
 import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
-import ch.systemsx.cisd.common.spring.HttpInvokerUtils;
 import ch.systemsx.cisd.common.utilities.BeanUtils;
-import ch.systemsx.cisd.openbis.generic.server.business.DataStoreServerSessionManager;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.ICommonBusinessObjectFactory;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IExternalDataBO;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.ISampleBO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IAttachmentDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDataStoreDAO;
 import ch.systemsx.cisd.openbis.generic.shared.IDataStoreService;
 import ch.systemsx.cisd.openbis.generic.shared.IWebService;
 import ch.systemsx.cisd.openbis.generic.shared.dto.AttachmentPE;
-import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServerSession;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataStorePE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServerInfo;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalData;
@@ -72,36 +70,17 @@ public class ETLService extends AbstractServer<IETLService> implements IETLServi
 
     private final ISessionManager<Session> sessionManager;
 
-    private final DataStoreServerSessionManager dssSessionManager;
-
     private final IDAOFactory daoFactory;
 
     private final ICommonBusinessObjectFactory boFactory;
 
     private final IDataStoreServiceFactory dssFactory;
 
-    public ETLService(ISessionManager<Session> sessionManager,
-            DataStoreServerSessionManager dssSessionManager, IDAOFactory daoFactory,
-            ICommonBusinessObjectFactory boFactory)
-    {
-        this(sessionManager, dssSessionManager, daoFactory, boFactory,
-                new IDataStoreServiceFactory()
-                    {
-                        public IDataStoreService create(String serverURL)
-                        {
-                            return HttpInvokerUtils.createServiceStub(IDataStoreService.class,
-                                    serverURL + "/" + DATA_STORE_SERVER_SERVICE_NAME, 5);
-                        }
-                    });
-    }
-
-    ETLService(ISessionManager<Session> sessionManager,
-            DataStoreServerSessionManager dssSessionManager, IDAOFactory daoFactory,
+    public ETLService(ISessionManager<Session> sessionManager, IDAOFactory daoFactory,
             ICommonBusinessObjectFactory boFactory, IDataStoreServiceFactory dssFactory)
     {
         super(sessionManager, daoFactory);
         this.sessionManager = sessionManager;
-        this.dssSessionManager = dssSessionManager;
         this.daoFactory = daoFactory;
         this.boFactory = boFactory;
         this.dssFactory = dssFactory;
@@ -129,13 +108,17 @@ public class ETLService extends AbstractServer<IETLService> implements IETLServi
         return daoFactory.getHomeDatabaseInstance();
     }
 
-    public void registerDataStoreServer(String sessionToken, int port, final String dssSessionToken)
+    public void registerDataStoreServer(String sessionToken, DataStoreServerInfo info)
     {
         Session session = sessionManager.getSession(sessionToken);
+        
+        String dssSessionToken = info.getSessionToken();
         String remoteHost = session.getRemoteHost();
+        int port = info.getPort();
         final String dssURL = "https://" + remoteHost + ":" + port;
-        DataStoreServerSession dssSession = dssSessionManager.tryToGetSession(dssURL);
-        if (dssSession == null)
+        IDataStoreDAO dataStoreDAO = daoFactory.getDataStoreDAO();
+        DataStorePE dataStore = dataStoreDAO.tryToFindDataStoreByCode(info.getDataStoreCode());
+        if (dataStore == null)
         {
             final IDataStoreService service = dssFactory.create(dssURL);
             if (operationLog.isInfoEnabled())
@@ -146,8 +129,8 @@ public class ETLService extends AbstractServer<IETLService> implements IETLServi
             if (IDataStoreService.VERSION != dssVersion)
             {
                 String msg =
-                        "Data Store Server version is " + dssVersion + " instead of "
-                                + IDataStoreService.VERSION;
+                    "Data Store Server version is " + dssVersion + " instead of "
+                    + IDataStoreService.VERSION;
                 notificationLog.error(msg);
                 throw new ConfigurationFailureException(msg);
             }
@@ -156,10 +139,14 @@ public class ETLService extends AbstractServer<IETLService> implements IETLServi
                 operationLog.info("Data Store Server (version " + dssVersion + ") registered for "
                         + dssURL);
             }
-            dssSession = new DataStoreServerSession(dssURL, service);
-            dssSessionManager.registerDataStoreServer(dssSession);
+            dataStore = new DataStorePE();
+            dataStore.setDatabaseInstance(getHomeDatabaseInstance(sessionToken));
         }
-        dssSession.setSessionToken(dssSessionToken);
+        dataStore.setCode(info.getDataStoreCode());
+        dataStore.setDownloadUrl(info.getDownloadUrl());
+        dataStore.setRemoteUrl(dssURL);
+        dataStore.setSessionToken(dssSessionToken);
+        dataStoreDAO.createOrUpdateDataStore(dataStore);
     }
 
     public String authenticate(String user, String password) throws UserFailureException
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceLogger.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceLogger.java
index d556cc0df852908e2de64664dd32ddcad706a5f8..ef606f9a8be9b252e0138ee15e2223d1cb176257 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceLogger.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceLogger.java
@@ -18,6 +18,7 @@ package ch.systemsx.cisd.openbis.generic.server;
 
 import ch.systemsx.cisd.authentication.ISessionManager;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServerInfo;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalData;
@@ -50,10 +51,15 @@ public class ETLServiceLogger extends AbstractServerLogger implements IETLServic
         return null;
     }
 
-    public void registerDataStoreServer(String sessionToken, int port, String dssSessionToken)
+    public void registerDataStoreServer(String sessionToken, DataStoreServerInfo info)
     {
-        logTracking(sessionToken, "register_data_store_server_session_token", "PORT(%s) DSS-TOKEN(%s)",
-                port, dssSessionToken);
+        String code = info.getDataStoreCode();
+        String downloadUrl = info.getDownloadUrl();
+        int port = info.getPort();
+        String dssSessionToken = info.getSessionToken();
+        logTracking(sessionToken, "register_data_store_server_session_token",
+                "CODE(%s) DOWNLOAD-URL(%s) PORT(%s) DSS-TOKEN(%s)", code, downloadUrl, port,
+                dssSessionToken);
     }
 
     public void registerDataSet(String sessionToken, SampleIdentifier sampleIdentifier,
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/DataStoreServerSessionManager.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/DataStoreServerSessionManager.java
deleted file mode 100644
index 6dc593f3256501900a34ba3d1a8191ca19a72f85..0000000000000000000000000000000000000000
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/DataStoreServerSessionManager.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.server.business;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServerSession;
-
-/**
- * Class managing Data Store Server (DSS) sessions. It is assumed that the URL of the DSS is unique
- * in order to allow more than one DSS.
- * 
- * @author Franz-Josef Elmer
- */
-public class DataStoreServerSessionManager
-{
-    private final Map<String, DataStoreServerSession> sessions = new HashMap<String, DataStoreServerSession>();
-    
-    /**
-     * Registers the specified DSS session.
-     */
-    public void registerDataStoreServer(DataStoreServerSession session)
-    {
-        synchronized (sessions)
-        {
-            sessions.put(session.getDataStoreServerURL(), session);
-        }
-    }
-    
-    /**
-     * Tries to get the DSS session identified by the specified Data Store Server URL.
-     * 
-     * @return <code>null</code> if no session has been registered for the specified URL.
-     */
-    public DataStoreServerSession tryToGetSession(String dssURL)
-    {
-        synchronized (sessions)
-        {
-            return sessions.get(dssURL);
-        }
-    }
-
-    /**
-     * Returns all registered DSS sessions.
-     */
-    public Collection<DataStoreServerSession> getSessions()
-    {
-        synchronized (sessions)
-        {
-            return new ArrayList<DataStoreServerSession>(sessions.values());
-        }
-    }
-}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBusinessObject.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBusinessObject.java
index 2742676ee63fc925baa9f66e975f3fd7d786e8f5..0f0ea0de0f83e9a700772ab15900f8ea979eb22d 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBusinessObject.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBusinessObject.java
@@ -21,6 +21,7 @@ import org.springframework.dao.DataAccessException;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDataSetTypeDAO;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDataStoreDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDatabaseInstanceDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IEntityPropertyTypeDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IEntityTypeDAO;
@@ -197,4 +198,9 @@ abstract class AbstractBusinessObject implements IDAOFactory
     {
         return daoFactory.getCodeSequenceDAO();
     }
+
+    public IDataStoreDAO getDataStoreDAO()
+    {
+        return daoFactory.getDataStoreDAO();
+    }
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBusinessObjectFactory.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBusinessObjectFactory.java
index 2ba222d2d596aa9630b5ccf00981d7affd607749..d7413f838d52273028cd7e36fc4e3a7b7fe36994 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBusinessObjectFactory.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBusinessObjectFactory.java
@@ -19,6 +19,7 @@ package ch.systemsx.cisd.openbis.generic.server.business.bo;
 import javax.annotation.Resource;
 
 import ch.systemsx.cisd.openbis.generic.server.ComponentNames;
+import ch.systemsx.cisd.openbis.generic.server.IDataStoreServiceFactory;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 
 /**
@@ -30,18 +31,27 @@ public abstract class AbstractBusinessObjectFactory
 {
     @Resource(name = ComponentNames.DAO_FACTORY)
     private IDAOFactory daoFactory;
+    
+    @Resource(name = ComponentNames.DSS_FACTORY)
+    private IDataStoreServiceFactory dssFactory;
 
     protected AbstractBusinessObjectFactory()
     {
     }
 
-    protected AbstractBusinessObjectFactory(final IDAOFactory daoFactory)
+    protected AbstractBusinessObjectFactory(final IDAOFactory daoFactory, IDataStoreServiceFactory dssFactory)
     {
         this.daoFactory = daoFactory;
+        this.dssFactory = dssFactory;
     }
 
     protected final IDAOFactory getDaoFactory()
     {
         return daoFactory;
     }
+    
+    protected final IDataStoreServiceFactory getDSSFactory()
+    {
+        return dssFactory;
+    }
 }
\ No newline at end of file
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBO.java
index f954a03f56126158cff8db8d9d64300af97c82a1..8ab996f977bcab55af0bd88afbdf88b724b05a13 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBO.java
@@ -115,6 +115,7 @@ public class ExternalDataBO extends AbstractExternalDataBusinessObject implement
                 .getStorageFormat()));
         externalData.setLocatorType(getLocatorTypeDAO().tryToFindLocatorTypeByCode(
                 locatorType.getCode()));
+        externalData.setDataStore(getDataStoreDAO().tryToFindDataStoreByCode(data.getDataStoreCode()));
         defineDataSetProperties(externalData, convertToDataSetProperties(data
                 .getDataSetProperties()));
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataTable.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataTable.java
index c3854676661ed1cdfb4411204b56296f5c745670..1010523250504d32a32de2bd1f28c0a3d12cc305 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataTable.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataTable.java
@@ -17,23 +17,24 @@
 package ch.systemsx.cisd.openbis.generic.server.business.bo;
 
 import java.util.ArrayList;
-import java.util.Collection;
+import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.apache.commons.lang.StringUtils;
 
 import ch.rinn.restrictions.Private;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
-import ch.systemsx.cisd.openbis.generic.server.business.DataStoreServerSessionManager;
+import ch.systemsx.cisd.openbis.generic.server.IDataStoreServiceFactory;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IExternalDataDAO;
 import ch.systemsx.cisd.openbis.generic.shared.IDataStoreService;
 import ch.systemsx.cisd.openbis.generic.shared.basic.BasicConstant;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetUploadContext;
-import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServerSession;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataStorePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ProjectPE;
@@ -84,12 +85,15 @@ public final class ExternalDataTable extends AbstractExternalDataBusinessObject
         return builder.toString();
     }
     
+    private final IDataStoreServiceFactory dssFactory;
 
     private List<ExternalDataPE> externalData;
 
-    public ExternalDataTable(final IDAOFactory daoFactory, final Session session)
+    public ExternalDataTable(final IDAOFactory daoFactory, IDataStoreServiceFactory dssFactory,
+            final Session session)
     {
         super(daoFactory, session);
+        this.dssFactory = dssFactory;
     }
 
     //
@@ -157,63 +161,62 @@ public final class ExternalDataTable extends AbstractExternalDataBusinessObject
         }
     }
 
-    public void deleteLoadedDataSets(DataStoreServerSessionManager dssSessionManager, String reason)
+    public void deleteLoadedDataSets(String reason)
     {
-        assertDataSetsAreKnown(dssSessionManager);
-        for (ExternalDataPE dataSet : externalData)
-        {
-            IExternalDataDAO externalDataDAO = getExternalDataDAO();
-            externalDataDAO.markAsDeleted(dataSet, session.tryGetPerson(), DELETION_DESCRIPTION, reason);
-        }
-        Collection<DataStoreServerSession> sessions = dssSessionManager.getSessions();
-        List<String> locations = getLocations();
-        for (DataStoreServerSession dssSession : sessions)
+        Map<DataStorePE, List<ExternalDataPE>> map = groupDataSetsByDataStores();
+        assertDataSetsAreKnown(map);
+        IExternalDataDAO externalDataDAO = getExternalDataDAO();
+        for (Map.Entry<DataStorePE, List<ExternalDataPE>> entry : map.entrySet())
         {
-            dssSession.getService().deleteDataSets(dssSession.getSessionToken(), locations);
+            DataStorePE dataStore = entry.getKey();
+            List<ExternalDataPE> dataSets = entry.getValue();
+            for (ExternalDataPE dataSet : dataSets)
+            {
+                externalDataDAO.markAsDeleted(dataSet, session.tryGetPerson(), DELETION_DESCRIPTION, reason);
+            }
+            deleteDataSets(dataStore, getLocations(dataSets));
         }
     }
 
-    public void uploadLoadedDataSetsToCIFEX(DataStoreServerSessionManager dssSessionManager,
-            DataSetUploadContext uploadContext)
+    public void uploadLoadedDataSetsToCIFEX(DataSetUploadContext uploadContext)
     {
-        assertDataSetsAreKnown(dssSessionManager);
-        Collection<DataStoreServerSession> sessions = dssSessionManager.getSessions();
+        Map<DataStorePE, List<ExternalDataPE>> map = groupDataSetsByDataStores();
+        assertDataSetsAreKnown(map);
         uploadContext.setUserEMail(session.getPrincipal().getEmail());
         if (StringUtils.isBlank(uploadContext.getComment()))
         {
             uploadContext.setComment(createUploadComment(externalData));
         }
-        for (ExternalDataPE dataSet : externalData)
+        for (Map.Entry<DataStorePE, List<ExternalDataPE>> entry : map.entrySet())
         {
-            HibernateUtils.initialize(dataSet.getParents());
-            SamplePE sample = dataSet.getAssociatedSample();
-            ExperimentPE experiment;
-            if (sample != null)
+            DataStorePE dataStore = entry.getKey();
+            List<ExternalDataPE> dataSets = entry.getValue();
+            for (ExternalDataPE dataSet : dataSets)
             {
-                experiment = sample.getExperiment();
-            } else
-            {
-                experiment = dataSet.getExperiment();
+                HibernateUtils.initialize(dataSet.getParents());
+                SamplePE sample = dataSet.getAssociatedSample();
+                ExperimentPE experiment;
+                if (sample != null)
+                {
+                    experiment = sample.getExperiment();
+                } else
+                {
+                    experiment = dataSet.getExperiment();
+                }
+                HibernateUtils.initialize(experiment.getProject().getGroup());
             }
-            HibernateUtils.initialize(experiment.getProject().getGroup());
-        }
-        for (DataStoreServerSession dssSession : sessions)
-        {
-            dssSession.getService().uploadDataSetsToCIFEX(dssSession.getSessionToken(), externalData,
-                    uploadContext);
+            uploadDataSetsToCIFEX(dataStore, dataSets, uploadContext);
         }
     }
 
-    private void assertDataSetsAreKnown(DataStoreServerSessionManager dssSessionManager)
+    private void assertDataSetsAreKnown(Map<DataStorePE, List<ExternalDataPE>> map)
     {
-        List<String> locations = getLocations();
         Set<String> knownLocations = new LinkedHashSet<String>();
-        Collection<DataStoreServerSession> sessions = dssSessionManager.getSessions();
-        for (DataStoreServerSession dssSession : sessions)
+        for (Map.Entry<DataStorePE, List<ExternalDataPE>> entry : map.entrySet())
         {
-            IDataStoreService service = dssSession.getService();
-            String dssSessionToken = dssSession.getSessionToken();
-            knownLocations.addAll(service.getKnownDataSets(dssSessionToken, locations));
+            DataStorePE dataStore = entry.getKey();
+            List<String> locations = getLocations(entry.getValue());
+            knownLocations.addAll(getKnownDataSets(dataStore, locations));
         }
         List<String> unknownDataSets = new ArrayList<String>();
         for (ExternalDataPE dataSet : externalData)
@@ -232,13 +235,52 @@ public final class ExternalDataTable extends AbstractExternalDataBusinessObject
         }
     }
     
-    private List<String> getLocations()
+    private Map<DataStorePE, List<ExternalDataPE>> groupDataSetsByDataStores()
     {
-        List<String> locations = new ArrayList<String>();
+        Map<DataStorePE, List<ExternalDataPE>> map = new LinkedHashMap<DataStorePE, List<ExternalDataPE>>();
         for (ExternalDataPE dataSet : externalData)
+        {
+            DataStorePE dataStore = dataSet.getDataStore();
+            List<ExternalDataPE> list = map.get(dataStore);
+            if (list == null)
+            {
+                list = new ArrayList<ExternalDataPE>();
+                map.put(dataStore, list);
+            }
+            list.add(dataSet);
+        }
+        return map;
+    }
+
+    private List<String> getLocations(List<ExternalDataPE> dataSets)
+    {
+        List<String> locations = new ArrayList<String>();
+        for (ExternalDataPE dataSet : dataSets)
         {
             locations.add(dataSet.getLocation());
         }
         return locations;
     }
+
+    private void uploadDataSetsToCIFEX(DataStorePE dataStore, List<ExternalDataPE> dataSets,
+            DataSetUploadContext context)
+    {
+        IDataStoreService service = dssFactory.create(dataStore.getRemoteUrl());
+        String sessionToken = dataStore.getSessionToken();
+        service.uploadDataSetsToCIFEX(sessionToken, dataSets, context);
+    }
+    
+    private void deleteDataSets(DataStorePE dataStore, List<String> locations)
+    {
+        IDataStoreService service = dssFactory.create(dataStore.getRemoteUrl());
+        String sessionToken = dataStore.getSessionToken();
+        service.deleteDataSets(sessionToken, locations);
+    }
+    
+    private List<String> getKnownDataSets(DataStorePE dataStore, List<String> locations)
+    {
+        IDataStoreService service = dssFactory.create(dataStore.getRemoteUrl());
+        String sessionToken = dataStore.getSessionToken();
+        return service.getKnownDataSets(sessionToken, locations);
+    }
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IExternalDataTable.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IExternalDataTable.java
index d9d1a91dc82e6d4d6889f1217776506bd5e7b774..14796d990b23c3f0a537d859fa25fb253c97d84e 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IExternalDataTable.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IExternalDataTable.java
@@ -18,7 +18,6 @@ package ch.systemsx.cisd.openbis.generic.server.business.bo;
 
 import java.util.List;
 
-import ch.systemsx.cisd.openbis.generic.server.business.DataStoreServerSessionManager;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetUploadContext;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifier;
@@ -58,16 +57,13 @@ public interface IExternalDataTable
     void setExternalData(List<ExternalDataPE> externalData);
     
     /**
-     * Deletes loaded data sets for specified reason on all Data Store Servers registered
-     * at specified manager.
+     * Deletes loaded data sets for specified reason.
      */
-    void deleteLoadedDataSets(DataStoreServerSessionManager dssSessionManager, String reason);
+    void deleteLoadedDataSets(String reason);
     
     /**
-     * Uploads loaded data sets to CIFEX server of specified URL using specified upload
-     * context.
+     * Uploads loaded data sets to CIFEX server as specified in the upload context.
      */
-    void uploadLoadedDataSetsToCIFEX(DataStoreServerSessionManager dssSessionManager,
-            DataSetUploadContext uploadContext);
+    void uploadLoadedDataSetsToCIFEX(DataSetUploadContext uploadContext);
 
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IDAOFactory.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IDAOFactory.java
index a9afc29f50e27a213b273675f50607d0bc8f9080..ca0672a77d7b85c25ce826dcfa28d2262508a682 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IDAOFactory.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IDAOFactory.java
@@ -91,5 +91,8 @@ public interface IDAOFactory extends IAuthorizationDAOFactory
 
     /** Returns an implementation of {@link ICodeSequenceDAO} */
     public ICodeSequenceDAO getCodeSequenceDAO();
+    
+    /** Returns an implementation of {@link IDataStoreDAO}. */
+    public IDataStoreDAO getDataStoreDAO();
 
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IDataStoreDAO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IDataStoreDAO.java
new file mode 100644
index 0000000000000000000000000000000000000000..8463e7abf8274289e0738416591c05259d249e46
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IDataStoreDAO.java
@@ -0,0 +1,37 @@
+/*
+ * 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.server.dataaccess;
+
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataStorePE;
+
+/**
+ * <i>Data Access Object</i> for {@link DataStorePE}.
+ *
+ * @author Franz-Josef Elmer
+ */
+public interface IDataStoreDAO
+{
+    /**
+     * Creates or updates specified data store.
+     */
+    public void createOrUpdateDataStore(DataStorePE dataStore);
+    
+    /**
+     * Tries to returns specified data store or <code>null</code> if not found.
+     */
+    public DataStorePE tryToFindDataStoreByCode(String dataStoreCode);
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/DAOFactory.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/DAOFactory.java
index 459058f29b8f6e8b096c5a2ec9cbdd2d21ef2464..73d4c8b3bbf6cfe448351caaee45cd7a2b98a79b 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/DAOFactory.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/DAOFactory.java
@@ -24,6 +24,7 @@ import org.hibernate.SessionFactory;
 import ch.systemsx.cisd.dbmigration.DatabaseConfigurationContext;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDataSetTypeDAO;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDataStoreDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IEntityPropertyTypeDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IEntityTypeDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IAttachmentDAO;
@@ -79,6 +80,8 @@ public final class DAOFactory extends AuthorizationDAOFactory implements IDAOFac
     private final IMaterialDAO materialDAO;
 
     private final ICodeSequenceDAO codeSequenceDAO;
+    
+    private final IDataStoreDAO dataStoreDAO;
 
     public DAOFactory(final DatabaseConfigurationContext context,
             final SessionFactory sessionFactory)
@@ -98,6 +101,7 @@ public final class DAOFactory extends AuthorizationDAOFactory implements IDAOFac
         locatorTypeDAO = new LocatorTypeDAO(sessionFactory, databaseInstance);
         materialDAO = new MaterialDAO(sessionFactory, databaseInstance);
         codeSequenceDAO = new CodeSequenceDAO(sessionFactory, databaseInstance);
+        dataStoreDAO = new DataStoreDAO(sessionFactory, databaseInstance);
         final EntityKind[] entityKinds = EntityKind.values();
         for (final EntityKind entityKind : entityKinds)
         {
@@ -187,4 +191,9 @@ public final class DAOFactory extends AuthorizationDAOFactory implements IDAOFac
     {
         return codeSequenceDAO;
     }
+
+    public IDataStoreDAO getDataStoreDAO()
+    {
+        return dataStoreDAO;
+    }
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/DataStoreDAO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/DataStoreDAO.java
new file mode 100644
index 0000000000000000000000000000000000000000..c868b2c5a9a013664a9c44a7008a831b660d4ca5
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/DataStoreDAO.java
@@ -0,0 +1,69 @@
+/*
+ * 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.server.dataaccess.db;
+
+import org.apache.log4j.Logger;
+import org.hibernate.Criteria;
+import org.hibernate.SessionFactory;
+import org.hibernate.criterion.Restrictions;
+import org.springframework.orm.hibernate3.HibernateTemplate;
+
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogFactory;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDataStoreDAO;
+import ch.systemsx.cisd.openbis.generic.shared.dto.CodeConverter;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataStorePE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE;
+
+/**
+ * Hibernate-based implementation of {@link IDataStoreDAO}.
+ *
+ * @author Franz-Josef Elmer
+ */
+public class DataStoreDAO extends AbstractDAO implements IDataStoreDAO
+{
+    private static final Logger operationLog =
+        LogFactory.getLogger(LogCategory.OPERATION, DataStoreDAO.class);
+
+    public DataStoreDAO(SessionFactory sessionFactory, DatabaseInstancePE databaseInstance)
+    {
+        super(sessionFactory, databaseInstance);
+    }
+
+    public void createOrUpdateDataStore(DataStorePE dataStore)
+    {
+        assert dataStore != null : "Unspecified data store";
+        
+        HibernateTemplate template = getHibernateTemplate();
+        template.saveOrUpdate(dataStore);
+        template.flush();
+        if (operationLog.isInfoEnabled())
+        {
+            operationLog.info(String.format("SAVE/UPDATE: data store '%s'.", dataStore));
+        }
+    }
+
+    public DataStorePE tryToFindDataStoreByCode(String dataStoreCode)
+    {
+        assert dataStoreCode != null : "Unspecified data store code.";
+        
+        final Criteria criteria = getSession().createCriteria(DataStorePE.class);
+        criteria.add(Restrictions.eq("code", CodeConverter.tryToDatabase(dataStoreCode)));
+        return (DataStorePE) criteria.uniqueResult();
+    }
+
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/plugin/IDataSetTypeSlaveServerPlugin.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/plugin/IDataSetTypeSlaveServerPlugin.java
index 7f8bf05d36f1ebecd73c4966d1830c9da712af15..491aef5de97d9a6bb4d0e7888ab94b029fbab711 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/plugin/IDataSetTypeSlaveServerPlugin.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/plugin/IDataSetTypeSlaveServerPlugin.java
@@ -18,7 +18,6 @@ package ch.systemsx.cisd.openbis.generic.server.plugin;
 
 import java.util.List;
 
-import ch.systemsx.cisd.openbis.generic.server.business.DataStoreServerSessionManager;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.db.DAOFactory;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
@@ -36,10 +35,8 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
 public interface IDataSetTypeSlaveServerPlugin
 {
     /**
-     * Deletes the specified data sets for the specified reason on all Data Store Servers 
-     * managed by the specifed DSS manager.
+     * Deletes the specified data sets for the specified reason.
      */
-    public void deleteDataSets(Session session, DataStoreServerSessionManager dssSessionManager,
-            List<ExternalDataPE> dataSets, String reason);
+    public void deleteDataSets(Session session, List<ExternalDataPE> dataSets, String reason);
 
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java
index 168f9c59fcceaf557541d62bdde55264cc9c72fa..b71514b52150fdc2c85a8791b4bf7d5f637ba6c5 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java
@@ -16,8 +16,6 @@
 
 package ch.systemsx.cisd.openbis.generic.shared;
 
-import javax.servlet.http.HttpServletRequest;
-
 import org.springframework.transaction.annotation.Transactional;
 
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
@@ -26,6 +24,7 @@ import ch.systemsx.cisd.openbis.generic.shared.authorization.annotation.Authoriz
 import ch.systemsx.cisd.openbis.generic.shared.authorization.annotation.RoleSet;
 import ch.systemsx.cisd.openbis.generic.shared.authorization.annotation.RolesAllowed;
 import ch.systemsx.cisd.openbis.generic.shared.authorization.predicate.SampleOwnerIdentifierPredicate;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServerInfo;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalData;
@@ -49,13 +48,11 @@ public interface IETLLIMSService extends IWebService, ISessionProvider
     public DatabaseInstancePE getHomeDatabaseInstance(final String sessionToken);
     
     /**
-     * Registers a Data Store Server for the specified port and DSS session token. 
-     * It should be reachable via this port and the DSS session token should be used when invoking
-     * methods. Note, that the host can be inferred by the asking {@link HttpServletRequest}.
+     * Registers a Data Store Server for the specified info. 
      */
     @Transactional
     @RolesAllowed(RoleSet.ETL_SERVER)
-    public void registerDataStoreServer(String sessionToken, int port, String dssSessionToken);
+    public void registerDataStoreServer(String sessionToken, DataStoreServerInfo dataStoreServerInfo);
 
     /**
      * Gets an {@link ExperimentPE} object specified by experiment ID and sample code.
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataStorePE.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataStorePE.java
index 83b53229095d10aa0b1937ed0d68050413590c8f..53008088c819ce7238483e001b1a48d7847f65e9 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataStorePE.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataStorePE.java
@@ -20,11 +20,15 @@ import java.util.Date;
 
 import javax.persistence.Column;
 import javax.persistence.Entity;
+import javax.persistence.FetchType;
 import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
 import javax.persistence.SequenceGenerator;
 import javax.persistence.Table;
+import javax.persistence.Version;
 
 import org.hibernate.annotations.Generated;
 import org.hibernate.annotations.GenerationTime;
@@ -61,6 +65,10 @@ public final class DataStorePE extends AbstractIdAndCodeHolder<DataStorePE>
     
     private String sessionToken;
 
+    private DatabaseInstancePE databaseInstance;
+
+    private Date modificationDate;
+
     public final void setId(final Long id)
     {
         this.id = id;
@@ -119,6 +127,31 @@ public final class DataStorePE extends AbstractIdAndCodeHolder<DataStorePE>
         this.registrationDate = registrationDate;
     }
 
+    @Version
+    @Column(name = ColumnNames.MODIFICATION_TIMESTAMP_COLUMN, nullable = false)
+    public Date getModificationDate()
+    {
+        return modificationDate;
+    }
+
+    public void setModificationDate(Date versionDate)
+    {
+        this.modificationDate = versionDate;
+    }
+
+    @ManyToOne(fetch = FetchType.EAGER)
+    @NotNull(message = ValidationMessages.DATABASE_INSTANCE_NOT_NULL_MESSAGE)
+    @JoinColumn(name = ColumnNames.DATABASE_INSTANCE_COLUMN, updatable = false)
+    public DatabaseInstancePE getDatabaseInstance()
+    {
+        return databaseInstance;
+    }
+
+    public void setDatabaseInstance(final DatabaseInstancePE databaseInstance)
+    {
+        this.databaseInstance = databaseInstance;
+    }
+
     //
     // AbstractIdAndCodeHolder
     //
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataStoreServerInfo.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataStoreServerInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..1dd0584d7e0ea0203903ab5a57f37f65afffd9d8
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataStoreServerInfo.java
@@ -0,0 +1,92 @@
+/*
+ * 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.shared.dto;
+
+import java.io.Serializable;
+
+import javax.servlet.http.HttpServletRequest;
+
+import ch.systemsx.cisd.openbis.generic.shared.GenericSharedConstants;
+
+/**
+ * Information about a data store server (DSS) needed by openBIS server.
+ * <p>
+ * It contains
+ * <ul>
+ * <li>the port on which the DSS is reachable. Note, that the host can be inferred by the asking
+ * {@link HttpServletRequest}.
+ * <li>the DSS session token which has to used when invoking methods on the DSS.
+ * <li>the download URL which is the URL at which the DSS Web server can be accessed from a Web
+ * browser.
+ * <li>the unique code of the DSS.
+ * </ul>
+ * 
+ * @author Franz-Josef Elmer
+ */
+public class DataStoreServerInfo implements Serializable
+{
+    private static final long serialVersionUID = GenericSharedConstants.VERSION;
+
+    private int port;
+    
+    private String dataStoreCode;
+    
+    private String downloadUrl;
+    
+    private String sessionToken;
+
+    public final int getPort()
+    {
+        return port;
+    }
+
+    public final void setPort(int port)
+    {
+        this.port = port;
+    }
+
+    public final String getDataStoreCode()
+    {
+        return dataStoreCode;
+    }
+
+    public final void setDataStoreCode(String dataStoreCode)
+    {
+        this.dataStoreCode = dataStoreCode;
+    }
+
+    public final String getDownloadUrl()
+    {
+        return downloadUrl;
+    }
+
+    public final void setDownloadUrl(String downloadUrl)
+    {
+        this.downloadUrl = downloadUrl;
+    }
+
+    public final String getSessionToken()
+    {
+        return sessionToken;
+    }
+
+    public final void setSessionToken(String sessionToken)
+    {
+        this.sessionToken = sessionToken;
+    }
+    
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataStoreServerSession.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataStoreServerSession.java
deleted file mode 100644
index a1be4426d5a649153089540d7a6d677d84b5f32c..0000000000000000000000000000000000000000
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataStoreServerSession.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.shared.dto;
-
-import java.io.Serializable;
-
-import ch.systemsx.cisd.openbis.generic.shared.IDataStoreService;
-
-/**
- * Class for session on a Data Store Server.
- *
- * @author Franz-Josef Elmer
- */
-public class DataStoreServerSession implements Serializable
-{
-    private static final long serialVersionUID = 1L;
-    
-    private final String dataStoreServerURL;
-    private final IDataStoreService service;
-    
-    private String sessionToken;
-
-    public DataStoreServerSession(String dataStoreServerURL, IDataStoreService service)
-    {
-        this.dataStoreServerURL = dataStoreServerURL;
-        this.service = service;
-    }
-
-    public final String getDataStoreServerURL()
-    {
-        return dataStoreServerURL;
-    }
-
-    public final String getSessionToken()
-    {
-        return sessionToken;
-    }
-
-    public final void setSessionToken(String sessionToken)
-    {
-        this.sessionToken = sessionToken;
-    }
-
-    public final IDataStoreService getService()
-    {
-        return service;
-    }
-    
-}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExternalData.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExternalData.java
index 780f052840b2d258c4ae1a873037bb56c23454c8..9f3c426555e3693dbb9f5436998991b74666f84c 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExternalData.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExternalData.java
@@ -50,6 +50,8 @@ public final class ExternalData extends ExtractableData
 
     private String associatedSampleCode;
     
+    private String dataStoreCode;
+    
     private boolean measured;
 
     /** Returns <code>dataSetType</code>. */
@@ -159,6 +161,16 @@ public final class ExternalData extends ExtractableData
         this.associatedSampleCode = sampleCode;
     }
 
+    public final String getDataStoreCode()
+    {
+        return dataStoreCode;
+    }
+
+    public final void setDataStoreCode(String dataStoreCode)
+    {
+        this.dataStoreCode = dataStoreCode;
+    }
+
     public final boolean isMeasured()
     {
         return measured;
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericDataSetTypeSlaveServerPlugin.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericDataSetTypeSlaveServerPlugin.java
index b1d5a8267f2499d9d66f198fe6506ff8c82bb396..04e948808a44428a3803956af2c67b1609e76a4e 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericDataSetTypeSlaveServerPlugin.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericDataSetTypeSlaveServerPlugin.java
@@ -23,7 +23,6 @@ import javax.annotation.Resource;
 import org.springframework.stereotype.Component;
 
 import ch.rinn.restrictions.Private;
-import ch.systemsx.cisd.openbis.generic.server.business.DataStoreServerSessionManager;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IExternalDataTable;
 import ch.systemsx.cisd.openbis.generic.server.plugin.IDataSetTypeSlaveServerPlugin;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE;
@@ -47,12 +46,11 @@ public class GenericDataSetTypeSlaveServerPlugin implements IDataSetTypeSlaveSer
     {
     }
     
-    public void deleteDataSets(Session session, DataStoreServerSessionManager dssSessionManager,
-            List<ExternalDataPE> dataSets, String reason)
+    public void deleteDataSets(Session session, List<ExternalDataPE> dataSets, String reason)
     {
         IExternalDataTable externalDataTable = businessObjectFactory.createExternalDataTable(session);
         externalDataTable.setExternalData(dataSets);
-        externalDataTable.deleteLoadedDataSets(dssSessionManager, reason);
+        externalDataTable.deleteLoadedDataSets(reason);
     }
     
 
diff --git a/openbis/source/java/genericApplicationContext.xml b/openbis/source/java/genericApplicationContext.xml
index 7f12f487957afe1c68772f1f2e53bf68ac56ba49..e11302dc5bdb9c5a3245c8ee59bc33741db97b74 100644
--- a/openbis/source/java/genericApplicationContext.xml
+++ b/openbis/source/java/genericApplicationContext.xml
@@ -36,6 +36,8 @@
         <constructor-arg ref="db-configuration-context" />
         <constructor-arg ref="hibernate-session-factory" />
     </bean>
+    
+    <bean id="dss-factory" class="ch.systemsx.cisd.openbis.generic.server.DataStoreServiceFactory"/>
 
     <bean id="authentication-service"
         class="ch.systemsx.cisd.openbis.generic.server.AuthenticationServiceHolder">
@@ -62,6 +64,7 @@
     <bean id="common-business-object-factory"
         class="ch.systemsx.cisd.openbis.generic.server.CommonBusinessObjectFactory">
         <constructor-arg ref="dao-factory" />
+        <constructor-arg ref="dss-factory" />
     </bean>
 
     <bean id="last-modification-state"
@@ -74,7 +77,6 @@
     <bean id="common-server" class="ch.systemsx.cisd.openbis.generic.server.CommonServer">
         <constructor-arg ref="authentication-service" />
         <constructor-arg ref="session-manager" />
-        <constructor-arg ref="dss-session-manager" />
         <constructor-arg ref="dao-factory" />
         <constructor-arg ref="common-business-object-factory" />
         <constructor-arg ref="last-modification-state" />
@@ -100,9 +102,9 @@
         <property name="target">
             <bean class="ch.systemsx.cisd.openbis.generic.server.ETLService">
                 <constructor-arg ref="session-manager" />
-                <constructor-arg ref="dss-session-manager" />
                 <constructor-arg ref="dao-factory" />
                 <constructor-arg ref="common-business-object-factory" />
+                <constructor-arg ref="dss-factory" />
             </bean>
         </property>
         <property name="interceptorNames">
@@ -113,8 +115,6 @@
         </property>
     </bean>
     
-    <bean id="dss-session-manager" class="ch.systemsx.cisd.openbis.generic.server.business.DataStoreServerSessionManager"/>
-
     <bean id="exception-translator"
         class="ch.systemsx.cisd.common.spring.ServiceExceptionTranslator">
         <property name="packagesNotMasqueraded">
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/CommonServerTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/CommonServerTest.java
index d3125f8aa33b3fd5cf4db8f42646f64cc48f3d04..f01e822c63c7c3c32f11cb2fd474d977aadce478 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/CommonServerTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/CommonServerTest.java
@@ -27,7 +27,6 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
-import ch.systemsx.cisd.openbis.generic.server.business.DataStoreServerSessionManager;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.ICommonBusinessObjectFactory;
 import ch.systemsx.cisd.openbis.generic.server.plugin.IDataSetTypeSlaveServerPlugin;
 import ch.systemsx.cisd.openbis.generic.server.plugin.ISampleTypeSlaveServerPlugin;
@@ -91,15 +90,13 @@ public final class CommonServerTest extends AbstractServerTestCase
 
     private ICommonBusinessObjectFactory commonBusinessObjectFactory;
 
-    private DataStoreServerSessionManager dssSessionManager;
-
     private ISampleTypeSlaveServerPlugin sampleTypeSlaveServerPlugin;
 
     private IDataSetTypeSlaveServerPlugin dataSetTypeSlaveServerPlugin;
 
     private final ICommonServer createServer()
     {
-        CommonServer server = new CommonServer(authenticationService, sessionManager, dssSessionManager,
+        CommonServer server = new CommonServer(authenticationService, sessionManager, 
                 daoFactory, commonBusinessObjectFactory, new LastModificationState());
         server.setSampleTypeSlaveServerPlugin(sampleTypeSlaveServerPlugin);
         server.setDataSetTypeSlaveServerPlugin(dataSetTypeSlaveServerPlugin);
@@ -122,7 +119,6 @@ public final class CommonServerTest extends AbstractServerTestCase
     public final void setUp()
     {
         super.setUp();
-        dssSessionManager = new DataStoreServerSessionManager();
         commonBusinessObjectFactory = context.mock(ICommonBusinessObjectFactory.class);
         sampleTypeSlaveServerPlugin = context.mock(ISampleTypeSlaveServerPlugin.class);
         dataSetTypeSlaveServerPlugin = context.mock(IDataSetTypeSlaveServerPlugin.class);
@@ -871,10 +867,10 @@ public final class CommonServerTest extends AbstractServerTestCase
                     ExternalDataPE ds3 = createDataSet("ds3", "type2");
                     will(returnValue(Arrays.asList(ds1, ds2, ds3)));
                     
-                    one(dataSetTypeSlaveServerPlugin).deleteDataSets(SESSION, dssSessionManager,
-                            Arrays.asList(ds1, ds2), "reason");
-                    one(dataSetTypeSlaveServerPlugin).deleteDataSets(SESSION, dssSessionManager,
-                            Arrays.asList(ds3), "reason");
+                    one(dataSetTypeSlaveServerPlugin).deleteDataSets(SESSION, Arrays.asList(ds1, ds2),
+                            "reason");
+                    one(dataSetTypeSlaveServerPlugin).deleteDataSets(SESSION, Arrays.asList(ds3),
+                            "reason");
                 }
             });
 
@@ -907,8 +903,7 @@ public final class CommonServerTest extends AbstractServerTestCase
                     will(returnValue(externalDataTable));
 
                     one(externalDataTable).loadByDataSetCodes(dataSetCodes);
-                    one(externalDataTable).uploadLoadedDataSetsToCIFEX(dssSessionManager,
-                            uploadContext);
+                    one(externalDataTable).uploadLoadedDataSetsToCIFEX(uploadContext);
                 }
             });
 
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceTest.java
index 95a178b81587150ed1fcdb99b6129703c06e8eab..6e38458396d5d959f9dbb92755dffdac29ac4a8f 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceTest.java
@@ -22,6 +22,8 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.LinkedHashSet;
 
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
 import org.jmock.Expectations;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -29,14 +31,15 @@ import org.testng.annotations.Test;
 import ch.rinn.restrictions.Friend;
 import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
-import ch.systemsx.cisd.openbis.generic.server.business.DataStoreServerSessionManager;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.ICommonBusinessObjectFactory;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDataStoreDAO;
 import ch.systemsx.cisd.openbis.generic.shared.AbstractServerTestCase;
 import ch.systemsx.cisd.openbis.generic.shared.IDataStoreService;
 import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService;
 import ch.systemsx.cisd.openbis.generic.shared.dto.AttachmentContentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.AttachmentPE;
-import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServerSession;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataStorePE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServerInfo;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE;
@@ -56,67 +59,139 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
 @Friend(toClasses=ETLService.class)
 public class ETLServiceTest extends AbstractServerTestCase
 {
+    private static final String DOWNLOAD_URL = "download-url";
+    private static final String DSS_CODE = "my-dss";
+    private static final String DSS_SESSION_TOKEN = "dss42";
+    private static final int PORT = 443;
+    private static final String URL = "https://" + SESSION.getRemoteHost() + ":" + PORT;
     private ICommonBusinessObjectFactory boFactory;
-    private DataStoreServerSessionManager dssSessionManager;
     private IDataStoreServiceFactory dssfactory;
     private IDataStoreService dataStoreService;
+    private IDataStoreDAO dataStoreDAO;
     
     @Override
     @BeforeMethod
     public final void setUp()
     {
         super.setUp();
-        dssSessionManager = new DataStoreServerSessionManager();
         boFactory = context.mock(ICommonBusinessObjectFactory.class);
         dssfactory = context.mock(IDataStoreServiceFactory.class);
         dataStoreService = context.mock(IDataStoreService.class);
+        dataStoreDAO = context.mock(IDataStoreDAO.class);
     }
     
     @Test
     public void testRegisterDataStoreServer()
     {
-        final String url = "https://" + SESSION.getRemoteHost() + ":443";
-        final String dssToken = "dss42";
         prepareGetSession();
         context.checking(new Expectations()
             {
                 {
-                    one(dssfactory).create(url);
+                    one(daoFactory).getDataStoreDAO();
+                    will(returnValue(dataStoreDAO));
+                    
+                    one(dataStoreDAO).tryToFindDataStoreByCode(DSS_CODE);
+                    will(returnValue(null));
+                    
+                    one(dssfactory).create(URL);
                     will(returnValue(dataStoreService));
                     
-                    one(dataStoreService).getVersion(dssToken);
+                    one(dataStoreService).getVersion(DSS_SESSION_TOKEN);
                     will(returnValue(IDataStoreService.VERSION));
+                    
+                    one(dataStoreDAO).createOrUpdateDataStore(with(new BaseMatcher<DataStorePE>()
+                        {
+                            public void describeTo(Description description)
+                            {
+                            }
+                    
+                            public boolean matches(Object item)
+                            {
+                                if (item instanceof DataStorePE)
+                                {
+                                    DataStorePE store = (DataStorePE) item;
+                                    return DSS_CODE.equals(store.getCode())
+                                            && URL.equals(store.getRemoteUrl())
+                                            && DOWNLOAD_URL.equals(store.getDownloadUrl())
+                                            && DSS_SESSION_TOKEN.equals(store.getSessionToken());
+                                }
+                                return false;
+                            }
+                    
+                        }));
                 }
             });
         
-        createService().registerDataStoreServer(SESSION_TOKEN, 443, dssToken);
-        
-        DataStoreServerSession session = dssSessionManager.tryToGetSession(url);
-        assertEquals(dssToken, session.getSessionToken());
+        createService().registerDataStoreServer(SESSION_TOKEN, createDSSInfo());
         
         context.assertIsSatisfied();
     }
     
+    @Test
+    public void testRegisterDataStoreServerAgain()
+    {
+        prepareGetSession();
+        context.checking(new Expectations()
+            {
+                {
+                    one(daoFactory).getDataStoreDAO();
+                    will(returnValue(dataStoreDAO));
+
+                    one(dataStoreDAO).tryToFindDataStoreByCode(DSS_CODE);
+                    will(returnValue(new DataStorePE()));
+
+                    one(dataStoreDAO).createOrUpdateDataStore(with(new BaseMatcher<DataStorePE>()
+                        {
+                            public void describeTo(Description description)
+                            {
+                            }
+
+                            public boolean matches(Object item)
+                            {
+                                if (item instanceof DataStorePE)
+                                {
+                                    DataStorePE store = (DataStorePE) item;
+                                    return DSS_CODE.equals(store.getCode())
+                                            && URL.equals(store.getRemoteUrl())
+                                            && DOWNLOAD_URL.equals(store.getDownloadUrl())
+                                            && DSS_SESSION_TOKEN.equals(store.getSessionToken());
+                                }
+                                return false;
+                            }
+
+                        }));
+                }
+            });
+
+        createService().registerDataStoreServer(SESSION_TOKEN, createDSSInfo());
+
+        context.assertIsSatisfied();
+    }
+
     @Test
     public void testRegisterDataStoreServerWithWrongVersion()
     {
-        final String url = "https://" + SESSION.getRemoteHost() + ":443";
-        final String dssToken = "dss42";
         prepareGetSession();
         context.checking(new Expectations()
             {
                 {
-                    one(dssfactory).create(url);
+                    one(daoFactory).getDataStoreDAO();
+                    will(returnValue(dataStoreDAO));
+                    
+                    one(dataStoreDAO).tryToFindDataStoreByCode(DSS_CODE);
+                    will(returnValue(null));
+                    
+                    one(dssfactory).create(URL);
                     will(returnValue(dataStoreService));
 
-                    one(dataStoreService).getVersion(dssToken);
+                    one(dataStoreService).getVersion(DSS_SESSION_TOKEN);
                     will(returnValue(VERSION + 1));
                 }
             });
 
         try
         {
-            createService().registerDataStoreServer(SESSION_TOKEN, 443, dssToken);
+            createService().registerDataStoreServer(SESSION_TOKEN, createDSSInfo());
             fail("ConfigurationFailureException expected");
         } catch (ConfigurationFailureException e)
         {
@@ -462,6 +537,16 @@ public class ETLServiceTest extends AbstractServerTestCase
     
     private IETLLIMSService createService()
     {
-        return new ETLService(sessionManager, dssSessionManager, daoFactory, boFactory, dssfactory);
+        return new ETLService(sessionManager, daoFactory, boFactory, dssfactory);
+    }
+    
+    private DataStoreServerInfo createDSSInfo()
+    {
+        DataStoreServerInfo info = new DataStoreServerInfo();
+        info.setPort(PORT);
+        info.setSessionToken(DSS_SESSION_TOKEN);
+        info.setDataStoreCode(DSS_CODE);
+        info.setDownloadUrl(DOWNLOAD_URL);
+        return info;
     }
 }
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBOTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBOTest.java
index 51627fb72f44e2fe51b756317b9d2a8a88586d61..2ebbc10779c89b6b9e825ef66b4bb6a66aeaf7fa 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBOTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBOTest.java
@@ -24,6 +24,7 @@ import org.testng.annotations.BeforeMethod;
 
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDataSetTypeDAO;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDataStoreDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDatabaseInstanceDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IEntityPropertyTypeDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IEntityTypeDAO;
@@ -85,6 +86,8 @@ public abstract class AbstractBOTest extends AssertJUnit
 
     ILocatorTypeDAO locatorTypeDAO;
 
+    IDataStoreDAO dataStoreDAO;
+
     @BeforeMethod
     public void beforeMethod()
     {
@@ -107,6 +110,7 @@ public abstract class AbstractBOTest extends AssertJUnit
         dataSetTypeDAO = context.mock(IDataSetTypeDAO.class);
         fileFormatTypeDAO = context.mock(IFileFormatTypeDAO.class);
         locatorTypeDAO = context.mock(ILocatorTypeDAO.class);
+        dataStoreDAO = context.mock(IDataStoreDAO.class);
         context.checking(new Expectations()
             {
                 {
@@ -128,6 +132,8 @@ public abstract class AbstractBOTest extends AssertJUnit
                     will(returnValue(locatorTypeDAO));
                     allowing(daoFactory).getExternalDataDAO();
                     will(returnValue(externalDataDAO));
+                    allowing(daoFactory).getDataStoreDAO();
+                    will(returnValue(dataStoreDAO));
                 }
             });
     }
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBOTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBOTest.java
index 397941b759a6999da324ae14a8db0e252513dd73..af6f67b256de59112c097ad8368a087189c17391 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBOTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBOTest.java
@@ -33,6 +33,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetPropertyPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetType;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetTypePE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataStorePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalData;
@@ -56,6 +57,8 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.types.ProcedureTypeCode;
  */
 public class ExternalDataBOTest extends AbstractBOTest
 {
+    private static final String DATA_STORE_CODE = "dss1";
+
     private static final String PARENT_CODE = "parent";
 
     private static final Date PRODUCTION_DATE = new Date(1001);
@@ -104,7 +107,7 @@ public class ExternalDataBOTest extends AbstractBOTest
         SamplePE sample = new SamplePE();
         ExperimentPE experimentPE = new ExperimentPE();
         sample.setExperiment(experimentPE);
-        prepareDefine(dataSetType, fileFormatType, vocabulary, locatorType);
+        prepareDefine(dataSetType, fileFormatType, vocabulary, locatorType, new DataStorePE());
 
         IExternalDataBO sampleBO = createExternalDataBO();
         sampleBO.define(createData(null), sample, SourceType.DERIVED);
@@ -141,7 +144,8 @@ public class ExternalDataBOTest extends AbstractBOTest
         SamplePE sample = new SamplePE();
         ExperimentPE experimentPE = new ExperimentPE();
         sample.setExperiment(experimentPE);
-        prepareDefine(dataSetType, fileFormatType, vocabulary, locatorType);
+        DataStorePE dataStore = new DataStorePE();
+        prepareDefine(dataSetType, fileFormatType, vocabulary, locatorType, dataStore);
         final DataPE data = new DataPE();
         context.checking(new Expectations()
             {
@@ -157,6 +161,7 @@ public class ExternalDataBOTest extends AbstractBOTest
 
         assertSame(sample, externalData.getSampleAcquiredFrom());
         assertSame(null, externalData.getSampleDerivedFrom());
+        assertSame(dataStore, externalData.getDataStore());
         assertEquals(1, externalData.getParents().size());
         assertSame(data, externalData.getParents().iterator().next());
         context.assertIsSatisfied();
@@ -174,7 +179,8 @@ public class ExternalDataBOTest extends AbstractBOTest
         vocabulary.addTerm(vocabularyTerm);
         final LocatorTypePE locatorType = new LocatorTypePE();
         final SamplePE sample = new SamplePE();
-        prepareDefine(dataSetType, fileFormatType, vocabulary, locatorType);
+        final DataStorePE dataStore = new DataStorePE();
+        prepareDefine(dataSetType, fileFormatType, vocabulary, locatorType, dataStore);
         final DataSetTypePE dataSetTypeUnknown = new DataSetTypePE();
         final DataPE parentData = new DataPE();
         parentData.setCode(PARENT_CODE);
@@ -202,6 +208,7 @@ public class ExternalDataBOTest extends AbstractBOTest
 
         assertSame(sample, externalData.getSampleAcquiredFrom());
         assertSame(null, externalData.getSampleDerivedFrom());
+        assertSame(dataStore, externalData.getDataStore());
         assertEquals(1, externalData.getParents().size());
         assertEquals(parentData, externalData.getParents().iterator().next());
         context.assertIsSatisfied();
@@ -219,7 +226,8 @@ public class ExternalDataBOTest extends AbstractBOTest
         vocabulary.addTerm(vocabularyTerm);
         final LocatorTypePE locatorType = new LocatorTypePE();
         final SamplePE data = new SamplePE();
-        prepareDefine(dataSetType, fileFormatType, vocabulary, locatorType);
+        final DataStorePE dataStore = new DataStorePE();
+        prepareDefine(dataSetType, fileFormatType, vocabulary, locatorType, dataStore);
         final DataSetTypePE dataSetTypeUnknown = new DataSetTypePE();
         final DataPE parentData = new DataPE();
         parentData.setCode(PARENT_CODE);
@@ -236,7 +244,7 @@ public class ExternalDataBOTest extends AbstractBOTest
                     one(dataSetTypeDAO).tryToFindDataSetTypeByCode(
                             DataSetTypeCode.UNKNOWN.getCode());
                     will(returnValue(dataSetTypeUnknown));
-
+                    
                     one(externalDataDAO).createDataSet(parentData);
                 }
             });
@@ -247,6 +255,7 @@ public class ExternalDataBOTest extends AbstractBOTest
 
         assertSame(data, externalData.getSampleAcquiredFrom());
         assertSame(null, externalData.getSampleDerivedFrom());
+        assertSame(dataStore, externalData.getDataStore());
         assertEquals(1, externalData.getParents().size());
         assertEquals(parentData, externalData.getParents().iterator().next());
         context.assertIsSatisfied();
@@ -265,7 +274,8 @@ public class ExternalDataBOTest extends AbstractBOTest
         final LocatorTypePE locatorType = new LocatorTypePE();
         SamplePE sample = new SamplePE();
         sample.setExperiment(new ExperimentPE());
-        prepareDefine(dataSetType, fileFormatType, vocabulary, locatorType);
+        final DataStorePE dataStore = new DataStorePE();
+        prepareDefine(dataSetType, fileFormatType, vocabulary, locatorType, dataStore);
         context.checking(new Expectations()
             {
                 {
@@ -298,7 +308,8 @@ public class ExternalDataBOTest extends AbstractBOTest
         final LocatorTypePE locatorType = new LocatorTypePE();
         SamplePE sample = new SamplePE();
         sample.setExperiment(new ExperimentPE());
-        prepareDefine(dataSetType, fileFormatType, vocabulary, locatorType);
+        final DataStorePE dataStore = new DataStorePE();
+        prepareDefine(dataSetType, fileFormatType, vocabulary, locatorType, dataStore);
         context.checking(new Expectations()
             {
                 {
@@ -320,6 +331,7 @@ public class ExternalDataBOTest extends AbstractBOTest
         sampleBO.save();
 
         ExternalDataPE externalData = sampleBO.getExternalData();
+        assertSame(dataStore, externalData.getDataStore());
         assertEquals(false, externalData.isPlaceholder());
         assertEquals(4711, externalData.getId().longValue());
         assertEquals(null, externalData.getSampleDerivedFrom());
@@ -359,7 +371,7 @@ public class ExternalDataBOTest extends AbstractBOTest
 
     private void prepareDefine(final DataSetTypePE dataSetType,
             final FileFormatTypePE fileFormatType, final VocabularyPE vocabulary,
-            final LocatorTypePE locatorType)
+            final LocatorTypePE locatorType, final DataStorePE dataStore)
     {
         context.checking(new Expectations()
             {
@@ -378,6 +390,9 @@ public class ExternalDataBOTest extends AbstractBOTest
                             dataSetType.getCode(), EXAMPLE_SESSION.tryGetPerson());
                     ArrayList<DataSetPropertyPE> properties = new ArrayList<DataSetPropertyPE>();
                     will(returnValue(properties));
+                    
+                    one(dataStoreDAO).tryToFindDataStoreByCode(DATA_STORE_CODE);
+                    will(returnValue(dataStore));
                 }
             });
     }
@@ -395,6 +410,7 @@ public class ExternalDataBOTest extends AbstractBOTest
         data.setFileFormatType(FILE_FORMAT_TYPE);
         data.setLocatorType(LOCATOR_TYPE);
         data.setLocation(LOCATION);
+        data.setDataStoreCode(DATA_STORE_CODE);
         return data;
     }
 
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataTableTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataTableTest.java
index 2a8079a03da72e5a30bfa20130be0670626afb0f..b0a4bb9b3f900c2342ce5a303593f95c7fa42fdd 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataTableTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataTableTest.java
@@ -30,13 +30,13 @@ import org.testng.annotations.Test;
 
 import ch.rinn.restrictions.Friend;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
-import ch.systemsx.cisd.openbis.generic.server.business.DataStoreServerSessionManager;
+import ch.systemsx.cisd.openbis.generic.server.IDataStoreServiceFactory;
 import ch.systemsx.cisd.openbis.generic.server.business.ManagerTestTool;
 import ch.systemsx.cisd.openbis.generic.shared.IDataStoreService;
 import ch.systemsx.cisd.openbis.generic.shared.basic.BasicConstant;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetUploadContext;
-import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServerSession;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataStorePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE;
@@ -59,12 +59,15 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
 @Friend(toClasses=ExternalDataTable.class)
 public final class ExternalDataTableTest extends AbstractBOTest
 {
-    private DataStoreServerSessionManager dssSessionManager;
-    private IDataStoreService dataStoreService;
+    private IDataStoreServiceFactory dssFactory;
+    private DataStorePE dss1;
+    private DataStorePE dss2;
+    private IDataStoreService dataStoreService1;
+    private IDataStoreService dataStoreService2;
 
     private final ExternalDataTable createExternalDataTable()
     {
-        return new ExternalDataTable(daoFactory, ManagerTestTool.EXAMPLE_SESSION);
+        return new ExternalDataTable(daoFactory, dssFactory, ManagerTestTool.EXAMPLE_SESSION);
     }
 
     @BeforeMethod
@@ -72,8 +75,21 @@ public final class ExternalDataTableTest extends AbstractBOTest
     public void beforeMethod()
     {
         super.beforeMethod();
-        dssSessionManager = new DataStoreServerSessionManager();
-        dataStoreService = context.mock(IDataStoreService.class);
+        dssFactory = context.mock(IDataStoreServiceFactory.class);
+        dss1 = createDataStore("dss1");
+        dss2 = createDataStore("dss2");
+        dataStoreService1 = context.mock(IDataStoreService.class, "dataStoreService1");
+        dataStoreService2 = context.mock(IDataStoreService.class, "dataStoreService2");
+        context.checking(new Expectations()
+            {
+                {
+                    allowing(dssFactory).create(dss1.getRemoteUrl());
+                    will(returnValue(dataStoreService1));
+                    
+                    allowing(dssFactory).create(dss2.getRemoteUrl());
+                    will(returnValue(dataStoreService2));
+                }
+            });
     }
 
     @Test
@@ -215,40 +231,11 @@ public final class ExternalDataTableTest extends AbstractBOTest
         context.assertIsSatisfied();
     }
     
-    @Test
-    public void testDeleteLoadedDataSetsButDataStoreServerIsDown()
-    {
-        final ExternalDataPE d1 = createDataSet("d1");
-        context.checking(new Expectations()
-            {
-                {
-                    one(externalDataDAO).tryToFindFullDataSetByCode(d1.getCode());
-                    will(returnValue(d1));
-                }
-            });
-
-        ExternalDataTable externalDataTable = createExternalDataTable();
-        externalDataTable.loadByDataSetCodes(Arrays.asList(d1.getCode()));
-        try
-        {
-            externalDataTable.deleteLoadedDataSets(dssSessionManager, "");
-            fail("UserFailureException expected");
-        } catch (UserFailureException e)
-        {
-            assertEquals(
-                    "The following data sets are unknown by any registered Data Store Server. "
-                            + "May be the responsible Data Store Server is not running.\n[d1]", e
-                            .getMessage());
-        }
-        
-        context.assertIsSatisfied();
-    }
-    
     @Test
     public void testLoadByDataSetCodes()
     {
-        final ExternalDataPE d1 = createDataSet("d1");
-        final ExternalDataPE d2 = createDataSet("d2");
+        final ExternalDataPE d1 = createDataSet("d1", dss1);
+        final ExternalDataPE d2 = createDataSet("d2", dss2);
         context.checking(new Expectations()
             {
                 {
@@ -272,32 +259,32 @@ public final class ExternalDataTableTest extends AbstractBOTest
     @Test
     public void testDeleteLoadedDataSetsButOneDataSetIsUnknown()
     {
-        dssSessionManager.registerDataStoreServer(new DataStoreServerSession("url",
-                dataStoreService));
-        final ExternalDataPE d1 = createDataSet("d1");
-        final ExternalDataPE d2 = createDataSet("d2");
+        final ExternalDataPE d1 = createDataSet("d1", dss1);
+        final ExternalDataPE d2 = createDataSet("d2", dss2);
         context.checking(new Expectations()
-        {
             {
-                one(externalDataDAO).tryToFindFullDataSetByCode(d1.getCode());
-                will(returnValue(d1));
-                
-                one(externalDataDAO).tryToFindFullDataSetByCode(d2.getCode());
-                will(returnValue(d2));
-                
-                List<String> locations = Arrays.asList(d1.getLocation(), d2.getLocation());
-                one(dataStoreService).getKnownDataSets(with(any(String.class)),
-                        with(equal(locations)));
-                will(returnValue(Arrays.asList(d1.getLocation())));
-                
-            }
-        });
+                {
+                    one(externalDataDAO).tryToFindFullDataSetByCode(d1.getCode());
+                    will(returnValue(d1));
+
+                    one(externalDataDAO).tryToFindFullDataSetByCode(d2.getCode());
+                    will(returnValue(d2));
+
+                    one(dataStoreService1).getKnownDataSets(dss1.getSessionToken(),
+                            Arrays.asList(d1.getLocation()));
+                    will(returnValue(Arrays.asList(d1.getLocation())));
+
+                    one(dataStoreService2).getKnownDataSets(dss2.getSessionToken(),
+                            Arrays.asList(d2.getLocation()));
+                    will(returnValue(Arrays.asList()));
+                }
+            });
         
         ExternalDataTable externalDataTable = createExternalDataTable();
         externalDataTable.loadByDataSetCodes(Arrays.asList(d1.getCode(), d2.getCode()));
         try
         {
-            externalDataTable.deleteLoadedDataSets(dssSessionManager, "");
+            externalDataTable.deleteLoadedDataSets("");
             fail("UserFailureException expected");
         } catch (UserFailureException e)
         {
@@ -313,10 +300,8 @@ public final class ExternalDataTableTest extends AbstractBOTest
     @Test
     public void testDeleteLoadedDataSets()
     {
-        dssSessionManager.registerDataStoreServer(new DataStoreServerSession("url",
-                dataStoreService));
-        final ExternalDataPE d1 = createDataSet("d1");
-        final ExternalDataPE d2 = createDataSet("d2");
+        final ExternalDataPE d1 = createDataSet("d1", dss1);
+        final ExternalDataPE d2 = createDataSet("d2", dss2);
         context.checking(new Expectations()
             {
                 {
@@ -325,23 +310,27 @@ public final class ExternalDataTableTest extends AbstractBOTest
 
                     one(externalDataDAO).tryToFindFullDataSetByCode(d2.getCode());
                     will(returnValue(d2));
+                    
+                    List<String> d1Locations = Arrays.asList(d1.getLocation());
+                    one(dataStoreService1).getKnownDataSets(dss1.getSessionToken(), d1Locations);
+                    will(returnValue(d1Locations));
 
-                    List<String> locations = Arrays.asList(d1.getLocation(), d2.getLocation());
-                    one(dataStoreService).getKnownDataSets(with(any(String.class)),
-                            with(equal(locations)));
-                    will(returnValue(locations));
+                    List<String> d2Locations = Arrays.asList(d2.getLocation());
+                    one(dataStoreService2).getKnownDataSets(dss2.getSessionToken(), d2Locations);
+                    will(returnValue(d2Locations));
 
                     PersonPE person = EXAMPLE_SESSION.tryGetPerson();
                     one(externalDataDAO).markAsDeleted(d1, person, DELETION_DESCRIPTION, "reason");
                     one(externalDataDAO).markAsDeleted(d2, person, DELETION_DESCRIPTION, "reason");
-                    one(dataStoreService).deleteDataSets(with(any(String.class)),
-                            with(equal(locations)));
+                    
+                    one(dataStoreService1).deleteDataSets(dss1.getSessionToken(), d1Locations);
+                    one(dataStoreService2).deleteDataSets(dss2.getSessionToken(), d2Locations);
                 }
             });
 
         ExternalDataTable externalDataTable = createExternalDataTable();
         externalDataTable.loadByDataSetCodes(Arrays.asList(d1.getCode(), d2.getCode()));
-        externalDataTable.deleteLoadedDataSets(dssSessionManager, "reason");
+        externalDataTable.deleteLoadedDataSets("reason");
 
         context.assertIsSatisfied();
     }
@@ -349,10 +338,8 @@ public final class ExternalDataTableTest extends AbstractBOTest
     @Test
     public void testUploadDataSets()
     {
-        dssSessionManager.registerDataStoreServer(new DataStoreServerSession("url",
-                dataStoreService));
-        final ExternalDataPE d1 = createDataSet("d1");
-        final ExternalDataPE d2 = createDataSet("d2");
+        final ExternalDataPE d1 = createDataSet("d1", dss1);
+        final ExternalDataPE d2 = createDataSet("d2", dss2);
         final DataSetUploadContext uploadContext = new DataSetUploadContext();
         uploadContext.setCifexURL("cifexURL");
         uploadContext.setUserID(EXAMPLE_SESSION.getUserName());
@@ -367,20 +354,25 @@ public final class ExternalDataTableTest extends AbstractBOTest
 
                     one(externalDataDAO).tryToFindFullDataSetByCode(d2.getCode());
                     will(returnValue(d2));
-
-                    List<String> locations = Arrays.asList(d1.getLocation(), d2.getLocation());
-                    one(dataStoreService).getKnownDataSets(with(any(String.class)),
-                            with(equal(locations)));
-                    will(returnValue(locations));
-
-                    one(dataStoreService).uploadDataSetsToCIFEX(with(any(String.class)),
-                            with(equal(Arrays.asList(d1, d2))), with(equal(uploadContext)));
+                    
+                    List<String> d1Locations = Arrays.asList(d1.getLocation());
+                    one(dataStoreService1).getKnownDataSets(dss1.getSessionToken(), d1Locations);
+                    will(returnValue(d1Locations));
+
+                    List<String> d2Locations = Arrays.asList(d2.getLocation());
+                    one(dataStoreService2).getKnownDataSets(dss2.getSessionToken(), d2Locations);
+                    will(returnValue(d2Locations));
+
+                    one(dataStoreService1).uploadDataSetsToCIFEX(dss1.getSessionToken(),
+                            Arrays.asList(d1), uploadContext);
+                    one(dataStoreService2).uploadDataSetsToCIFEX(dss2.getSessionToken(),
+                            Arrays.asList(d2), uploadContext);
                 }
             });
 
         ExternalDataTable externalDataTable = createExternalDataTable();
         externalDataTable.loadByDataSetCodes(Arrays.asList(d1.getCode(), d2.getCode()));
-        externalDataTable.uploadLoadedDataSetsToCIFEX(dssSessionManager, uploadContext);
+        externalDataTable.uploadLoadedDataSetsToCIFEX(uploadContext);
 
         context.assertIsSatisfied();
     }
@@ -433,10 +425,11 @@ public final class ExternalDataTableTest extends AbstractBOTest
         return sequence.substring(0, codeLength - result.length()) + result;
     }
     
-    private ExternalDataPE createDataSet(String code)
+    private ExternalDataPE createDataSet(String code, DataStorePE dataStore)
     {
         ExternalDataPE data = new ExternalDataPE();
         data.setCode(code);
+        data.setDataStore(dataStore);
         data.setLocation("here/" + code);
         ExperimentPE experiment = new ExperimentPE();
         experiment.setCode("exp1");
@@ -449,4 +442,13 @@ public final class ExternalDataTableTest extends AbstractBOTest
         data.setExperiment(experiment);
         return data;
     }
+    
+    private DataStorePE createDataStore(String code)
+    {
+        DataStorePE dataStore = new DataStorePE();
+        dataStore.setCode(code);
+        dataStore.setRemoteUrl("http://" + code);
+        dataStore.setSessionToken("session-" + code);
+        return dataStore;
+    }
 }
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java.expected b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java.expected
index 65cfa59590bde5318103fc5f53a1c95b58377681..75d8808a69397cbe60c667188a810435f7884dca 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java.expected
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java.expected
@@ -16,8 +16,6 @@
 
 package ch.systemsx.cisd.openbis.generic.shared;
 
-import javax.servlet.http.HttpServletRequest;
-
 import org.springframework.transaction.annotation.Transactional;
 
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
@@ -26,6 +24,7 @@ import ch.systemsx.cisd.openbis.generic.shared.authorization.annotation.Authoriz
 import ch.systemsx.cisd.openbis.generic.shared.authorization.annotation.RoleSet;
 import ch.systemsx.cisd.openbis.generic.shared.authorization.annotation.RolesAllowed;
 import ch.systemsx.cisd.openbis.generic.shared.authorization.predicate.SampleOwnerIdentifierPredicate;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServerInfo;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalData;
@@ -33,41 +32,85 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePropertyPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
 
+/**
+ * <b>LIMS</b> <i>Web Service</i> interface for the <b>ETL</b> (<i>Extract, Transform, Load</i>)
+ * server.
+ * 
+ * @author Christian Ribeaud
+ */
 public interface IETLLIMSService extends IWebService, ISessionProvider
 {
-    
+    /**
+     * Returns the home database instance.
+     */
     @Transactional(readOnly = true)
     @RolesAllowed(RoleSet.ETL_SERVER)
     public DatabaseInstancePE getHomeDatabaseInstance(final String sessionToken);
-
+    
+    /**
+     * Registers a Data Store Server for the specified info. 
+     */
     @Transactional
     @RolesAllowed(RoleSet.ETL_SERVER)
-    public void registerDataStoreServer(String sessionToken, int port, String dssSessionToken);
-    
+    public void registerDataStoreServer(String sessionToken, DataStoreServerInfo dataStoreServerInfo);
+
+    /**
+     * Gets an {@link ExperimentPE} object specified by experiment ID and sample code.
+     * 
+     * @param sessionToken the user authentication token. Must not be <code>null</code>.
+     * @param sampleIdentifier an identifier which uniquely identifies the sample.
+     * @return <code>null</code> if no experiment could be found for given <var>sampleIdentifier</var>.
+     */
     @Transactional(readOnly = true)
     @RolesAllowed(RoleSet.ETL_SERVER)
     public ExperimentPE tryToGetBaseExperiment(final String sessionToken,
             @AuthorizationGuard(guardClass = SampleOwnerIdentifierPredicate.class)
             final SampleIdentifier sampleIdentifier) throws UserFailureException;
 
+    /**
+     * Tries to return the properties of the top sample (e.g. master plate) registered for the
+     * specified sample code.
+     * 
+     * @param sessionToken the user authentication token. Must not be <code>null</code>.
+     * @param sampleIdentifier an identifier which uniquely identifies the sample.
+     * @return <code>null</code> if no appropriated sample found. Returns an empty array if a a
+     *         sample found with no properties.
+     */
     @Transactional(readOnly = true)
     @RolesAllowed(RoleSet.ETL_SERVER)
     public SamplePropertyPE[] tryToGetPropertiesOfTopSampleRegisteredFor(final String sessionToken,
             @AuthorizationGuard(guardClass = SampleOwnerIdentifierPredicate.class)
             final SampleIdentifier sampleIdentifier) throws UserFailureException;
 
+    /**
+     * Registers the specified data.
+     * 
+     * @param sessionToken The user authentication token. Must not be <code>null</code>.
+     * @param sampleIdentifier an identifier which uniquely identifies the sample.
+     * @param externalData Data set to be registered. It is assumed that the attributes
+     *            <code>location</code>, <code>fileFormatType</code>, <code>dataSetType</code>,
+     *            and <code>locatorType</code> are not-<code>null</code>.
+     * @throws UserFailureException if given data set code could not be found in the persistence
+     *             layer.
+     */
     @Transactional
     @RolesAllowed(RoleSet.ETL_SERVER)
     public void registerDataSet(final String sessionToken,
             @AuthorizationGuard(guardClass = SampleOwnerIdentifierPredicate.class)
             final SampleIdentifier sampleIdentifier, final ExternalData externalData)
             throws UserFailureException;
-            
+    
+    /**
+     * Tries to return the data set specified by its code.
+     */
     @Transactional
     @RolesAllowed(RoleSet.OBSERVER)
     public ExternalDataPE tryGetDataSet(String sessionToken, String dataSetCode)
             throws UserFailureException;
 
+    /**
+     * Creates and returns a unique code for a new data set.
+     */
     @Transactional
     @RolesAllowed(RoleSet.ETL_SERVER)
     public String createDataSetCode(final String sessionToken) throws UserFailureException;