From 4e4a0705bb6ea6ecb100c84c65c7d0b5cb5e6ba6 Mon Sep 17 00:00:00 2001
From: cramakri <cramakri>
Date: Thu, 1 Apr 2010 18:27:12 +0000
Subject: [PATCH] LMS-1465 Made infrastructure more flexible and implemented
 dynamic discovery of a available RPC service interface.

SVN: 15334
---
 .../dss/component/impl/DssComponent.java      |  35 ++++-
 .../dss/generic/server/DataStoreServer.java   |  27 +++-
 .../server/DssServiceRpcNameServer.java       |  71 +++++++++
 .../dss/generic/shared/ServiceProvider.java   |   6 +
 .../dss/rpc/client/DssServiceRpcFactory.java  |  29 +++-
 .../dss/rpc/client/IDssServiceRpcFactory.java |  12 +-
 .../rpc/shared/DssServiceRpcInterface.java    |  69 +++++++++
 .../openbis/dss/rpc/shared/FileInfoDss.java   |   1 -
 .../dss/rpc/shared/FileInfoDssBuilder.java    |  18 ++-
 .../rpc/shared/IDssServiceRpcNameServer.java  |  27 ++++
 .../source/java/dssApplicationContext.xml     |   9 ++
 .../dss/component/impl/DssComponentTest.java  | 143 ++++++++++++++++--
 .../generic/server/DssServiceRpcV1Test.java   |  14 +-
 .../openbis/dss/impl/component/.gitignore     |   0
 14 files changed, 415 insertions(+), 46 deletions(-)
 create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DssServiceRpcNameServer.java
 create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/shared/DssServiceRpcInterface.java
 create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/shared/IDssServiceRpcNameServer.java
 delete mode 100644 datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/impl/component/.gitignore

diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/component/impl/DssComponent.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/component/impl/DssComponent.java
index a18a5261a8e..1c6ee7669d7 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/component/impl/DssComponent.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/component/impl/DssComponent.java
@@ -24,14 +24,15 @@ import org.springframework.remoting.RemoteConnectFailureException;
 import ch.systemsx.cisd.common.exceptions.AuthorizationFailureException;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 import ch.systemsx.cisd.common.exceptions.InvalidSessionException;
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.common.spring.HttpInvokerUtils;
 import ch.systemsx.cisd.openbis.dss.component.IDataSetDss;
 import ch.systemsx.cisd.openbis.dss.component.IDssComponent;
 import ch.systemsx.cisd.openbis.dss.rpc.client.DssServiceRpcFactory;
 import ch.systemsx.cisd.openbis.dss.rpc.client.IDssServiceRpcFactory;
+import ch.systemsx.cisd.openbis.dss.rpc.shared.DssServiceRpcInterface;
 import ch.systemsx.cisd.openbis.dss.rpc.shared.FileInfoDss;
 import ch.systemsx.cisd.openbis.dss.rpc.shared.IDssServiceRpcV1;
-import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataStore;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
@@ -193,7 +194,7 @@ class UnauthenticatedState extends AbstractDssComponentState
  */
 class AuthenticatedState extends AbstractDssComponentState
 {
-    private static final String RPC_V1 = "/rpc/v1";
+    private static final String V1_INTERFACE_NAME = "V1";
 
     private final SessionContextDTO session;
 
@@ -278,21 +279,43 @@ class AuthenticatedState extends AbstractDssComponentState
     private IDssServiceRpcV1 getDssServiceForUrl(String url)
     {
         // Get an RPC service for the DSS server
-        String serverURL = url + RPC_V1;
-        IDssServiceRpcV1 dssService = null;
+        String serverURL = url;
         try
         {
-            dssService = dssServiceFactory.getServiceV1(serverURL, false);
+            IDssServiceRpcV1 dssService = basicGetDssServiceForUrl(serverURL);
+            return dssService;
         } catch (RemoteAccessException e)
         {
+            IDssServiceRpcV1 dssService = null;
             // if the url begins with https, try http
             if (serverURL.startsWith("https://"))
             {
                 // https:// has 8 characters
                 serverURL = "http://" + serverURL.substring(8);
-                dssService = dssServiceFactory.getServiceV1(serverURL, false);
+                dssService = basicGetDssServiceForUrl(serverURL);
+                return dssService;
             }
+            return dssService;
+        }
+    }
 
+    /**
+     * A less sophisticated implementation of getDssServiceForUrl
+     */
+    private IDssServiceRpcV1 basicGetDssServiceForUrl(String serverURL)
+    {
+        IDssServiceRpcV1 dssService = null;
+        DssServiceRpcInterface[] ifaces =
+                dssServiceFactory.getSupportedInterfaces(serverURL, false);
+        for (DssServiceRpcInterface iface : ifaces)
+        {
+            if (V1_INTERFACE_NAME.equals(iface.getInterfaceName()))
+            {
+                dssService =
+                        dssServiceFactory.getService(iface, IDssServiceRpcV1.class, serverURL,
+                                false);
+                break;
+            }
         }
         return dssService;
     }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServer.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServer.java
index 4595330df94..9da10d2ed05 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServer.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServer.java
@@ -53,6 +53,7 @@ import ch.systemsx.cisd.openbis.dss.generic.server.ConfigParameters.PluginServle
 import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
 import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider;
 import ch.systemsx.cisd.openbis.dss.generic.shared.utils.PropertyParametersUtil;
+import ch.systemsx.cisd.openbis.dss.rpc.shared.DssServiceRpcInterface;
 import ch.systemsx.cisd.openbis.generic.shared.IServer;
 
 /**
@@ -263,9 +264,29 @@ public class DataStoreServer
         service.setStoreDirectory(applicationContext.getConfigParameters().getStorePath());
 
         // Export the spring bean to the world by wrapping it in an HttpInvokerServlet
-        String rpcPath = "/" + DATA_STORE_SERVER_RPC_SERVICE_NAME + "/v1";
-        context.addServlet(new ServletHolder(new HttpInvokerServlet(serviceExporter, rpcPath)),
-                rpcPath);
+        String rpcV1Path = "/" + DATA_STORE_SERVER_RPC_SERVICE_NAME + "/v1";
+        context.addServlet(new ServletHolder(new HttpInvokerServlet(serviceExporter, rpcV1Path)),
+                rpcV1Path);
+
+        // Inform the name server about the services I export
+        // N.b. In the future, this could be done using spring instead of programmatically
+        serviceExporter = ServiceProvider.getDssServiceRpcNameServer();
+        DssServiceRpcNameServer rpcNameServer =
+                (DssServiceRpcNameServer) serviceExporter.getService();
+        DssServiceRpcInterface v1Interface = new DssServiceRpcInterface();
+        v1Interface.setInterfaceName("V1");
+        v1Interface.setInterfaceUrlSuffix(rpcV1Path);
+        rpcNameServer.addSupportedInterface(v1Interface);
+
+        String nameServerPath = "/" + DATA_STORE_SERVER_RPC_SERVICE_NAME;
+        DssServiceRpcInterface nameServerInterface = new DssServiceRpcInterface();
+        nameServerInterface.setInterfaceName("NameServer");
+        nameServerInterface.setInterfaceUrlSuffix(nameServerPath);
+        rpcNameServer.addSupportedInterface(nameServerInterface);
+
+        context.addServlet(new ServletHolder(
+                new HttpInvokerServlet(serviceExporter, nameServerPath)), nameServerPath);
+
     }
 
     private static void registerPluginServlets(Context context, List<PluginServlet> pluginServlets)
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DssServiceRpcNameServer.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DssServiceRpcNameServer.java
new file mode 100644
index 00000000000..c05d085ed86
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DssServiceRpcNameServer.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.generic.server;
+
+import java.util.ArrayList;
+
+import org.apache.log4j.Logger;
+
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogFactory;
+import ch.systemsx.cisd.openbis.dss.rpc.shared.DssServiceRpcInterface;
+import ch.systemsx.cisd.openbis.dss.rpc.shared.IDssServiceRpcNameServer;
+
+/**
+ * Implementation of the IDssServiceRpcNameServer interface which registry for accessing the RPC
+ * services supported by a server.
+ * 
+ * @author Chandrasekhar Ramakrishnan
+ */
+public class DssServiceRpcNameServer implements IDssServiceRpcNameServer
+{
+    private final ArrayList<DssServiceRpcInterface> supportedInterfaces;
+
+    static private final Logger operationLog =
+            LogFactory.getLogger(LogCategory.OPERATION, DssServiceRpcNameServer.class);
+
+    DssServiceRpcNameServer()
+    {
+        supportedInterfaces = new ArrayList<DssServiceRpcInterface>();
+    }
+
+    public DssServiceRpcInterface[] getSupportedInterfaces()
+    {
+        DssServiceRpcInterface[] ifaces = new DssServiceRpcInterface[supportedInterfaces.size()];
+        return supportedInterfaces.toArray(ifaces);
+    }
+
+    public int getMinClientVersion()
+    {
+        return 1;
+    }
+
+    public int getVersion()
+    {
+        return 1;
+    }
+
+    /**
+     * Package-visible method for configuring the registry
+     */
+    void addSupportedInterface(DssServiceRpcInterface iface)
+    {
+        supportedInterfaces.add(iface);
+        operationLog.info("Registered RPC Interface " + iface.toString());
+    }
+
+}
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/ServiceProvider.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/ServiceProvider.java
index ea47ad1c2b7..d2d8aaabdf1 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/ServiceProvider.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/ServiceProvider.java
@@ -46,6 +46,12 @@ public class ServiceProvider
         return ((HttpInvokerServiceExporter) APPLICATION_CONTEXT.getBean("data-store-server"));
     }
 
+    public static StreamSupportingHttpInvokerServiceExporter getDssServiceRpcNameServer()
+    {
+        return ((StreamSupportingHttpInvokerServiceExporter) APPLICATION_CONTEXT
+                .getBean("data-store-rpc-name-server"));
+    }
+
     public static StreamSupportingHttpInvokerServiceExporter getDssServiceRpcV1()
     {
         return ((StreamSupportingHttpInvokerServiceExporter) APPLICATION_CONTEXT
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/client/DssServiceRpcFactory.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/client/DssServiceRpcFactory.java
index d687fd2ead4..db15a3004a7 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/client/DssServiceRpcFactory.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/client/DssServiceRpcFactory.java
@@ -43,8 +43,9 @@ import com.marathon.util.spring.StreamSupportingHttpInvokerProxyFactoryBean;
 
 import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel;
 import ch.systemsx.cisd.common.spring.HttpInvokerUtils;
+import ch.systemsx.cisd.openbis.dss.rpc.shared.DssServiceRpcInterface;
 import ch.systemsx.cisd.openbis.dss.rpc.shared.IDssServiceRpc;
-import ch.systemsx.cisd.openbis.dss.rpc.shared.IDssServiceRpcV1;
+import ch.systemsx.cisd.openbis.dss.rpc.shared.IDssServiceRpcNameServer;
 
 /**
  * Client-side factory for DssServiceRpc objects.
@@ -58,16 +59,36 @@ public class DssServiceRpcFactory implements IDssServiceRpcFactory
 {
     private static final int SERVER_TIMEOUT_MIN = 5;
 
-    public IDssServiceRpcV1 getServiceV1(String serviceURL, boolean getServerCertificateFromServer)
+    private static final String NAME_SERVER_SUFFIX = "/rpc";
+
+    public DssServiceRpcInterface[] getSupportedInterfaces(String serverURL,
+            boolean getServerCertificateFromServer) throws IncompatibleAPIVersionsException
+    {
+        // We assume the location of the name server follows the convention
+        String nameServerURL = serverURL + NAME_SERVER_SUFFIX;
+        Class<IDssServiceRpcNameServer> clazz = IDssServiceRpcNameServer.class;
+        if (getServerCertificateFromServer)
+        {
+            new SslCertificateHelper(nameServerURL, getConfigDirectory()).setUpKeyStore();
+        }
+
+        IDssServiceRpcNameServer nameServer =
+                new ServiceProxyBuilder<IDssServiceRpcNameServer>(nameServerURL, clazz,
+                        SERVER_TIMEOUT_MIN, 1).getServiceInterface();
+        return nameServer.getSupportedInterfaces();
+    }
+
+    public <T extends IDssServiceRpc> T getService(DssServiceRpcInterface iface,
+            Class<T> ifaceClazz, String serverURL, boolean getServerCertificateFromServer)
             throws IncompatibleAPIVersionsException
     {
-        Class<IDssServiceRpcV1> clazz = IDssServiceRpcV1.class;
+        String serviceURL = serverURL + iface.getInterfaceUrlSuffix();
         if (getServerCertificateFromServer)
         {
             new SslCertificateHelper(serviceURL, getConfigDirectory()).setUpKeyStore();
         }
 
-        return new ServiceProxyBuilder<IDssServiceRpcV1>(serviceURL, clazz, SERVER_TIMEOUT_MIN, 1)
+        return new ServiceProxyBuilder<T>(serviceURL, ifaceClazz, SERVER_TIMEOUT_MIN, 1)
                 .getServiceInterface();
     }
 
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/client/IDssServiceRpcFactory.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/client/IDssServiceRpcFactory.java
index 63f62d8d6df..686b5833bdd 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/client/IDssServiceRpcFactory.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/client/IDssServiceRpcFactory.java
@@ -16,17 +16,23 @@
 
 package ch.systemsx.cisd.openbis.dss.rpc.client;
 
-import ch.systemsx.cisd.openbis.dss.rpc.shared.IDssServiceRpcV1;
+import ch.systemsx.cisd.openbis.dss.rpc.shared.DssServiceRpcInterface;
+import ch.systemsx.cisd.openbis.dss.rpc.shared.IDssServiceRpc;
 
 /**
  * The interface for creating proxies to the data store server.
+ * <p>
+ * Because of the inherent potential variability in the DSS RPC, the interface has been made
+ * flexible to provide clients simultaneous access to several different communication interfaces.
  * 
  * @author Chandrasekhar Ramakrishnan
  */
 public interface IDssServiceRpcFactory
 {
-
-    public abstract IDssServiceRpcV1 getServiceV1(String serviceURL,
+    public abstract DssServiceRpcInterface[] getSupportedInterfaces(String serverURL,
             boolean getServerCertificateFromServer) throws IncompatibleAPIVersionsException;
 
+    public abstract <T extends IDssServiceRpc> T getService(DssServiceRpcInterface iface,
+            Class<T> ifaceClazz, String serverURL, boolean getServerCertificateFromServer)
+            throws IncompatibleAPIVersionsException;
 }
\ No newline at end of file
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/shared/DssServiceRpcInterface.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/shared/DssServiceRpcInterface.java
new file mode 100644
index 00000000000..4f77edaea05
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/shared/DssServiceRpcInterface.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.rpc.shared;
+
+import java.io.Serializable;
+
+/**
+ * @author Chandrasekhar Ramakrishnan
+ */
+public class DssServiceRpcInterface implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    private String interfaceName;
+
+    private String interfaceUrlSuffix;
+
+    /**
+     * The name of this interface used for identification.
+     */
+    public String getInterfaceName()
+    {
+        return interfaceName;
+    }
+
+    public void setInterfaceName(String interfaceName)
+    {
+        this.interfaceName = interfaceName;
+    }
+
+    /**
+     * The suffix added to the DSS URL to interact with this interface
+     */
+    public String getInterfaceUrlSuffix()
+    {
+        return interfaceUrlSuffix;
+    }
+
+    public void setInterfaceUrlSuffix(String interfaceUrlSuffix)
+    {
+        this.interfaceUrlSuffix = interfaceUrlSuffix;
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+        sb.append("DssServiceRpcInterface[");
+        sb.append(getInterfaceName());
+        sb.append(",");
+        sb.append(getInterfaceUrlSuffix());
+        sb.append("]");
+        return sb.toString();
+    }
+}
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/shared/FileInfoDss.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/shared/FileInfoDss.java
index 36009bef1e9..9afb49c53f0 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/shared/FileInfoDss.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/shared/FileInfoDss.java
@@ -98,5 +98,4 @@ public class FileInfoDss implements Serializable
         sb.append("]");
         return sb.toString();
     }
-
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/shared/FileInfoDssBuilder.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/shared/FileInfoDssBuilder.java
index 95db120cded..ef76bd45820 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/shared/FileInfoDssBuilder.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/shared/FileInfoDssBuilder.java
@@ -53,18 +53,24 @@ public class FileInfoDssBuilder
             boolean isRecursive) throws IOException
     {
         // at the top level, we should list the contents of directories, but only recurse if the
-        // search is recursive
-        appendFileInfosForFile(requestedFile, list, isRecursive ? Integer.MAX_VALUE : 0);
+        // search is recursive; and we should skip listing the top directory
+        appendFileInfosForFile(requestedFile, list, isRecursive ? Integer.MAX_VALUE : 0, true);
     }
 
     private void appendFileInfosForFile(File requestedFile, ArrayList<FileInfoDss> list,
-            int maxDepth) throws IOException
+            int maxDepth, boolean excludeTopLevelIfDirectory) throws IOException
     {
         FileInfoDss fileInfo = fileInfoForFile(requestedFile);
-        list.add(fileInfo);
+        if (excludeTopLevelIfDirectory && fileInfo.isDirectory())
+        {
+            // If specified, skip the top level if it is a directory
+        } else
+        {
+            list.add(fileInfo);
+        }
 
         // If this is a file or we have exhausted the depth, return
-        if (requestedFile.isDirectory() == false || maxDepth < 0)
+        if (fileInfo.isDirectory() == false || maxDepth < 0)
         {
             return;
         }
@@ -72,7 +78,7 @@ public class FileInfoDssBuilder
         File[] files = requestedFile.listFiles();
         for (File file : files)
         {
-            appendFileInfosForFile(file, list, maxDepth - 1);
+            appendFileInfosForFile(file, list, maxDepth - 1, false);
         }
     }
 
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/shared/IDssServiceRpcNameServer.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/shared/IDssServiceRpcNameServer.java
new file mode 100644
index 00000000000..3512701ad10
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/rpc/shared/IDssServiceRpcNameServer.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.rpc.shared;
+
+/**
+ * An Interface for finding out about the DssServiceRpc interfaces supported by a server.
+ * 
+ * @author Chandrasekhar Ramakrishnan
+ */
+public interface IDssServiceRpcNameServer extends IDssServiceRpc
+{
+    DssServiceRpcInterface[] getSupportedInterfaces();
+}
diff --git a/datastore_server/source/java/dssApplicationContext.xml b/datastore_server/source/java/dssApplicationContext.xml
index 76e27d3eb25..ab0033260da 100644
--- a/datastore_server/source/java/dssApplicationContext.xml
+++ b/datastore_server/source/java/dssApplicationContext.xml
@@ -67,6 +67,15 @@
         <property name="serviceInterface" value="ch.systemsx.cisd.openbis.dss.rpc.shared.IDssServiceRpcV1" />
     </bean>
     
+    <bean id="data-store-rpc-name-server"
+        class="com.marathon.util.spring.StreamSupportingHttpInvokerServiceExporter">
+        <property name="service">
+            <bean class="ch.systemsx.cisd.openbis.dss.generic.server.DssServiceRpcNameServer">
+            </bean>
+        </property>
+        <property name="serviceInterface" value="ch.systemsx.cisd.openbis.dss.rpc.shared.IDssServiceRpcNameServer" />
+    </bean>
+    
     <bean id="session-token-manager" class="ch.systemsx.cisd.openbis.dss.generic.server.SessionTokenManager"/>
     
     
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/component/impl/DssComponentTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/component/impl/DssComponentTest.java
index b20c38a0045..ae46ed2b675 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/component/impl/DssComponentTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/component/impl/DssComponentTest.java
@@ -16,14 +16,29 @@
 
 package ch.systemsx.cisd.openbis.dss.component.impl;
 
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Random;
+
+import org.apache.commons.io.IOUtils;
 import org.jmock.Expectations;
 import org.jmock.Mockery;
-import org.testng.AssertJUnit;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase;
 import ch.systemsx.cisd.openbis.dss.component.IDataSetDss;
 import ch.systemsx.cisd.openbis.dss.rpc.client.IDssServiceRpcFactory;
+import ch.systemsx.cisd.openbis.dss.rpc.shared.DssServiceRpcInterface;
+import ch.systemsx.cisd.openbis.dss.rpc.shared.FileInfoDss;
+import ch.systemsx.cisd.openbis.dss.rpc.shared.FileInfoDssBuilder;
 import ch.systemsx.cisd.openbis.dss.rpc.shared.IDssServiceRpcV1;
 import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataStore;
@@ -33,7 +48,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SessionContextDTO;
 /**
  * @author Chandrasekhar Ramakrishnan
  */
-public class DssComponentTest extends AssertJUnit
+public class DssComponentTest extends AbstractFileSystemTestCase
 {
     private Mockery context;
 
@@ -43,20 +58,27 @@ public class DssComponentTest extends AssertJUnit
 
     private DssComponent dssComponent;
 
+    private File randomDataFile;
+
     private static final String DUMMY_SESSSION_TOKEN = "DummySessionToken";
 
+    private static final String DUMMY_DSS_URL = "http://localhost/datastore_server";
+
     public DssComponentTest()
     {
 
     }
 
+    @Override
     @BeforeMethod
-    public void setUp()
+    public void setUp() throws IOException
     {
+        super.setUp();
         context = new Mockery();
         openBisService = context.mock(IETLLIMSService.class);
         dssServiceFactory = context.mock(IDssServiceRpcFactory.class);
         dssComponent = new DssComponent(openBisService, dssServiceFactory);
+        randomDataFile = getFileWithRandomData(1);
     }
 
     @Test
@@ -73,17 +95,77 @@ public class DssComponentTest extends AssertJUnit
             });
 
         dssComponent.login("foo", "bar");
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testListDataSetFiles() throws IOException
+    {
+        setupExpectations();
+
+        dssComponent.login("foo", "bar");
+        IDataSetDss dataSetProxy = dssComponent.getDataSet("DummyDataSetCode");
+        FileInfoDss[] fileInfos = dataSetProxy.listFiles("/", true);
+        System.err.println(Arrays.toString(fileInfos));
+        assertEquals(1, fileInfos.length);
+
+        context.assertIsSatisfied();
     }
 
     @Test
-    public void testListDataSetFiles()
+    public void testGetFileContents() throws IOException
+    {
+        setupExpectations();
+
+        dssComponent.login("foo", "bar");
+        IDataSetDss dataSetProxy = dssComponent.getDataSet("DummyDataSetCode");
+        FileInfoDss[] fileInfos = dataSetProxy.listFiles("/", true);
+        FileInfoDss fileFileInfo = null;
+        for (FileInfoDss fid : fileInfos)
+        {
+            if (fid.isDirectory() == false)
+            {
+                fileFileInfo = fid;
+                break;
+            }
+        }
+        if (fileFileInfo == null)
+        {
+            fail("Could not find file info for random file");
+            return;
+        }
+
+        InputStream is = dataSetProxy.getFile(fileFileInfo.getPath());
+        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+        int charCount = 0;
+        while (reader.read() >= 0)
+        {
+            ++charCount;
+        }
+
+        assertEquals(fileFileInfo.getFileSize(), charCount);
+    }
+
+    private void setupExpectations() throws IOException
     {
         final SessionContextDTO session = getDummySession();
         final ExternalData dataSetExternalData = new ExternalData();
         DataStore dataStore = new DataStore();
-        dataStore.setDownloadUrl("http://localhost/path/to/dataset");
+        dataStore.setDownloadUrl(DUMMY_DSS_URL);
         dataSetExternalData.setDataStore(dataStore);
         final IDssServiceRpcV1 dssService = context.mock(IDssServiceRpcV1.class);
+        ArrayList<FileInfoDss> list = new ArrayList<FileInfoDss>();
+        FileInfoDssBuilder builder = new FileInfoDssBuilder(workingDirectory.getCanonicalPath());
+        builder.appendFileInfosForFile(workingDirectory, list, true);
+        final FileInfoDss[] fileInfos = new FileInfoDss[list.size()];
+        list.toArray(fileInfos);
+
+        final DssServiceRpcInterface[] ifaces = new DssServiceRpcInterface[1];
+        final DssServiceRpcInterface iface = new DssServiceRpcInterface();
+        iface.setInterfaceName("V1");
+        iface.setInterfaceUrlSuffix("/rpc/v1");
+        ifaces[0] = iface;
 
         context.checking(new Expectations()
             {
@@ -92,21 +174,21 @@ public class DssComponentTest extends AssertJUnit
 
                     one(openBisService).tryToAuthenticate("foo", "bar");
                     will(returnValue(session));
-                    one(openBisService).tryGetDataSet(DUMMY_SESSSION_TOKEN, dataSetCode);
+                    allowing(openBisService).tryGetDataSet(DUMMY_SESSSION_TOKEN, dataSetCode);
                     will(returnValue(dataSetExternalData));
-                    one(dssServiceFactory).getServiceV1("http://localhost/path/to/dataset/rpc/v1",
-                            false);
+                    allowing(dssServiceFactory).getSupportedInterfaces(DUMMY_DSS_URL, false);
+                    will(returnValue(ifaces));
+                    allowing(dssServiceFactory).getService(iface, IDssServiceRpcV1.class,
+                            DUMMY_DSS_URL, false);
                     will(returnValue(dssService));
-                    one(dssService).listFilesForDataSet(DUMMY_SESSSION_TOKEN, dataSetCode, "/",
-                            true);
-                    will(returnValue(null));
+                    allowing(dssService).listFilesForDataSet(DUMMY_SESSSION_TOKEN, dataSetCode,
+                            "/", true);
+                    will(returnValue(fileInfos));
+                    allowing(dssService).getFileForDataSet(DUMMY_SESSSION_TOKEN, dataSetCode,
+                            "/random.txt");
+                    will(returnValue(new FileInputStream(randomDataFile)));
                 }
             });
-
-        dssComponent.login("foo", "bar");
-        IDataSetDss dataSetProxy = dssComponent.getDataSet("DummyDataSetCode");
-        dataSetProxy.listFiles("/", true);
-        // Don't check values, just check that the invocation path works
     }
 
     private SessionContextDTO getDummySession()
@@ -115,4 +197,33 @@ public class DssComponentTest extends AssertJUnit
         session.setSessionToken(DUMMY_SESSSION_TOKEN);
         return session;
     }
+
+    private File getFileWithRandomData(long sizeInKB) throws IOException
+    {
+        File file = new File(workingDirectory, "random.txt");
+        Random random = new Random();
+        FileOutputStream outputStream = null;
+        try
+        {
+            outputStream = new FileOutputStream(file);
+            byte[] bytes = new byte[1024];
+            for (int i = 0; i < sizeInKB; i++)
+            {
+                random.nextBytes(bytes);
+                outputStream.write(bytes);
+            }
+        } catch (IOException ex)
+        {
+            throw ex;
+        } finally
+        {
+            if (outputStream != null)
+            {
+                IOUtils.closeQuietly(outputStream);
+            }
+        }
+
+        return file;
+    }
+
 }
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/DssServiceRpcV1Test.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/DssServiceRpcV1Test.java
index 2f86a667bc5..585d528d4ce 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/DssServiceRpcV1Test.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/DssServiceRpcV1Test.java
@@ -124,7 +124,7 @@ public class DssServiceRpcV1Test extends AbstractFileSystemTestCase
         setupStandardExpectations();
         FileInfoDss[] fileInfos =
                 rpcService.listFilesForDataSet(SESSION_TOKEN, DATA_SET_CODE, "/", false);
-        assertEquals(3, fileInfos.length);
+        assertEquals(2, fileInfos.length);
         int dirCount = 0;
         int fileIndex = 0;
         int i = 0;
@@ -139,7 +139,7 @@ public class DssServiceRpcV1Test extends AbstractFileSystemTestCase
             }
             ++i;
         }
-        assertEquals(2, dirCount);
+        assertEquals(1, dirCount);
         FileInfoDss fileInfo = fileInfos[fileIndex];
         assertEquals("/foo.txt", fileInfo.getPath());
         assertEquals(100, fileInfo.getFileSize());
@@ -153,7 +153,7 @@ public class DssServiceRpcV1Test extends AbstractFileSystemTestCase
         setupStandardExpectations();
         FileInfoDss[] fileInfos =
                 rpcService.listFilesForDataSet(SESSION_TOKEN, DATA_SET_CODE, "/", true);
-        assertEquals(4, fileInfos.length);
+        assertEquals(3, fileInfos.length);
         int dirCount = 0;
         int fileCount = 0;
         int[] fileIndices = new int[2];
@@ -169,7 +169,7 @@ public class DssServiceRpcV1Test extends AbstractFileSystemTestCase
             }
             ++i;
         }
-        assertEquals(2, dirCount);
+        assertEquals(1, dirCount);
         for (i = 0; i < 2; ++i)
         {
             FileInfoDss fileInfo = fileInfos[fileIndices[i]];
@@ -194,7 +194,7 @@ public class DssServiceRpcV1Test extends AbstractFileSystemTestCase
         setupStandardExpectations();
         FileInfoDss[] fileInfos =
                 rpcService.listFilesForDataSet(SESSION_TOKEN, DATA_SET_CODE, "/stuff/", false);
-        assertEquals(2, fileInfos.length);
+        assertEquals(1, fileInfos.length);
         int dirCount = 0;
         int fileIndex = 0;
         int i = 0;
@@ -209,7 +209,7 @@ public class DssServiceRpcV1Test extends AbstractFileSystemTestCase
             }
             ++i;
         }
-        assertEquals(1, dirCount);
+        assertEquals(0, dirCount);
         FileInfoDss fileInfo = fileInfos[fileIndex];
         assertEquals("/stuff/bar.txt", fileInfo.getPath());
         assertEquals(110, fileInfo.getFileSize());
@@ -223,7 +223,7 @@ public class DssServiceRpcV1Test extends AbstractFileSystemTestCase
         setupStandardExpectations();
         FileInfoDss[] fileInfos =
                 rpcService.listFilesForDataSet(SESSION_TOKEN, DATA_SET_CODE, "stuff/", false);
-        assertEquals(2, fileInfos.length);
+        assertEquals(1, fileInfos.length);
 
         context.assertIsSatisfied();
     }
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/impl/component/.gitignore b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/impl/component/.gitignore
deleted file mode 100644
index e69de29bb2d..00000000000
-- 
GitLab