From 096e3a36b3a99821839c659c4b45d470764219da Mon Sep 17 00:00:00 2001
From: felmer <felmer>
Date: Thu, 16 Jun 2011 10:40:36 +0000
Subject: [PATCH] LMS-2294 Extend OpenBISScreeningML with methods to list files
 in data sets and to down load files from a data set.

SVN: 21717
---
 .../dss/client/api/v1/IDataSetDss.java        |  15 ++
 .../dss/client/api/v1/impl/DataSetDss.java    |   8 +-
 .../dss/client/api/v1/impl/DssComponent.java  |  36 ++++-
 .../server/api/v1/DssServiceRpcGeneric.java   |  14 +-
 .../api/v1/DssServiceRpcGenericTest.java      | 137 ++++++++++++------
 screening/source/java/OpenBISScreeningML.java |  88 ++++++++++-
 .../v1/IScreeningOpenbisServiceFacade.java    |   3 +
 .../api/v1/ScreeningOpenbisServiceFacade.java |   8 +
 .../java/OpenBISScreeningMLTest.java          |  55 +++++++
 9 files changed, 304 insertions(+), 60 deletions(-)

diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/IDataSetDss.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/IDataSetDss.java
index a5402790f83..22543b7d1a8 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/IDataSetDss.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/IDataSetDss.java
@@ -84,5 +84,20 @@ public interface IDataSetDss
      */
     public File getLinkOrCopyOfContents(String overrideStoreRootPathOrNull, File downloadDir)
             throws IllegalArgumentException, InvalidSessionException;
+    
+    /**
+     * Returns a {@link File}, if possible, that directly references some specified content of a data set in
+     * the data store server. If not possible, downloads that content and returns a File in
+     * the downloadDir containing that content.
+     * 
+     * @param overrideStoreRootPathOrNull A path, in the context of the local file system mounts, to
+     *            the DSS' store root. If null, datasets are copied to the downloadDir folder.
+     * @param downloadDir The directory in which to place the contents of the data set if they must
+     *            be downloaded.
+     * @param pathInDataSet Path of requested content inside the data set.
+     * @return A File containing the requested content.
+     */
+    public File getLinkOrCopyOfContent(String overrideStoreRootPathOrNull, File downloadDir,
+            String pathInDataSet) throws IllegalArgumentException, InvalidSessionException;
 
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/DataSetDss.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/DataSetDss.java
index 05f6dea8522..d9603fa4ad9 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/DataSetDss.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/DataSetDss.java
@@ -71,7 +71,13 @@ public class DataSetDss implements IDataSetDss
     public File getLinkOrCopyOfContents(String overrideStoreRootPathOrNull, File downloadDir)
             throws IllegalArgumentException, InvalidSessionException
     {
-        return parent.getLinkOrCopyOfContents(this, overrideStoreRootPathOrNull, downloadDir);
+        return parent.getLinkOrCopyOfContents(this, overrideStoreRootPathOrNull, downloadDir, null);
+    }
+
+    public File getLinkOrCopyOfContent(String overrideStoreRootPathOrNull, File downloadDir,
+            String pathInDataSet) throws IllegalArgumentException, InvalidSessionException
+    {
+        return parent.getLinkOrCopyOfContents(this, overrideStoreRootPathOrNull, downloadDir, pathInDataSet);
     }
 
     public AuthenticatedState getParent()
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/DssComponent.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/DssComponent.java
index 7fd14d1456d..37a8ce28241 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/DssComponent.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/DssComponent.java
@@ -479,6 +479,41 @@ class AuthenticatedState extends AbstractDssComponentState
      * otherwise copy the contents locally.
      */
     File getLinkOrCopyOfContents(DataSetDss dataSetDss, String overrideStoreRootPathOrNull,
+            File downloadDir, String pathInDataSetOrNull) throws InvalidSessionException
+    {
+        if (pathInDataSetOrNull == null)
+        {
+            return getLinkOrCopyOfContents(dataSetDss, overrideStoreRootPathOrNull, downloadDir);
+        }
+        File link = tryLinkToContents(dataSetDss, overrideStoreRootPathOrNull);
+        if (null != link)
+        {
+            File file = new File(link, pathInDataSetOrNull);
+            if (file.exists() == false)
+            {
+                throw new IllegalArgumentException("Data set " + dataSetDss.getCode()
+                        + " has no file/folder with path '" + pathInDataSetOrNull + "'.");
+            }
+            return file;
+        }
+
+        FileInfoDssDTO[] fileInfos =
+                dataSetDss.getService().listFilesForDataSet(getSessionToken(),
+                        dataSetDss.getCode(), pathInDataSetOrNull, true);
+        File result = new File(new File(downloadDir, dataSetDss.getCode()), pathInDataSetOrNull);
+        File outputDir = result;
+        if (fileInfos.length == 1 && fileInfos[0].isDirectory() == false)
+        {
+            outputDir = result.getParentFile();
+        }
+        outputDir.mkdirs();
+        FileInfoDssDownloader downloader =
+                new FileInfoDssDownloader(dataSetDss, fileInfos, outputDir);
+        downloader.downloadFiles();
+        return result;
+    }
+
+    private File getLinkOrCopyOfContents(DataSetDss dataSetDss, String overrideStoreRootPathOrNull,
             File downloadDir) throws InvalidSessionException
     {
         File link = tryLinkToContents(dataSetDss, overrideStoreRootPathOrNull);
@@ -500,7 +535,6 @@ class AuthenticatedState extends AbstractDssComponentState
 
         return outputDir;
     }
-
     /**
      * Create a connection to the DSS server referenced by url
      */
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/api/v1/DssServiceRpcGeneric.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/api/v1/DssServiceRpcGeneric.java
index 5337f54edee..4e0553ff8d6 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/api/v1/DssServiceRpcGeneric.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/api/v1/DssServiceRpcGeneric.java
@@ -88,13 +88,15 @@ public class DssServiceRpcGeneric extends AbstractDssServiceRpc<IDssServiceRpcGe
         {
             content = getHierarchicalContent(dataSetCode);
             IHierarchicalContentNode startPathNode = getContentNode(content, startPath);
-
-            IHierarchicalContentNode listingRootNode =
-                    (startPathNode.isDirectory()) ? startPathNode : content.getNode(startPathNode
-                            .getParentRelativePath());
-
             ArrayList<FileInfoDssDTO> list = new ArrayList<FileInfoDssDTO>();
-            appendFileInfosForFile(listingRootNode, list, isRecursive);
+            if (startPathNode.isDirectory())
+            {
+                appendFileInfosForFile(startPathNode, list, isRecursive);
+            } else
+            {
+                list.add(new FileInfoDssDTO(startPathNode.getRelativePath(), startPathNode
+                        .getName(), false, startPathNode.getFileLength()));
+            }
             FileInfoDssDTO[] fileInfos = new FileInfoDssDTO[list.size()];
             return list.toArray(fileInfos);
         } catch (IOException ex)
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/api/v1/DssServiceRpcGenericTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/api/v1/DssServiceRpcGenericTest.java
index 3c1cf8167b2..e596c75ee46 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/api/v1/DssServiceRpcGenericTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/api/v1/DssServiceRpcGenericTest.java
@@ -90,7 +90,7 @@ public class DssServiceRpcGenericTest extends AssertJUnit
     }
 
     @Test
-    public void testFilesForData()
+    public void testListSingleFileForData()
     {
         final String dataSetCode = "ds-1";
         final String path = "abc/de";
@@ -100,61 +100,102 @@ public class DssServiceRpcGenericTest extends AssertJUnit
         context.checking(new Expectations()
             {
                 {
-                    IHierarchicalContentNode mainNode = createNodeMock("mainNode");
                     one(content).getNode(path);
+                    IHierarchicalContentNode mainNode = context.mock(IHierarchicalContentNode.class, "mainNode");
                     will(returnValue(mainNode));
-
-                    IHierarchicalContentNode childNode1 = createNodeMock("childNode1");
-                    IHierarchicalContentNode childNode1Child1 = createNodeMock("childNode1Child1");
-                    IHierarchicalContentNode childNode1Child2 = createNodeMock("childNode1Child2");
-                    IHierarchicalContentNode childNode2 = createNodeMock("childNode2");
-                    IHierarchicalContentNode childNode2Child1 = createNodeMock("childNode2Child1");
-                    IHierarchicalContentNode childNode3 = createNodeMock("childNode3");
-
-                    prepareDirectoryNode(mainNode, path, childNode1, childNode2, childNode3);
-                    // child1
-                    prepareDirectoryNode(childNode1, path + "/child1", childNode1Child1,
-                            childNode1Child2);
-                    prepareFileNode(childNode1Child1, path + "/child1/child1", 11);
-                    prepareFileNode(childNode1Child2, path + "/child1/child2", 12);
-                    // child2
-                    prepareDirectoryNode(childNode2, path + "/child2", childNode2Child1);
-                    prepareFileNode(childNode2Child1, path + "/child2/child1", 21);
-                    // child3
-                    prepareFileNode(childNode3, path + "/child3", 3);
-                }
-
-                private IHierarchicalContentNode createNodeMock(String mockName)
-                {
-                    return context.mock(IHierarchicalContentNode.class, mockName);
-                }
-
-                private void prepareFileNode(IHierarchicalContentNode node,
-                        final String relativePath, long length)
-                {
-                    allowing(node).isDirectory();
+                    
+                    allowing(mainNode).getName();
+                    will(returnValue("main-node"));
+                    
+                    allowing(mainNode).isDirectory();
                     will(returnValue(false));
-                    allowing(node).getRelativePath();
-                    will(returnValue(relativePath));
-                    one(node).getFileLength();
-                    will(returnValue(length));
+                    
+                    allowing(mainNode).getRelativePath();
+                    will(returnValue(path));
+                    
+                    one(mainNode).getFileLength();
+                    will(returnValue(42L));
                 }
 
-                private void prepareDirectoryNode(IHierarchicalContentNode node,
-                        final String relativePath, IHierarchicalContentNode... childNodes)
-                {
-                    allowing(node).isDirectory();
-                    will(returnValue(true));
-                    allowing(node).getRelativePath();
-                    will(returnValue(relativePath));
-                    one(node).getChildNodes();
-                    will(returnValue(Arrays.asList(childNodes)));
-                }
             });
 
-        FileInfoDssDTO[] dataSets =
+        FileInfoDssDTO[] files =
                 dssService.listFilesForDataSet(SESSION_TOKEN, dataSetCode, path, true);
 
+        assertEquals(path, files[0].getPathInDataSet());
+        assertEquals("main-node", files[0].getPathInListing());
+        assertEquals(42L, files[0].getFileSize());
+        assertEquals(1, files.length);
+        context.assertIsSatisfied();
+    }
+
+    
+    @Test
+    public void testFilesForData()
+    {
+        final String dataSetCode = "ds-1";
+        final String path = "abc/de";
+        prepareLockDataSet(dataSetCode);
+        prepareAuthorizationCheck(dataSetCode);
+        prepareGetContent(dataSetCode);
+        context.checking(new Expectations()
+        {
+            {
+                IHierarchicalContentNode mainNode = createNodeMock("mainNode");
+                one(content).getNode(path);
+                will(returnValue(mainNode));
+                
+                IHierarchicalContentNode childNode1 = createNodeMock("childNode1");
+                IHierarchicalContentNode childNode1Child1 = createNodeMock("childNode1Child1");
+                IHierarchicalContentNode childNode1Child2 = createNodeMock("childNode1Child2");
+                IHierarchicalContentNode childNode2 = createNodeMock("childNode2");
+                IHierarchicalContentNode childNode2Child1 = createNodeMock("childNode2Child1");
+                IHierarchicalContentNode childNode3 = createNodeMock("childNode3");
+                
+                prepareDirectoryNode(mainNode, path, childNode1, childNode2, childNode3);
+                // child1
+                prepareDirectoryNode(childNode1, path + "/child1", childNode1Child1,
+                        childNode1Child2);
+                prepareFileNode(childNode1Child1, path + "/child1/child1", 11);
+                prepareFileNode(childNode1Child2, path + "/child1/child2", 12);
+                // child2
+                prepareDirectoryNode(childNode2, path + "/child2", childNode2Child1);
+                prepareFileNode(childNode2Child1, path + "/child2/child1", 21);
+                // child3
+                prepareFileNode(childNode3, path + "/child3", 3);
+            }
+            
+            private IHierarchicalContentNode createNodeMock(String mockName)
+            {
+                return context.mock(IHierarchicalContentNode.class, mockName);
+            }
+            
+            private void prepareFileNode(IHierarchicalContentNode node,
+                    final String relativePath, long length)
+            {
+                allowing(node).isDirectory();
+                will(returnValue(false));
+                allowing(node).getRelativePath();
+                will(returnValue(relativePath));
+                one(node).getFileLength();
+                will(returnValue(length));
+            }
+            
+            private void prepareDirectoryNode(IHierarchicalContentNode node,
+                    final String relativePath, IHierarchicalContentNode... childNodes)
+            {
+                allowing(node).isDirectory();
+                will(returnValue(true));
+                allowing(node).getRelativePath();
+                will(returnValue(relativePath));
+                one(node).getChildNodes();
+                will(returnValue(Arrays.asList(childNodes)));
+            }
+        });
+        
+        FileInfoDssDTO[] dataSets =
+            dssService.listFilesForDataSet(SESSION_TOKEN, dataSetCode, path, true);
+        
         assertEquals(6, dataSets.length);
         assertEquals(fileInfoString(path, "child1", -1), dataSets[0].toString());
         assertEquals(fileInfoString(path, "child1/child1", 11), dataSets[1].toString());
@@ -164,7 +205,7 @@ public class DssServiceRpcGenericTest extends AssertJUnit
         assertEquals(fileInfoString(path, "child3", 3), dataSets[5].toString());
         context.assertIsSatisfied();
     }
-
+    
     private static String fileInfoString(String startPath, String pathInListing, long length)
     {
         return String.format("FileInfoDssDTO[%s/%s,%s,%d]", startPath, pathInListing,
diff --git a/screening/source/java/OpenBISScreeningML.java b/screening/source/java/OpenBISScreeningML.java
index ebc52ae0d4f..ae2418811fc 100644
--- a/screening/source/java/OpenBISScreeningML.java
+++ b/screening/source/java/OpenBISScreeningML.java
@@ -34,6 +34,7 @@ import java.util.Set;
 import java.util.TreeSet;
 
 import ch.systemsx.cisd.openbis.dss.client.api.v1.IDataSetDss;
+import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.FileInfoDssDTO;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetMetadataDTO;
 import ch.systemsx.cisd.openbis.generic.client.cli.Login;
 import ch.systemsx.cisd.openbis.plugin.screening.client.api.v1.IScreeningOpenbisServiceFacade;
@@ -290,6 +291,7 @@ public class OpenBISScreeningML
         {
             throw new RuntimeException("Couldn't create a temporary directory.");
         }
+        temporarySessionDir.deleteOnExit();
         loadedImages = new HashMap<PlateImageReference, File>();
         experiments = openbis.listExperiments();
         experimentCodeToExperimentMap.clear();
@@ -695,7 +697,7 @@ public class OpenBISScreeningML
      * 
      * <pre>
      * % Load all data sets of plate P005 in space SPACE
-     * dsinfo = OpenBISScreeningML.loadDataSets('/SPACE/P005')
+     * dsinfo = OpenBISScreeningML.loadDataSets('/SPACE/P005', '.*', '')
      * % Get the data set codes
      * dsinfo(:,1)
      * % Get root path of first data set (assuming there is at least one)
@@ -703,7 +705,7 @@ public class OpenBISScreeningML
      * </pre>
      * 
      * @param augmentedPlateCode The augmented plate code.
-     * @param datasetTypeCodePattern only datasets of the type which matche the specified pattern
+     * @param dataSetTypeCodePattern only datasets of the type which matches the specified pattern
      *            will be returned. To fetch all datasets specify ".*".
      * @param overrideStoreRootPathOrNull A path, in the context of the local file system mounts, to
      *            the DSS' store root. If null, paths are returned in the context of the DSS' file
@@ -712,13 +714,13 @@ public class OpenBISScreeningML
      *         <p>
      *         <code>{ data set code, data set root path  }</code>
      */
-    public static Object[][] loadDataSets(String augmentedPlateCode, String datasetTypeCodePattern,
+    public static Object[][] loadDataSets(String augmentedPlateCode, String dataSetTypeCodePattern,
             String overrideStoreRootPathOrNull)
     {
         checkLoggedIn();
         Plate plateIdentifier = getPlate(augmentedPlateCode);
 
-        List<IDataSetDss> dataSets = openbis.getDataSets(plateIdentifier, datasetTypeCodePattern);
+        List<IDataSetDss> dataSets = openbis.getDataSets(plateIdentifier, dataSetTypeCodePattern);
         Object[][] result = new Object[dataSets.size()][];
         try
         {
@@ -743,6 +745,84 @@ public class OpenBISScreeningML
                     + "' failed: " + ex, ex);
         }
     }
+    
+    /**
+     * Loads file/folder of specified data set and specified file/folder path inside the data set.
+     * If it is possible the path points directly into the data set store. No data is copied.
+     * Otherwise the data is retrieved from the data store server.
+     * <p>
+     * Matlab example:
+     * 
+     * <pre>
+     * % List all data sets of plate P005 in space SPACE. The query is restricted to data sets
+     * % of a type starting with HCS_IMAGE
+     * files = OpenBISScreeningML.listDataSetsFiles('/SPACE/P005', 'HCS_IMAGE.*')
+     * % Load from the first data set (assuming at least one data set found) the third file/folder 
+     * % (assuming at least three files/folders)
+     * file = OpenBISScreeningML.loadDataSetFile(files(1,1), files(1,2,3), '')
+     * </pre>
+     * 
+     * @param dataSetCode The code of the data set.
+     * @param pathInDataSet Path inside the data set pointing to the file/folder which should be
+     *            down loaded. Use '/' if all files are requested.
+     * @param overrideStoreRootPathOrNull A path, in the context of the local file system mounts, to
+     *            the DSS' store root. If null, paths are returned in the context of the DSS' file
+     *            system mounts.
+     * @return path to the down loaded file/folder.
+     */
+    public static Object loadDataSetFile(String dataSetCode, String pathInDataSet, String overrideStoreRootPathOrNull)
+    {
+        checkLoggedIn();
+        IDataSetDss dataSet = openbis.getDataSet(dataSetCode);
+        return dataSet.getLinkOrCopyOfContent(overrideStoreRootPathOrNull, temporarySessionDir, pathInDataSet).toString();
+    }
+    
+    /**
+     * Lists all files of all data sets for specifies plate and data set type code matching
+     * specified regular expression pattern. 
+     * <p>
+     * Matlab example:
+     * 
+     * <pre>
+     * % List all data sets of plate P005 in space SPACE. The query is restricted to data sets
+     * % of a type starting with HCS_IMAGE
+     * files = OpenBISScreeningML.listDataSetsFiles('/SPACE/P005', 'HCS_IMAGE.*')
+     * % Codes of all found data sets
+     * files(:,1)
+     * % Code of third data set (assuming at least three data sets found)
+     * files(3,1)
+     * % Files of third data set (assuming at least three data sets found)
+     * files(3,2,:)
+     * </pre>
+     * 
+     * @param augmentedPlateCode The augmented plate code.
+     * @param dataSetTypeCodePattern only data sets of the type which matches the specified pattern
+     *            will be returned. To fetch all data sets specify ".*".
+     * @return <code>{data set code, file/folder paths}</code>
+     */
+    public static Object[][][] listDataSetsFiles(String augmentedPlateCode, String dataSetTypeCodePattern)
+    {
+        checkLoggedIn();
+        Plate plateIdentifier = getPlate(augmentedPlateCode);
+
+        List<IDataSetDss> dataSets = openbis.getDataSets(plateIdentifier, dataSetTypeCodePattern);
+        Object[][][] result = new Object[dataSets.size()][][];
+        for (int i = 0; i < dataSets.size(); i++)
+        {
+            IDataSetDss dataSet = dataSets.get(i);
+            FileInfoDssDTO[] fileInfos = dataSet.listFiles("/", true);
+            String code = dataSet.getCode();
+            result[i] = new Object[2][];
+            result[i][0] = new Object[] {code};
+            result[i][1] = new Object[fileInfos.length];
+            for (int j = 0; j < fileInfos.length; j++)
+            {
+                FileInfoDssDTO fileInfo = fileInfos[j];
+                result[i][1][j] = fileInfo.getPathInDataSet();
+            }
+        }
+        return result;
+    }
 
     /**
      * Uploads specified data set for specified plate. The data set code will be returned.
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IScreeningOpenbisServiceFacade.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IScreeningOpenbisServiceFacade.java
index 1ed3d41ecab..40e9a0c8b98 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IScreeningOpenbisServiceFacade.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IScreeningOpenbisServiceFacade.java
@@ -173,6 +173,9 @@ public interface IScreeningOpenbisServiceFacade
     public List<IDataSetDss> getDataSets(WellIdentifier wellIdentifier,
             String datasetTypeCodePattern) throws IllegalStateException,
             EnvironmentFailureException;
+    
+    public IDataSetDss getDataSet(String dataSetCode) throws IllegalStateException,
+            EnvironmentFailureException;
 
     /**
      * Upload a new data set to the DSS for a well.
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java
index a2934019b4e..09f410524bb 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java
@@ -412,6 +412,14 @@ public class ScreeningOpenbisServiceFacade implements IScreeningOpenbisServiceFa
         final Sample wellSample = getWellSample(wellIdentifier);
         return getDataSets(wellSample, datasetTypeCodePattern);
     }
+    
+    
+
+    public IDataSetDss getDataSet(String dataSetCode) throws IllegalStateException,
+            EnvironmentFailureException
+    {
+        return dssComponent.getDataSet(dataSetCode);
+    }
 
     /**
      * Get proxies to the data sets owned by specified plate.
diff --git a/screening/sourceTest/java/OpenBISScreeningMLTest.java b/screening/sourceTest/java/OpenBISScreeningMLTest.java
index 651a5074fc2..45961ee6522 100644
--- a/screening/sourceTest/java/OpenBISScreeningMLTest.java
+++ b/screening/sourceTest/java/OpenBISScreeningMLTest.java
@@ -41,6 +41,7 @@ import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase;
 import ch.systemsx.cisd.common.filesystem.FileUtilities;
 import ch.systemsx.cisd.common.test.RecordingMatcher;
 import ch.systemsx.cisd.openbis.dss.client.api.v1.IDataSetDss;
+import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.FileInfoDssDTO;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetMetadataDTO;
 import ch.systemsx.cisd.openbis.generic.client.cli.Login;
 import ch.systemsx.cisd.openbis.plugin.screening.client.api.v1.IScreeningOpenbisServiceFacade;
@@ -535,6 +536,60 @@ public class OpenBISScreeningMLTest extends AbstractFileSystemTestCase
         context.assertIsSatisfied();
     }
 
+    @Test
+    public void testListDataSetsFiles()
+    {
+        context.checking(new Expectations()
+            {
+                {
+                    one(openbis).getDataSets(p1, ".*");
+                    will(returnValue(Arrays.asList(ds1, ds2)));
+                    
+                    one(ds1).listFiles("/", true);
+                    will(returnValue(new FileInfoDssDTO[]
+                        { new FileInfoDssDTO("a", "a", true, -1),
+                                new FileInfoDssDTO("a/b", "a/b", false, 42) }));
+                    one(ds1).getCode();
+                    will(returnValue("ds1"));
+
+                    one(ds2).listFiles("/", true);
+                    will(returnValue(new FileInfoDssDTO[]
+                        { new FileInfoDssDTO("c", "c", false, 137) }));
+                    one(ds2).getCode();
+                    will(returnValue("ds2"));
+                }
+            });
+        
+        Object[][][] files = OpenBISScreeningML.listDataSetsFiles(p1.getAugmentedCode(), ".*");
+        
+        assertEquals("ds1", files[0][0][0]);
+        assertEquals("[a, a/b]", Arrays.asList(files[0][1]).toString());
+        assertEquals("ds2", files[1][0][0]);
+        assertEquals("[c]", Arrays.asList(files[1][1]).toString());
+        assertEquals(2, files.length);
+        context.assertIsSatisfied();
+    }
+    
+    @Test
+    public void testLoadDataSetFile()
+    {
+        context.checking(new Expectations()
+            {
+                {
+                    one(openbis).getDataSet("ds1");
+                    will(returnValue(ds1));
+                    
+                    one(ds1).getLinkOrCopyOfContent("root", tempDir, "a/b/c");
+                    will(returnValue(new File("data")));
+                }
+            });
+        
+        Object file = OpenBISScreeningML.loadDataSetFile("ds1", "a/b/c", "root");
+        
+        assertEquals("data", file);
+        context.assertIsSatisfied();
+    }
+    
     @Test
     public void testLoadDataSets()
     {
-- 
GitLab