diff --git a/screening/etc/service.properties b/screening/etc/service.properties
index 7f7a322157ba0688a5e5a1c0492aecf1b2c7a571..32409fbca4b575a15d9028978454ad3df55bb7ea 100644
--- a/screening/etc/service.properties
+++ b/screening/etc/service.properties
@@ -79,25 +79,7 @@ quiet-period = 3
 # ---------------------------------------------------------------------------
 
 # Comma separated names of reporting plugins. Each plugin should have configuration properties prefixed with its name.
-reporting-plugins = plate-image-reporter, plate-image-params-reporter, default-plate-image-analysis-merger, plate-image-analysis-graph, demo-reporter
-
-# Label of the plugin which will be shown for the users.
-demo-reporter.label = Show Dataset Size
-# Comma separated list of dataset type codes which can be handled by this plugin.
-demo-reporter.dataset-types = UNKNOWN
-# Plugin class specification (together with the list of packages this class belongs to).
-demo-reporter.class = ch.systemsx.cisd.openbis.dss.generic.server.plugins.demo.DemoReportingPlugin
-# The property file. Its content will be passed as a parameter to the plugin.
-demo-reporter.properties-file = 
-
-# Label of the plugin which will be shown for the users.
-plate-image-reporter.label = Show Plate Images
-# Comma separated list of dataset type codes which can be handled by this plugin.
-plate-image-reporter.dataset-types = HCS_IMAGE
-# Plugin class specification (together with the list of packages this class belongs to).
-plate-image-reporter.class = ch.systemsx.cisd.openbis.dss.generic.server.plugins.ScreeningImageReportingPlugin
-# The property file. Its content will be passed as a parameter to the plugin.
-plate-image-reporter.properties-file = 
+reporting-plugins = plate-image-params-reporter, default-plate-image-analysis-merger, plate-image-analysis-graph
 
 # Label of the plugin which will be shown for the users.
 plate-image-params-reporter.label = Show Plate Image Parameters
@@ -169,7 +151,7 @@ imaging-db.scriptFolder = source/sql
 
 # Comma separated names of processing threads. Each thread should have configuration properties prefixed with its name.
 # E.g. 'code-extractor' property for the thread 'my-etl' should be specified as 'my-etl.code-extractor'
-inputs=raw-data, plate-analysis-data, lmc-raw-data, lmc-jpg-data, lmc-segmented-jpg-data, image-db-data
+inputs=raw-data, plate-analysis-data, lmc-raw-data, lmc-jpg-data, lmc-segmented-jpg-data, image-db-data, pelk-raw-img-db, lmc-raw-img-db
 
 # ---------------------------------------------------------------------------
 # 'raw-data' thread configuration
@@ -377,6 +359,62 @@ image-db-data.storage-processor.well_geometry = 1x1
 image-db-data.storage-processor.deprecated-file-extractor = ch.systemsx.cisd.openbis.dss.etl.genedata.HCSImageFileExtractor
 image-db-data.storage-processor.data-source = imaging-db
 
+# ---------------------------------------------------------------------------
+
+# The directory to watch for incoming data.
+pelk-raw-img-db.incoming-dir = targets/raw-images-pelk-db
+pelk-raw-img-db.incoming-data-completeness-condition = auto-detection
+
+# The extractor class to use for code extraction
+pelk-raw-img-db.data-set-info-extractor = ch.systemsx.cisd.etlserver.DefaultDataSetInfoExtractor
+pelk-raw-img-db.data-set-info-extractor.entity-separator = ${data-set-file-name-entity-separator}
+pelk-raw-img-db.data-set-info-extractor.index-of-sample-code = -1
+pelk-raw-img-db.data-set-info-extractor.index-of-data-producer-code = 1
+pelk-raw-img-db.data-set-info-extractor.index-of-data-production-date = 0
+pelk-raw-img-db.data-set-info-extractor.space-code = DEMO
+
+# The extractor class to use for type extraction
+pelk-raw-img-db.type-extractor = ch.systemsx.cisd.etlserver.SimpleTypeExtractor
+pelk-raw-img-db.type-extractor.file-format-type = PNG
+pelk-raw-img-db.type-extractor.locator-type = RELATIVE_LOCATION
+pelk-raw-img-db.type-extractor.data-set-type = HCS_IMAGE
+pelk-raw-img-db.type-extractor.is-measured = true
+
+pelk-raw-img-db.storage-processor = ch.systemsx.cisd.openbis.dss.etl.PlateStorageProcessor
+pelk-raw-img-db.storage-processor.channel-names = blue, green
+pelk-raw-img-db.storage-processor.well_geometry = 3x3
+pelk-raw-img-db.storage-processor.deprecated-file-extractor = ch.systemsx.cisd.etlserver.imsb.HCSImageFileExtractor
+pelk-raw-img-db.storage-processor.data-source = imaging-db
+
+
+# ---------------------------------------------------------------------------
+
+# The directory to watch for incoming data.
+lmc-raw-img-db.incoming-dir = targets/raw-images-lmc-db
+lmc-raw-img-db.incoming-data-completeness-condition = auto-detection
+
+# The extractor class to use for code extraction
+lmc-raw-img-db.data-set-info-extractor = ch.systemsx.cisd.etlserver.DefaultDataSetInfoExtractor
+lmc-raw-img-db.data-set-info-extractor.entity-separator = .
+lmc-raw-img-db.data-set-info-extractor.index-of-sample-code = 0
+lmc-raw-img-db.data-set-info-extractor.index-of-data-producer-code = 
+lmc-raw-img-db.data-set-info-extractor.space-code = DEMO
+
+# The extractor class to use for type extraction
+lmc-raw-img-db.type-extractor = ch.systemsx.cisd.etlserver.SimpleTypeExtractor
+lmc-raw-img-db.type-extractor.file-format-type = JPG
+lmc-raw-img-db.type-extractor.locator-type = RELATIVE_LOCATION
+lmc-raw-img-db.type-extractor.data-set-type = HCS_IMAGE
+lmc-raw-img-db.type-extractor.is-measured = true
+
+lmc-raw-img-db.storage-processor = ch.systemsx.cisd.openbis.dss.etl.PlateStorageProcessor
+lmc-raw-img-db.storage-processor.channel-names = dapi, gfp
+lmc-raw-img-db.storage-processor.well_geometry = 3x3
+lmc-raw-img-db.storage-processor.deprecated-file-extractor = ch.systemsx.cisd.openbis.dss.etl.lmc.HCSImageFileExtractor
+lmc-raw-img-db.storage-processor.data-source = imaging-db
+#lmc-raw-img-db.storage-processor.
+#lmc-raw-img-db.storage-processor.compute_file_checksums = false
+
 # ---------------------------------------------------------------------------
 # (optional) archiver configuration
 # ---------------------------------------------------------------------------
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbsoluteImageReference.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbsoluteImageReference.java
new file mode 100644
index 0000000000000000000000000000000000000000..0d225fa9feccd6beac8c15101949fb3fe03a0570
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbsoluteImageReference.java
@@ -0,0 +1,55 @@
+/*
+ * 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.etl;
+
+import ch.systemsx.cisd.openbis.dss.etl.dataaccess.ImgImageDTO.ColorComponent;
+
+/**
+ * @author Tomasz Pylak
+ */
+public class AbsoluteImageReference
+{
+    private final String imageAbsolutePath;
+
+    private final Integer pageOrNull;
+
+    private final ColorComponent colorComponentOrNull;
+
+    public AbsoluteImageReference(String imageAbsolutePath, Integer pageOrNull,
+            ColorComponent colorComponentOrNull)
+    {
+        this.imageAbsolutePath = imageAbsolutePath;
+        this.pageOrNull = pageOrNull;
+        this.colorComponentOrNull = colorComponentOrNull;
+    }
+
+    public String getImageAbsolutePath()
+    {
+        return imageAbsolutePath;
+    }
+
+    public Integer tryGetPage()
+    {
+        return pageOrNull;
+    }
+
+    public ColorComponent tryGetColorComponent()
+    {
+        return colorComponentOrNull;
+    }
+
+}
\ No newline at end of file
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSDatasetLoaderFactory.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSDatasetLoaderFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..a7568cbf59e6378bc8b92893777cb506a6f1838d
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSDatasetLoaderFactory.java
@@ -0,0 +1,100 @@
+/*
+ * 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.etl;
+
+import java.io.File;
+
+import javax.sql.DataSource;
+
+import net.lemnik.eodsql.QueryTool;
+
+import ch.systemsx.cisd.bds.hcs.Geometry;
+import ch.systemsx.cisd.bds.hcs.Location;
+import ch.systemsx.cisd.openbis.dss.etl.dataaccess.HCSDatasetLoader;
+import ch.systemsx.cisd.openbis.dss.etl.dataaccess.IImagingUploadDAO;
+import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ScreeningConstants;
+
+/**
+ * @author Tomasz Pylak
+ */
+public class HCSDatasetLoaderFactory
+{
+    private static final IImagingUploadDAO query = createQuery();
+
+    private static IImagingUploadDAO createQuery()
+    {
+        DataSource dataSource =
+                ServiceProvider.getDataSourceProvider().getDataSource(
+                        ScreeningConstants.IMAGING_DATA_SOURCE);
+        return QueryTool.getQuery(dataSource, IImagingUploadDAO.class);
+    }
+
+    public static final IHCSDatasetLoader create(File datasetRootDir, String datasetCode)
+    {
+        return createImageDBLoader(datasetRootDir, datasetCode);
+        // return createBDSLoader(datasetRootDir);
+    }
+
+    private static HCSDatasetLoader createImageDBLoader(File datasetRootDir, String datasetCode)
+    {
+        return new HCSDatasetLoader(query, datasetCode, datasetRootDir);
+    }
+
+    private static IHCSDatasetLoader createBDSLoader(File datasetRootDir)
+    {
+        final ch.systemsx.cisd.bds.hcs.HCSDatasetLoader loader =
+                new ch.systemsx.cisd.bds.hcs.HCSDatasetLoader(datasetRootDir);
+        return adapt(loader);
+    }
+
+    private static IHCSDatasetLoader adapt(final ch.systemsx.cisd.bds.hcs.HCSDatasetLoader loader)
+    {
+        return new IHCSDatasetLoader()
+            {
+
+                public void close()
+                {
+                    loader.close();
+                }
+
+                public int getChannelCount()
+                {
+                    return loader.getChannelCount();
+                }
+
+                public Geometry getPlateGeometry()
+                {
+                    return loader.getPlateGeometry();
+                }
+
+                public Geometry getWellGeometry()
+                {
+                    return loader.getWellGeometry();
+                }
+
+                public AbsoluteImageReference tryGetImage(int chosenChannel, Location wellLocation,
+                        Location tileLocation)
+                {
+                    String absPath =
+                            loader.tryGetStandardNodeAt(chosenChannel, wellLocation, tileLocation);
+                    return new AbsoluteImageReference(absPath, null, null);
+                }
+
+            };
+    }
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSDatasetUploader.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSDatasetUploader.java
index 07603feebe67fd071c55b83e34b6f1d204fb0f31..6bb1120e83769a1b2e5def7063deeebe124d4efa 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSDatasetUploader.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSDatasetUploader.java
@@ -24,6 +24,7 @@ import java.util.Set;
 import java.util.Map.Entry;
 
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.openbis.dss.etl.HCSImageFileExtractionResult.Channel;
 import ch.systemsx.cisd.openbis.dss.etl.dataaccess.IImagingUploadDAO;
 import ch.systemsx.cisd.openbis.dss.etl.dataaccess.ImgAcquiredImageDTO;
 import ch.systemsx.cisd.openbis.dss.etl.dataaccess.ImgChannelDTO;
@@ -66,17 +67,45 @@ class HCSDatasetUploader
     private Map<String, Long> getOrCreateChannels(long expId,
             Set<HCSImageFileExtractionResult.Channel> channels)
     {
-        Map<String/* name */, ImgChannelDTO> existingChannels =
-                asNameMap(dao.getChannelsByExperimentId(expId));
+        List<ImgChannelDTO> allChannels = dao.getChannelsByExperimentId(expId);
+        if (allChannels.size() == 0)
+        {
+            return createChannels(expId, channels);
+        } else
+        {
+            return updateChannels(expId, channels, allChannels);
+        }
+    }
+
+    private Map<String, Long> updateChannels(long expId, Set<Channel> channels,
+            List<ImgChannelDTO> allChannels)
+    {
+        Map<String/* name */, ImgChannelDTO> existingChannels = asNameMap(allChannels);
+        Map<String, Long> map = new HashMap<String, Long>();
+        for (HCSImageFileExtractionResult.Channel channel : channels)
+        {
+            ImgChannelDTO channelDTO = updateChannel(channel, expId, existingChannels);
+            addChannel(map, channelDTO);
+        }
+        return map;
+    }
+
+    private Map<String, Long> createChannels(long expId, Set<Channel> channels)
+    {
         Map<String, Long> map = new HashMap<String, Long>();
         for (HCSImageFileExtractionResult.Channel channel : channels)
         {
-            ImgChannelDTO channelDTO = getOrCreateChannel(channel, expId, existingChannels);
-            map.put(channelDTO.getName(), channelDTO.getId());
+            ImgChannelDTO channelDTO = createChannel(expId, channel);
+            addChannel(map, channelDTO);
         }
         return map;
     }
 
+    private static void addChannel(Map<String, Long> map, ImgChannelDTO channelDTO)
+    {
+        map.put(channelDTO.getName(), channelDTO.getId());
+    }
+
     private static Map<String, ImgChannelDTO> asNameMap(List<ImgChannelDTO> channels)
     {
         Map<String, ImgChannelDTO> nameMap = new HashMap<String, ImgChannelDTO>();
@@ -87,37 +116,51 @@ class HCSDatasetUploader
         return nameMap;
     }
 
-    private ImgChannelDTO getOrCreateChannel(HCSImageFileExtractionResult.Channel channel,
-            long expId, Map<String, ImgChannelDTO> existingChannels)
+    private ImgChannelDTO updateChannel(HCSImageFileExtractionResult.Channel channel, long expId,
+            Map<String, ImgChannelDTO> existingChannels)
     {
-        ImgChannelDTO channelDTO = createChannelDTO(channel, expId);
-        ImgChannelDTO existingChannel = existingChannels.get(channelDTO.getName());
-        if (existingChannel != null)
+        ImgChannelDTO channelDTO = makeChannelDTO(channel, expId);
+        String channelName = channelDTO.getName();
+        ImgChannelDTO existingChannel = existingChannels.get(channelName);
+        if (existingChannel == null)
         {
-            // if a channel with a specified name already exists for an experiment, its description
-            // will be updated
-            if (existingChannel.getWavelength() != channelDTO.getWavelength())
-            {
-                throw UserFailureException
-                        .fromTemplate(
-                                "There are already datasets registered for the experiment "
-                                        + "which use the same channel name, but with a different wavelength! "
-                                        + "Channel %s, old wavelength %d, new wavelength %d.",
-                                channelDTO.getName(), existingChannel.getWavelength(), channelDTO
-                                        .getWavelength());
-            }
-            channelDTO.setId(existingChannel.getId());
-            dao.updateChannel(channelDTO);
-        } else
+            throw createInvalidNewChannelException(expId, existingChannels, channelName);
+        }
+        // a channel with a specified name already exists for an experiment, its description
+        // will be updated
+        if (existingChannel.getWavelength().equals(channelDTO.getWavelength()) == false)
         {
-            // if a channel with a specified name does not exists for an experiment, it's created
-            long channelId = dao.addChannel(channelDTO);
-            channelDTO.setId(channelId);
+            throw UserFailureException.fromTemplate(
+                    "There are already datasets registered for the experiment "
+                            + "which use the same channel name, but with a different wavelength! "
+                            + "Channel %s, old wavelength %d, new wavelength %d.", channelName,
+                    existingChannel.getWavelength(), channelDTO.getWavelength());
         }
+        channelDTO.setId(existingChannel.getId());
+        dao.updateChannel(channelDTO);
+        return channelDTO;
+    }
+
+    private static UserFailureException createInvalidNewChannelException(long expId,
+            Map<String, ImgChannelDTO> existingChannels, String channelName)
+    {
+        return UserFailureException.fromTemplate(
+                "Experiment with id '%d' has already some channels registered "
+                        + "and does not have a channel with a name '%s'. "
+                        + "Register a new experiment to use new channels. "
+                        + "Available channel names in this experiment: %s.", expId, channelName,
+                existingChannels.keySet());
+    }
+
+    private ImgChannelDTO createChannel(long expId, HCSImageFileExtractionResult.Channel channel)
+    {
+        ImgChannelDTO channelDTO = makeChannelDTO(channel, expId);
+        long channelId = dao.addChannel(channelDTO);
+        channelDTO.setId(channelId);
         return channelDTO;
     }
 
-    private static ImgChannelDTO createChannelDTO(HCSImageFileExtractionResult.Channel channel,
+    private static ImgChannelDTO makeChannelDTO(HCSImageFileExtractionResult.Channel channel,
             long expId)
     {
         return ImgChannelDTO.createExperimentChannel(channel.getName(), channel.getDescription(),
@@ -156,7 +199,7 @@ class HCSDatasetUploader
         createImages(stackImagesMap, channelsMap);
     }
 
-    private Map<ImgChannelStackDTO, List<AcquiredImageInStack>> makeStackImagesMap(
+    private static Map<ImgChannelStackDTO, List<AcquiredImageInStack>> makeStackImagesMap(
             List<AcquiredPlateImage> images, Long[][] spotIds, long datasetId)
     {
         Map<ImgChannelStackDTO, List<AcquiredImageInStack>> map =
@@ -260,8 +303,8 @@ class HCSDatasetUploader
         return newSpots;
     }
 
-    private Boolean[][] extractNewSpots(int rows, int columns, List<AcquiredPlateImage> images,
-            List<ImgSpotDTO> existingSpots)
+    private static Boolean[][] extractNewSpots(int rows, int columns,
+            List<AcquiredPlateImage> images, List<ImgSpotDTO> existingSpots)
     {
         Boolean[][] spots = extractExistingSpots(rows, columns, images);
         unmarkSpots(existingSpots, spots);
@@ -312,15 +355,15 @@ class HCSDatasetUploader
     {
         for (ImgSpotDTO existingSpot : existingSpots)
         {
-            spotMatrix[existingSpot.getRow()][existingSpot.getColumn()] = false;
+            spotMatrix[existingSpot.getRow() - 1][existingSpot.getColumn() - 1] = false;
         }
     }
 
     private long createDataset(long contId, ScreeningContainerDatasetInfo info)
     {
         ImgDatasetDTO dataset =
-                new ImgDatasetDTO(info.getDatasetPermId(), info.getTileRows(), info
-                        .getTileColumns(), contId);
+                new ImgDatasetDTO(info.getDatasetPermId(), info.getRelativeImagesDirectory(), info
+                        .getTileRows(), info.getTileColumns(), contId);
         return dao.addDataset(dataset);
     }
 
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSImageCheckList.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSImageCheckList.java
index 70b6872723a9490090b889088d7d2cdbd1cd62ad..7ee65431e9f9cab8b0cfe244cde7fbef94fd390b 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSImageCheckList.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSImageCheckList.java
@@ -23,6 +23,7 @@ import java.util.Map;
 
 import ch.systemsx.cisd.bds.hcs.Geometry;
 import ch.systemsx.cisd.common.utilities.AbstractHashable;
+import ch.systemsx.cisd.etlserver.PlateDimension;
 
 /**
  * Helper class to set the <code>is_complete</code> flag in the <i>BDS</i> library.
@@ -38,7 +39,7 @@ public final class HCSImageCheckList
 
     private final Map<FullLocation, Check> imageMap;
 
-    public HCSImageCheckList(final String[] channelNames, final Geometry plateGeometry,
+    public HCSImageCheckList(final String[] channelNames, final PlateDimension plateGeometry,
             final Geometry wellGeometry)
     {
         if (channelNames.length < 1)
@@ -56,9 +57,9 @@ public final class HCSImageCheckList
         imageMap = new HashMap<FullLocation, Check>();
         for (String channelName : channelNames)
         {
-            for (int wellCol = 1; wellCol <= plateGeometry.getColumns(); wellCol++)
+            for (int wellCol = 1; wellCol <= plateGeometry.getColsNum(); wellCol++)
             {
-                for (int wellRow = 1; wellRow <= plateGeometry.getRows(); wellRow++)
+                for (int wellRow = 1; wellRow <= plateGeometry.getRowsNum(); wellRow++)
                 {
                     for (int tileCol = 1; tileCol <= wellGeometry.getColumns(); tileCol++)
                     {
@@ -71,8 +72,8 @@ public final class HCSImageCheckList
                 }
             }
         }
-        assert imageMap.size() == channelNames.length * plateGeometry.getColumns()
-                * plateGeometry.getRows() * wellGeometry.getColumns() * wellGeometry.getRows() : "Wrong map size";
+        assert imageMap.size() == channelNames.length * plateGeometry.getColsNum()
+                * plateGeometry.getRowsNum() * wellGeometry.getColumns() * wellGeometry.getRows() : "Wrong map size";
     }
 
     public final void checkOff(AcquiredPlateImage image)
@@ -147,7 +148,7 @@ public final class HCSImageCheckList
 
         private final static String toString(final int row, final int col, final String type)
         {
-            return type + "= (" + row + ", " + col + ")";
+            return type + "=(" + row + "," + col + ")";
         }
 
         //
@@ -157,7 +158,7 @@ public final class HCSImageCheckList
         @Override
         public final String toString()
         {
-            return "[ channel = " + channelName + "," + toString(wellRow, wellCol, "well") + ","
+            return "[channel=" + channelName + ", " + toString(wellRow, wellCol, "well") + ", "
                     + toString(tileRow, tileCol, "tile") + "]";
         }
     }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/IHCSDatasetLoader.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/IHCSDatasetLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..fb3fab8b5a9c27a35b5980589c32ca5e23d2eca3
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/IHCSDatasetLoader.java
@@ -0,0 +1,43 @@
+/*
+ * 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.etl;
+
+import ch.systemsx.cisd.bds.hcs.Geometry;
+import ch.systemsx.cisd.bds.hcs.Location;
+
+/**
+ * @author Tomasz Pylak
+ */
+public interface IHCSDatasetLoader
+{
+
+    /** has to be called at the end */
+    void close();
+
+    Geometry getPlateGeometry();
+
+    Geometry getWellGeometry();
+
+    int getChannelCount();
+
+    /**
+     * @param chosenChannel start from 1
+     * @return image (with absolute path, page and color)
+     */
+    AbsoluteImageReference tryGetImage(int chosenChannel, Location wellLocation,
+            Location tileLocation);
+}
\ No newline at end of file
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PlateStorageProcessor.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PlateStorageProcessor.java
index 37f1f3eb844631bb6200da552cd6b2254b714214..72b8be9eaf574e62dd91ce94f8c1701caf41970f 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PlateStorageProcessor.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PlateStorageProcessor.java
@@ -35,7 +35,6 @@ import org.apache.log4j.Logger;
 import ch.systemsx.cisd.bds.hcs.Channel;
 import ch.systemsx.cisd.bds.hcs.Geometry;
 import ch.systemsx.cisd.bds.hcs.Location;
-import ch.systemsx.cisd.bds.hcs.PlateGeometry;
 import ch.systemsx.cisd.bds.storage.IFile;
 import ch.systemsx.cisd.bds.storage.filesystem.NodeFactory;
 import ch.systemsx.cisd.common.collections.CollectionUtils;
@@ -168,8 +167,8 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor
 
     // ---------------------------------
 
-    private ScreeningContainerDatasetInfo createScreeningDatasetInfo(final Experiment experiment,
-            final DataSetInformation dataSetInformation)
+    private ScreeningContainerDatasetInfo createScreeningDatasetInfo(Experiment experiment,
+            DataSetInformation dataSetInformation, String relativeImagesDirectory)
     {
         ScreeningContainerDatasetInfo info = new ScreeningContainerDatasetInfo();
         info.setExperimentPermId(experiment.getPermId());
@@ -179,19 +178,21 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor
         info.setContainerPermId(sample.getPermId());
         info.setDatasetPermId(dataSetInformation.getDataSetCode());
 
-        Geometry plateGeometry = getPlateGeometry(dataSetInformation);
-        int plateRows = plateGeometry.getRows();
-        int plateCols = plateGeometry.getColumns();
+        PlateDimension plateGeometry = getPlateGeometry(dataSetInformation);
+        int plateRows = plateGeometry.getRowsNum();
+        int plateCols = plateGeometry.getColsNum();
         info.setContainerRows(plateRows);
         info.setContainerColumns(plateCols);
 
         info.setTileRows(spotGeometry.getRows());
         info.setTileColumns(spotGeometry.getColumns());
 
+        info.setRelativeImagesDirectory(relativeImagesDirectory);
+
         return info;
     }
 
-    private Geometry getPlateGeometry(final DataSetInformation dataSetInformation)
+    private PlateDimension getPlateGeometry(final DataSetInformation dataSetInformation)
     {
         final IEntityProperty[] sampleProperties = dataSetInformation.getProperties();
         final PlateDimension plateDimension =
@@ -202,9 +203,7 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor
                     "Missing plate geometry for the plate registered for sample identifier '"
                             + dataSetInformation.getSampleIdentifier() + "'.");
         }
-        final Geometry plateGeometry =
-                new PlateGeometry(plateDimension.getRowsNum(), plateDimension.getColsNum());
-        return plateGeometry;
+        return plateDimension;
     }
 
     // ---------------------------------
@@ -275,22 +274,33 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor
         HCSImageFileExtractionResult extractionResult =
                 extractImages(dataSetInformation, incomingDataSetDirectory);
 
-        ScreeningContainerDatasetInfo info =
-                createScreeningDatasetInfo(experiment, dataSetInformation);
+        validateImages(dataSetInformation, mailClient, incomingDataSetDirectory, extractionResult);
 
-        validateImages(dataSetInformation, mailClient, incomingDataSetDirectory, info,
-                extractionResult);
+        File imagesInStoreFolder = moveFileToDirectory(incomingDataSetDirectory, originalFolder);
+        String relativeImagesDirectory =
+                getRelativeImagesDirectory(rootDirectory, imagesInStoreFolder);
 
-        moveFileToDirectory(incomingDataSetDirectory, originalFolder);
-        storeInDatabase(info, extractionResult.getImages(), extractionResult.getChannels());
+        storeInDatabase(experiment, dataSetInformation, extractionResult, relativeImagesDirectory);
         return rootDirectory;
     }
 
+    private String getRelativeImagesDirectory(File rootDirectory, File imagesInStoreFolder)
+    {
+        String root = rootDirectory.getAbsolutePath();
+        String imgDir = imagesInStoreFolder.getAbsolutePath();
+        if (imgDir.startsWith(root) == false)
+        {
+            throw UserFailureException.fromTemplate(
+                    "Directory %s should be a subdirectory of directory %s.", imgDir, root);
+        }
+        return imgDir.substring(root.length());
+    }
+
     private void validateImages(final DataSetInformation dataSetInformation,
             final IMailClient mailClient, final File incomingDataSetDirectory,
-            ScreeningContainerDatasetInfo info, HCSImageFileExtractionResult extractionResult)
+            HCSImageFileExtractionResult extractionResult)
     {
-        HCSImageCheckList imageCheckList = createImageCheckList(info);
+        HCSImageCheckList imageCheckList = createImageCheckList(dataSetInformation);
         checkImagesForDuplicates(extractionResult, imageCheckList);
         if (extractionResult.getInvalidFiles().size() > 0)
         {
@@ -318,12 +328,10 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor
         }
     }
 
-    private HCSImageCheckList createImageCheckList(ScreeningContainerDatasetInfo info)
+    private HCSImageCheckList createImageCheckList(DataSetInformation dataSetInformation)
     {
-        Geometry plateGeometry = getPlateGeometry(info);
-        HCSImageCheckList imageCheckList =
-                new HCSImageCheckList(channelNames, plateGeometry, spotGeometry);
-        return imageCheckList;
+        PlateDimension plateGeometry = getPlateGeometry(dataSetInformation);
+        return new HCSImageCheckList(channelNames, plateGeometry, spotGeometry);
     }
 
     private HCSImageFileExtractionResult extractImages(final DataSetInformation dataSetInformation,
@@ -386,6 +394,12 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor
         checkParameters(incomingDataSetDirectory, storedDataDirectory);
 
         final File originalDataFile = tryGetProprietaryData(storedDataDirectory);
+        if (originalDataFile == null)
+        {
+            // nothing has been stored in the file system yet,
+            // e.g. because images could not be validated
+            return;
+        }
         // Move the data from the 'original' directory back to the 'incoming' directory.
         final File incomingDirectory = incomingDataSetDirectory.getParentFile();
         try
@@ -420,22 +434,27 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor
         }
     }
 
-    private void storeInDatabase(ScreeningContainerDatasetInfo info,
-            List<AcquiredPlateImage> images, Set<HCSImageFileExtractionResult.Channel> channels)
+    private void storeInDatabase(Experiment experiment, DataSetInformation dataSetInformation,
+            HCSImageFileExtractionResult extractionResult, String relativeImagesDirectory)
     {
+        ScreeningContainerDatasetInfo info =
+                createScreeningDatasetInfo(experiment, dataSetInformation, relativeImagesDirectory);
+
         if (currentTransaction != null)
         {
             throw new IllegalStateException("previous transaction has not been commited!");
         }
         currentTransaction = createQuery();
-        HCSDatasetUploader.upload(currentTransaction, info, images, channels);
+
+        HCSDatasetUploader.upload(currentTransaction, info, extractionResult.getImages(),
+                extractionResult.getChannels());
     }
 
     private void rollbackDatabaseChanges()
     {
         if (currentTransaction == null)
         {
-            throw new IllegalStateException("there is no transaction to rollback");
+            return; // storing in the imaging db has not started
         }
         try
         {
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ScreeningContainerDatasetInfo.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ScreeningContainerDatasetInfo.java
index 46a5705dbdf843bfd5a03f051543d31d1adc41ba..8c2de3f9987c3f2ca76926c96028eba4e978c0da 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ScreeningContainerDatasetInfo.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ScreeningContainerDatasetInfo.java
@@ -33,6 +33,10 @@ class ScreeningContainerDatasetInfo
 
     private int tileRows, tileColumns;
 
+    // All images paths are relative to the IMAGES_DIRECTORY folder.
+    // The folder path itself is relative to the dataset root directory.
+    private String relativeImagesDirectory;
+
     public String getExperimentPermId()
     {
         return experimentPermId;
@@ -103,4 +107,13 @@ class ScreeningContainerDatasetInfo
         this.tileColumns = tileColumns;
     }
 
+    public String getRelativeImagesDirectory()
+    {
+        return relativeImagesDirectory;
+    }
+
+    public void setRelativeImagesDirectory(String relativeImagesDirectory)
+    {
+        this.relativeImagesDirectory = relativeImagesDirectory;
+    }
 }
\ No newline at end of file
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/HCSDatasetLoader.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/HCSDatasetLoader.java
index c4742d1454a76c272ed0285deb757638d51da7a0..14210b9c8e72360d3aa15f77f3a2969a759eae4d 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/HCSDatasetLoader.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/HCSDatasetLoader.java
@@ -16,14 +16,12 @@
 
 package ch.systemsx.cisd.openbis.dss.etl.dataaccess;
 
-import java.sql.Connection;
-import java.sql.SQLException;
-
-import net.lemnik.eodsql.QueryTool;
+import java.io.File;
 
 import ch.systemsx.cisd.bds.hcs.Geometry;
 import ch.systemsx.cisd.bds.hcs.Location;
-import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.openbis.dss.etl.AbsoluteImageReference;
+import ch.systemsx.cisd.openbis.dss.etl.IHCSDatasetLoader;
 
 /**
  * Helper class for easy handling of HCS image dataset standard structure.
@@ -31,26 +29,26 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException;
  * @author Tomasz Pylak
  * @author Piotr Buczek
  */
-public class HCSDatasetLoader
+public class HCSDatasetLoader implements IHCSDatasetLoader
 {
+    private final File datasetRootDir;
+
     private final IImagingUploadDAO query;
 
     private final ImgDatasetDTO dataset;
 
     private ImgContainerDTO container;
 
-    private Long channelCount;
+    private Integer channelCount;
 
-    /**
-     * @exception SQLException if a database access error occurs
-     */
-    public HCSDatasetLoader(Connection connection, String datasetPermId) throws SQLException
+    public HCSDatasetLoader(IImagingUploadDAO query, String datasetPermId, File datasetRootDir)
     {
-        this.query = QueryTool.getQuery(connection, IImagingUploadDAO.class);
+        this.datasetRootDir = datasetRootDir;
+        this.query = query;
         this.dataset = query.tryGetDatasetByPermId(datasetPermId);
         if (dataset == null)
         {
-            throw UserFailureException.fromTemplate("Dataset '%s' not found", datasetPermId);
+            throw new IllegalStateException(String.format("Dataset '%s' not found", datasetPermId));
         }
     }
 
@@ -85,7 +83,7 @@ public class HCSDatasetLoader
                 .getFieldNumberOfColumns());
     }
 
-    public long getChannelCount()
+    public int getChannelCount()
     {
         if (channelCount == null)
         {
@@ -100,7 +98,7 @@ public class HCSDatasetLoader
      * @param chosenChannel start from 1
      * @return image (with absolute path, page and color)
      */
-    public ImgImageDTO tryGetStandardNodeAt(int chosenChannel, Location wellLocation,
+    public AbsoluteImageReference tryGetImage(int chosenChannel, Location wellLocation,
             Location tileLocation)
     {
         assert chosenChannel > 0;
@@ -116,6 +114,23 @@ public class HCSDatasetLoader
 
         long chosenChannelId = channelIds[chosenChannel - 1];
 
-        return query.getImage(chosenChannelId, getDataset().getId(), tileLocation, wellLocation);
+        ImgImageDTO imageDTO =
+                query
+                        .tryGetImage(chosenChannelId, getDataset().getId(), tileLocation,
+                                wellLocation);
+        if (imageDTO != null)
+        {
+            return new AbsoluteImageReference(getAbsolutePath(imageDTO), imageDTO.getPage(),
+                    imageDTO.getColorComponent());
+        } else
+        {
+            return null;
+        }
+    }
+
+    private String getAbsolutePath(ImgImageDTO imageDTO)
+    {
+        File imgDir = new File(datasetRootDir, dataset.getImagesDirectoryPath());
+        return new File(imgDir, imageDTO.getFilePath()).getAbsolutePath();
     }
 }
\ No newline at end of file
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/IImagingUploadDAO.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/IImagingUploadDAO.java
index 299b774adea26d6ea9950bb95f286a8b69e9ab62..54ece75b656834d9629a8d429dd960d2d930af5f 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/IImagingUploadDAO.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/IImagingUploadDAO.java
@@ -31,8 +31,7 @@ public interface IImagingUploadDAO extends TransactionQuery
 {
     public static final int FETCH_SIZE = 1000;
 
-    // select acquired_images.images.*
-    // from acquired_images
+    // select acquired_images.images.* from acquired_images
     @Select("select i.* from IMAGES i, ACQUIRED_IMAGES ai, CHANNEL_STACKS cs, SPOTS s "
             + "where                                                                "
             // where acquired_images.channel.id = ?{channelId}
@@ -45,8 +44,7 @@ public interface IImagingUploadDAO extends TransactionQuery
             + "cs.x = ?{3.x} and cs.y = ?{3.y} and s.x = ?{4.x} and s.y = ?{4.y} and "
             // joins
             + "ai.IMG_ID = i.ID and ai.CHANNEL_STACK_ID = cs.ID and cs.SPOT_ID = s.ID")
-    // TODO 2010-05-19, Tomasz Pylak: add unit tests
-    public ImgImageDTO getImage(long channelId, long datasetId, Location tileLocation,
+    public ImgImageDTO tryGetImage(long channelId, long datasetId, Location tileLocation,
             Location wellLocation);
 
     // simple getters
@@ -64,7 +62,7 @@ public interface IImagingUploadDAO extends TransactionQuery
     public ImgContainerDTO getContainerById(long containerId);
 
     @Select("select count(*) from CHANNELS where DS_ID = ?{1} or EXP_ID = ?{2}")
-    public long countChannelByDatasetIdOrExperimentId(long datasetId, long experimentId);
+    public int countChannelByDatasetIdOrExperimentId(long datasetId, long experimentId);
 
     @Select(sql = "select id from CHANNELS where DS_ID = ?{1} or EXP_ID = ?{2} order by name", fetchSize = FETCH_SIZE)
     public long[] getChannelIdsByDatasetIdOrExperimentId(long datasetId, long experimentId);
@@ -96,8 +94,9 @@ public interface IImagingUploadDAO extends TransactionQuery
             + "(?{1.permId}, ?{1.numberOfColumns}, ?{1.numberOfRows}, ?{1.experimentId}) returning ID")
     public long addContainer(ImgContainerDTO container);
 
-    @Select("insert into DATA_SETS (PERM_ID, FIELDS_WIDTH, FIELDS_HEIGHT, CONT_ID) values "
-            + "(?{1.permId}, ?{1.fieldNumberOfColumns}, ?{1.fieldNumberOfRows}, ?{1.containerId}) returning ID")
+    @Select("insert into DATA_SETS (PERM_ID, IMAGES_DIRECTORY, FIELDS_WIDTH, FIELDS_HEIGHT, CONT_ID) values "
+            + "(?{1.permId}, ?{1.imagesDirectoryPath}, ?{1.fieldNumberOfColumns}, "
+            + "?{1.fieldNumberOfRows}, ?{1.containerId}) returning ID")
     public long addDataset(ImgDatasetDTO dataset);
 
     @Select("insert into IMAGES (PATH, PAGE, COLOR) values "
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgDatasetDTO.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgDatasetDTO.java
index 18a1ee6d1090b95769835af1ab91297690146f5f..f53826a611d55ada0bd909c8d37a3ed6f16b5372 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgDatasetDTO.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgDatasetDTO.java
@@ -32,6 +32,9 @@ public class ImgDatasetDTO extends AbstractHashable
     @ResultColumn("PERM_ID")
     private String permId;
 
+    @ResultColumn("IMAGES_DIRECTORY")
+    private String imagesDirectoryPath;
+
     @ResultColumn("FIELDS_WIDTH")
     private Integer fieldNumberOfColumnsOrNull;
 
@@ -47,10 +50,11 @@ public class ImgDatasetDTO extends AbstractHashable
         // All Data-Object classes must have a default constructor.
     }
 
-    public ImgDatasetDTO(String permId, Integer fieldNumberOfRowsOrNull,
-            Integer fieldNumberOfColumnsOrNull, long containerId)
+    public ImgDatasetDTO(String permId, String imagesDirectoryPath,
+            Integer fieldNumberOfRowsOrNull, Integer fieldNumberOfColumnsOrNull, long containerId)
     {
         this.permId = permId;
+        this.imagesDirectoryPath = imagesDirectoryPath;
         this.fieldNumberOfColumnsOrNull = fieldNumberOfColumnsOrNull;
         this.fieldNumberOfRowsOrNull = fieldNumberOfRowsOrNull;
         this.containerId = containerId;
@@ -106,4 +110,14 @@ public class ImgDatasetDTO extends AbstractHashable
         this.containerId = containerId;
     }
 
+    public String getImagesDirectoryPath()
+    {
+        return imagesDirectoryPath;
+    }
+
+    public void setImagesDirectoryPath(String imagesDirectoryPath)
+    {
+        this.imagesDirectoryPath = imagesDirectoryPath;
+    }
+
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/AbstractImagesDownloadServlet.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/AbstractImagesDownloadServlet.java
index dc7dfbb46460f146566ffe723741858016d2549e..0a8ff8c0f5381a7b6c261d2dbe8d2f39f222975d 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/AbstractImagesDownloadServlet.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/AbstractImagesDownloadServlet.java
@@ -43,7 +43,7 @@ abstract class AbstractImagesDownloadServlet extends AbstractDatasetDownloadServ
      * @throw EnvironmentFailureException if image does not exist
      */
     protected abstract ResponseContentStream createImageResponse(TileImageReference params,
-            File datasetRoot) throws IOException, EnvironmentFailureException;
+            File datasetRoot, String datasetCode) throws IOException, EnvironmentFailureException;
 
     protected static class RequestParams extends TileImageReference
     {
@@ -151,7 +151,7 @@ abstract class AbstractImagesDownloadServlet extends AbstractDatasetDownloadServ
         ResponseContentStream responseStream;
         try
         {
-            responseStream = createImageResponse(params, datasetRoot);
+            responseStream = createImageResponse(params, datasetRoot, params.getDatasetCode());
         } catch (EnvironmentFailureException e)
         {
             operationLog.warn(e.getMessage());
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/MergingImagesDownloadServlet.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/MergingImagesDownloadServlet.java
index a2f4ee4a544f5164a057981ebd5f8cde4d1509cd..6b389338437e6d1d25eb1b27440865a19e66771d 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/MergingImagesDownloadServlet.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/MergingImagesDownloadServlet.java
@@ -42,9 +42,9 @@ public class MergingImagesDownloadServlet extends AbstractImagesDownloadServlet
      **/
     @Override
     protected final ResponseContentStream createImageResponse(TileImageReference params,
-            File datasetRoot) throws IOException, EnvironmentFailureException
+            File datasetRoot, String datasetCode) throws IOException, EnvironmentFailureException
     {
-        List<File> imageFiles = ImageChannelsUtils.getImagePaths(datasetRoot, params);
+        List<File> imageFiles = ImageChannelsUtils.getImagePaths(datasetRoot, datasetCode, params);
         BufferedImage image = ImageChannelsUtils.mergeImageChannels(params, imageFiles);
         File singleFileOrNull = imageFiles.size() == 1 ? imageFiles.get(0) : null;
         return createResponseContentStream(image, singleFileOrNull);
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/SplittingImagesDownloadServlet.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/SplittingImagesDownloadServlet.java
index 82739794b59e9f341e52130b3620bbb8bc563b67..0da94aff87d4eec92ea8af9461d2b4c794d2ca7d 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/SplittingImagesDownloadServlet.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/SplittingImagesDownloadServlet.java
@@ -20,9 +20,10 @@ import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.IOException;
 
-import ch.systemsx.cisd.bds.hcs.HCSDatasetLoader;
 import ch.systemsx.cisd.bds.hcs.Location;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
+import ch.systemsx.cisd.openbis.dss.etl.HCSDatasetLoaderFactory;
+import ch.systemsx.cisd.openbis.dss.etl.IHCSDatasetLoader;
 import ch.systemsx.cisd.openbis.dss.generic.server.images.ImageChannelsUtils;
 import ch.systemsx.cisd.openbis.dss.generic.server.images.TileImageReference;
 
@@ -38,10 +39,10 @@ public class SplittingImagesDownloadServlet extends AbstractImagesDownloadServle
     private static final long serialVersionUID = 1L;
 
     /** throws {@link EnvironmentFailureException} when image does not exist */
-    private File getImagePath(File datasetRoot, TileImageReference params)
+    private File getImagePath(File datasetRoot, String datasetCode, TileImageReference params)
             throws EnvironmentFailureException
     {
-        HCSDatasetLoader imageAccessor = new HCSDatasetLoader(datasetRoot);
+        IHCSDatasetLoader imageAccessor = HCSDatasetLoaderFactory.create(datasetRoot, datasetCode);
         Location wellLocation = params.getWellLocation();
         Location tileLocation = params.getTileLocation();
         int channel = 1; // NOTE: we assume that there is only one channel
@@ -56,9 +57,9 @@ public class SplittingImagesDownloadServlet extends AbstractImagesDownloadServle
      **/
     @Override
     protected final ResponseContentStream createImageResponse(TileImageReference params,
-            File datasetRoot) throws IOException, EnvironmentFailureException
+            File datasetRoot, String datasetCode) throws IOException, EnvironmentFailureException
     {
-        File imageFile = getImagePath(datasetRoot, params);
+        File imageFile = getImagePath(datasetRoot, datasetCode, params);
         BufferedImage image = ImageChannelsUtils.mergeImageChannels(params, imageFile);
         return createResponseContentStream(image, imageFile);
     }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/images/ImageChannelsUtils.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/images/ImageChannelsUtils.java
index a78d64eb95b1b63af69b859762d8dab16b31dfd0..3ae25a3a1087a40460720a36274c2731ddbb0549 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/images/ImageChannelsUtils.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/images/ImageChannelsUtils.java
@@ -22,9 +22,11 @@ import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 
-import ch.systemsx.cisd.bds.hcs.HCSDatasetLoader;
 import ch.systemsx.cisd.bds.hcs.Location;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
+import ch.systemsx.cisd.openbis.dss.etl.AbsoluteImageReference;
+import ch.systemsx.cisd.openbis.dss.etl.HCSDatasetLoaderFactory;
+import ch.systemsx.cisd.openbis.dss.etl.IHCSDatasetLoader;
 import ch.systemsx.cisd.openbis.dss.generic.server.AbstractDatasetDownloadServlet.Size;
 import ch.systemsx.cisd.openbis.dss.generic.shared.utils.ImageUtil;
 
@@ -57,9 +59,10 @@ public class ImageChannelsUtils
      *         be merged.
      * @throw {@link EnvironmentFailureException} when image does not exist
      */
-    public static List<File> getImagePaths(File datasetRoot, TileImageReference params)
+    public static List<File> getImagePaths(File datasetRoot, String datasetCode,
+            TileImageReference params)
     {
-        HCSDatasetLoader imageAccessor = new HCSDatasetLoader(datasetRoot);
+        IHCSDatasetLoader imageAccessor = HCSDatasetLoaderFactory.create(datasetRoot, datasetCode);
         Location wellLocation = params.getWellLocation();
         Location tileLocation = params.getTileLocation();
         List<File> paths = new ArrayList<File>();
@@ -152,14 +155,16 @@ public class ImageChannelsUtils
      * @param chosenChannel starts from 1
      * @throw {@link EnvironmentFailureException} when image does not exist
      */
-    public static File getImagePath(HCSDatasetLoader imageAccessor, Location wellLocation,
+    public static File getImagePath(IHCSDatasetLoader imageAccessor, Location wellLocation,
             Location tileLocation, int chosenChannel)
     {
-        String imagePath =
-                imageAccessor.tryGetStandardNodeAt(chosenChannel, wellLocation, tileLocation);
-        if (imagePath != null)
+        AbsoluteImageReference image =
+                imageAccessor.tryGetImage(chosenChannel, wellLocation, tileLocation);
+        if (image != null)
         {
-            return new File(imagePath);
+            // TODO 2010-05-31, Tomasz Pylak: add support for tiff paged images and channels
+            // splitting
+            return new File(image.getImageAbsolutePath());
         } else
         {
             throw EnvironmentFailureException.fromTemplate(
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ScreeningImageReportingPlugin.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ScreeningImageReportingPlugin.java
deleted file mode 100644
index 0d41b216824912a5031e8a4159c39cd6c032b395..0000000000000000000000000000000000000000
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ScreeningImageReportingPlugin.java
+++ /dev/null
@@ -1,206 +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.dss.generic.server.plugins;
-
-import static ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.SimpleTableModelBuilder.asNum;
-import static ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.SimpleTableModelBuilder.asText;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Properties;
-
-import ch.systemsx.cisd.bds.DataStructureLoader;
-import ch.systemsx.cisd.bds.IDataStructure;
-import ch.systemsx.cisd.bds.hcs.Geometry;
-import ch.systemsx.cisd.bds.hcs.IHCSImageFormattedData;
-import ch.systemsx.cisd.bds.hcs.Location;
-import ch.systemsx.cisd.bds.storage.INode;
-import ch.systemsx.cisd.bds.v1_0.IDataStructureV1_0;
-import ch.systemsx.cisd.common.exceptions.NotImplementedException;
-import ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard.AbstractDatastorePlugin;
-import ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.IReportingPluginTask;
-import ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.SimpleTableModelBuilder;
-import ch.systemsx.cisd.openbis.dss.generic.shared.utils.ImageUtil;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ISerializableComparable;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ImageTableCell;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.StringTableCell;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TableModel;
-import ch.systemsx.cisd.openbis.generic.shared.dto.DatasetDescription;
-
-/**
- * Reporting plugin which shows all images in wells of a plate dataset.
- * 
- * @author Tomasz Pylak
- */
-public class ScreeningImageReportingPlugin extends AbstractDatastorePlugin implements
-        IReportingPluginTask
-{
-    // -------- column headers used to describe images for a plate
-
-    private static final String IMAGE = "Thumbnail";
-
-    private static final String CHANNEL = "Channel";
-
-    private static final String TILE = "Tile";
-
-    private static final String COLUMN = "Column";
-
-    private static final String ROW = "Row";
-
-    private static final String DATASET_CODE = "Dataset Code";
-
-    private static final String IMAGE_PATH = "Image Path";
-
-    // ----------
-
-    private static final long serialVersionUID = 1L;
-
-    public ScreeningImageReportingPlugin(Properties properties, File storeRoot)
-    {
-        super(properties, storeRoot);
-    }
-
-    public TableModel createReport(List<DatasetDescription> datasets)
-    {
-        SimpleTableModelBuilder builder = new SimpleTableModelBuilder();
-        addReportHeaders(builder);
-        DataStructureLoader loader = new DataStructureLoader(storeRoot);
-        for (DatasetDescription dataset : datasets)
-        {
-            IDataStructureV1_0 structure = createDatasetAccessor(loader, dataset);
-            IHCSImageFormattedData imageAccessor = getImageAccessor(structure);
-            addReportRows(builder, dataset, imageAccessor);
-            structure.close();
-        }
-        return builder.getTableModel();
-    }
-
-    private void addReportHeaders(SimpleTableModelBuilder builder)
-    {
-        // Note: we rely on that column order at the openBIS server side!
-        builder.addHeader(DATASET_CODE);
-        builder.addHeader(ROW);
-        builder.addHeader(COLUMN);
-        builder.addHeader(TILE);
-        builder.addHeader(CHANNEL);
-        builder.addHeader(IMAGE);
-        builder.addHeader(IMAGE_PATH);
-    }
-
-    private void addReportRows(SimpleTableModelBuilder builder, DatasetDescription dataset,
-            IHCSImageFormattedData imageAccessor)
-    {
-        Geometry plateGeometry = imageAccessor.getPlateGeometry();
-        Geometry wellGeometry = imageAccessor.getWellGeometry();
-        for (int channel = 1; channel <= imageAccessor.getChannelCount(); channel++)
-        {
-            for (Location wellLocation : new GeometryIterable(plateGeometry))
-            {
-                for (Location tileLocation : new GeometryIterable(wellGeometry))
-                {
-                    INode img =
-                            imageAccessor.tryGetStandardNodeAt(channel, wellLocation, tileLocation);
-                    if (img != null)
-                    {
-
-                        String imageStoreRelativePath = getStoreRelativePath(img.getPath());
-                        ISerializableComparable image =
-                                createImageCell(dataset, new File(img.getPath()));
-                        String datasetCode = dataset.getDatasetCode();
-                        int tileNumber =
-                                tileLocation.getX() + (tileLocation.getY() - 1)
-                                        * wellGeometry.getColumns();
-                        List<ISerializableComparable> row =
-                                Arrays.<ISerializableComparable> asList(asText(datasetCode),
-                                        asNum(wellLocation.getY()), asNum(wellLocation.getX()),
-                                        asNum(tileNumber), asNum(channel), image,
-                                        asText(imageStoreRelativePath));
-                        builder.addRow(row);
-                    }
-                }
-            }
-        }
-    }
-
-    private static class GeometryIterable implements Iterable<Location>
-    {
-        private final Geometry geometry;
-
-        public GeometryIterable(Geometry geometry)
-        {
-            this.geometry = geometry;
-        }
-
-        public Iterator<Location> iterator()
-        {
-            return new Iterator<Location>()
-                {
-                    private int x = 0;
-
-                    private int y = 1;
-
-                    public boolean hasNext()
-                    {
-                        return x < geometry.getColumns() || y < geometry.getRows();
-                    }
-
-                    public Location next()
-                    {
-                        if (x < geometry.getColumns())
-                        {
-                            x++;
-                        } else
-                        {
-                            x = 1;
-                            y++;
-                        }
-                        return new Location(x, y);
-                    }
-
-                    public void remove()
-                    {
-                        throw new NotImplementedException();
-                    }
-                };
-        }
-    }
-
-    private IHCSImageFormattedData getImageAccessor(IDataStructureV1_0 structure)
-    {
-        return (IHCSImageFormattedData) structure.getFormattedData();
-    }
-
-    private IDataStructureV1_0 createDatasetAccessor(DataStructureLoader loader,
-            DatasetDescription dataset)
-    {
-        IDataStructure dataStructure = loader.load(dataset.getDataSetLocation(), false);
-        return (IDataStructureV1_0) dataStructure;
-    }
-
-    private static ISerializableComparable createImageCell(DatasetDescription dataset, File file)
-    {
-        if (ImageUtil.isImageFile(file))
-        {
-            String code = dataset.getDatasetCode();
-            String location = dataset.getDataSetLocation();
-            return new ImageTableCell(code, location, file.getPath(), 100, 60);
-        }
-        return new StringTableCell(file.getName());
-    }
-}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ScreeningPlateImageParamsReportingPlugin.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ScreeningPlateImageParamsReportingPlugin.java
index 0af088f7920fdb593eb6b32bab52f07c1e5af41b..4ea9ea4d3fcf049f02461d47ee4c625633e9bf9d 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ScreeningPlateImageParamsReportingPlugin.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ScreeningPlateImageParamsReportingPlugin.java
@@ -25,7 +25,8 @@ import java.util.List;
 import java.util.Properties;
 
 import ch.systemsx.cisd.bds.hcs.Geometry;
-import ch.systemsx.cisd.bds.hcs.HCSDatasetLoader;
+import ch.systemsx.cisd.openbis.dss.etl.HCSDatasetLoaderFactory;
+import ch.systemsx.cisd.openbis.dss.etl.IHCSDatasetLoader;
 import ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard.AbstractDatastorePlugin;
 import ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.IReportingPluginTask;
 import ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.SimpleTableModelBuilder;
@@ -71,7 +72,8 @@ public class ScreeningPlateImageParamsReportingPlugin extends AbstractDatastoreP
         for (DatasetDescription dataset : datasets)
         {
             File datasetFile = new File(storeRoot, dataset.getDataSetLocation());
-            HCSDatasetLoader imageAccessor = new HCSDatasetLoader(datasetFile);
+            IHCSDatasetLoader imageAccessor =
+                    HCSDatasetLoaderFactory.create(datasetFile, dataset.getDatasetCode());
             addReportRows(builder, dataset, imageAccessor);
             imageAccessor.close();
         }
@@ -90,7 +92,7 @@ public class ScreeningPlateImageParamsReportingPlugin extends AbstractDatastoreP
     }
 
     private void addReportRows(SimpleTableModelBuilder builder, DatasetDescription dataset,
-            HCSDatasetLoader imageAccessor)
+            IHCSDatasetLoader imageAccessor)
     {
         Geometry plateGeometry = imageAccessor.getPlateGeometry();
         Geometry wellGeometry = imageAccessor.getWellGeometry();
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java
index 57b039f014d23561a9afaf48d54df3587d0cf8e8..89215b2f40e1cb2b61100a975c27ad669a60383c 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java
@@ -32,13 +32,15 @@ import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;
 import com.csvreader.CsvReader;
 
 import ch.systemsx.cisd.bds.hcs.Geometry;
-import ch.systemsx.cisd.bds.hcs.HCSDatasetLoader;
 import ch.systemsx.cisd.bds.hcs.Location;
 import ch.systemsx.cisd.common.api.RpcServiceInterfaceVersionDTO;
 import ch.systemsx.cisd.common.api.server.RpcServiceNameServer;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.common.io.ConcatenatedFileInputStream;
+import ch.systemsx.cisd.openbis.dss.etl.AbsoluteImageReference;
+import ch.systemsx.cisd.openbis.dss.etl.HCSDatasetLoaderFactory;
+import ch.systemsx.cisd.openbis.dss.etl.IHCSDatasetLoader;
 import ch.systemsx.cisd.openbis.dss.generic.server.AbstractDssServiceRpc;
 import ch.systemsx.cisd.openbis.dss.generic.server.images.ImageChannelsUtils;
 import ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.DatasetFileLines;
@@ -75,8 +77,9 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc implements
 
         // Register the service with the name server
         RpcServiceInterfaceVersionDTO ifaceVersion =
-                new RpcServiceInterfaceVersionDTO("screening-dss", "/rmi-datastore-server-screening-api-v1",
-                        getMajorVersion(), getMinorVersion());
+                new RpcServiceInterfaceVersionDTO("screening-dss",
+                        "/rmi-datastore-server-screening-api-v1", getMajorVersion(),
+                        getMinorVersion());
         HttpInvokerServiceExporter nameServiceExporter =
                 ServiceProvider.getRpcNameServiceExporter();
         RpcServiceNameServer nameServer = (RpcServiceNameServer) nameServiceExporter.getService();
@@ -166,7 +169,8 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc implements
     private static ImageDatasetMetadata extractImageMetadata(IImageDatasetIdentifier dataset,
             File datasetRoot)
     {
-        HCSDatasetLoader imageAccessor = new HCSDatasetLoader(datasetRoot);
+        IHCSDatasetLoader imageAccessor =
+                HCSDatasetLoaderFactory.create(datasetRoot, dataset.getDatasetCode());
         File imageFile = getAnyImagePath(imageAccessor, dataset);
         Geometry wellGeometry = imageAccessor.getWellGeometry();
         int channelsNumber = imageAccessor.getChannelCount();
@@ -176,7 +180,7 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc implements
                 image.getHeight());
     }
 
-    private static File getAnyImagePath(HCSDatasetLoader imageAccessor,
+    private static File getAnyImagePath(IHCSDatasetLoader imageAccessor,
             IImageDatasetIdentifier dataset)
     {
         Geometry plateGeometry = imageAccessor.getPlateGeometry();
@@ -184,12 +188,11 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc implements
         {
             for (int col = 1; col <= plateGeometry.getColumns(); col++)
             {
-                String imagePath =
-                        imageAccessor.tryGetStandardNodeAt(1, new Location(col, row), new Location(
-                                1, 1));
-                if (imagePath != null)
+                AbsoluteImageReference image =
+                        imageAccessor.tryGetImage(1, new Location(col, row), new Location(1, 1));
+                if (image != null)
                 {
-                    return new File(imagePath);
+                    return new File(image.getImageAbsolutePath());
                 }
             }
         }
@@ -223,14 +226,14 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc implements
 
     public InputStream loadImages(String sessionToken, List<PlateImageReference> imageReferences)
     {
-        Map<String, HCSDatasetLoader> imageLoadersMap =
+        Map<String, IHCSDatasetLoader> imageLoadersMap =
                 getImageDatasetsMap(sessionToken, imageReferences);
         List<File> imageFiles = new ArrayList<File>();
         try
         {
             for (PlateImageReference imageReference : imageReferences)
             {
-                HCSDatasetLoader imageAccessor =
+                IHCSDatasetLoader imageAccessor =
                         imageLoadersMap.get(imageReference.getDatasetCode());
                 assert imageAccessor != null : "imageAccessor not found for: " + imageReference;
                 File imageFile = tryGetImageFile(imageAccessor, imageReference);
@@ -243,20 +246,20 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc implements
         return new ConcatenatedFileInputStream(true, imageFiles);
     }
 
-    private static void closeDatasetLoaders(Collection<HCSDatasetLoader> loaders)
+    private static void closeDatasetLoaders(Collection<IHCSDatasetLoader> loaders)
     {
-        for (HCSDatasetLoader loader : loaders)
+        for (IHCSDatasetLoader loader : loaders)
         {
             loader.close();
         }
     }
 
     // throws exception if some datasets cannot be found
-    private Map<String/* image or feature vector dataset code */, HCSDatasetLoader> getImageDatasetsMap(
+    private Map<String/* image or feature vector dataset code */, IHCSDatasetLoader> getImageDatasetsMap(
             String sessionToken, List<PlateImageReference> imageReferences)
     {
-        Map<String/* dataset code */, HCSDatasetLoader> imageDatasetsMap =
-                new HashMap<String, HCSDatasetLoader>();
+        Map<String/* dataset code */, IHCSDatasetLoader> imageDatasetsMap =
+                new HashMap<String, IHCSDatasetLoader>();
         for (PlateImageReference imageReference : imageReferences)
         {
             if (imageDatasetsMap.containsKey(imageReference.getDatasetCode()) == false)
@@ -265,7 +268,7 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc implements
                         tryFindImageDataset(sessionToken, imageReference.getDatasetCode());
                 if (imageDataset != null)
                 {
-                    HCSDatasetLoader imageAccessor = createImageLoader(imageDataset.getCode());
+                    IHCSDatasetLoader imageAccessor = createImageLoader(imageDataset.getCode());
                     imageDatasetsMap.put(imageReference.getDatasetCode(), imageAccessor);
                 } else
                 {
@@ -277,7 +280,7 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc implements
         return imageDatasetsMap;
     }
 
-    private File tryGetImageFile(HCSDatasetLoader imageAccessor, PlateImageReference imageRef)
+    private File tryGetImageFile(IHCSDatasetLoader imageAccessor, PlateImageReference imageRef)
     {
         Location wellLocation = asLocation(imageRef.getWellPosition());
         Location tileLocation =
@@ -310,12 +313,10 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc implements
         return new Location(wellPosition.getWellColumn(), wellPosition.getWellRow());
     }
 
-    private HCSDatasetLoader createImageLoader(String datasetCode)
+    private IHCSDatasetLoader createImageLoader(String datasetCode)
     {
-        HCSDatasetLoader loader;
         File datasetRoot = getRootDirectoryForDataSet(datasetCode);
-        loader = new HCSDatasetLoader(datasetRoot);
-        return loader;
+        return HCSDatasetLoaderFactory.create(datasetRoot, datasetCode);
     }
 
     private ExternalData tryFindImageDataset(String sessionToken, String datasetCode)
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServer.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServer.java
index 8c8f91858cecde34ddb2c0442f653c12191b1667..dc5aa32e0e11d336f1db7c6feb316b8c77e12c51 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServer.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServer.java
@@ -58,7 +58,6 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.Plate;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateContent;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateImages;
-import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateSingleImageReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellContent;
 
 /**
@@ -138,12 +137,6 @@ public final class ScreeningServer extends AbstractServer<IScreeningServer> impl
                 geneMaterialId, experimentIdentifier);
     }
 
-    public List<PlateSingleImageReference> listPlateImages(String sessionToken, TechId plateId)
-    {
-        Session session = getSession(sessionToken);
-        return PlateContentLoader.listPlateImages(session, businessObjectFactory, plateId);
-    }
-
     public TableModel loadImageAnalysisForExperiment(String sessionToken, TechId experimentId)
     {
         Session session = getSession(sessionToken);
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServerLogger.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServerLogger.java
index 537045c43db1bfca6c3ac9d387e8a88303007d31..a8b44548a209a753c22e576af84c3e60ae25dee5 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServerLogger.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServerLogger.java
@@ -41,7 +41,6 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.Plate;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateContent;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateImages;
-import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateSingleImageReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellContent;
 
 /**
@@ -98,12 +97,6 @@ final class ScreeningServerLogger extends AbstractServerLogger implements IScree
         return null;
     }
 
-    public List<PlateSingleImageReference> listPlateImages(String sessionToken, TechId plateId)
-    {
-        logAccess(sessionToken, "loadAllImages", "PLATE(%s)", plateId.getId());
-        return null;
-    }
-
     public TableModel loadImageAnalysisForExperiment(String sessionToken, TechId experimentId)
     {
         logAccess(sessionToken, "loadImageAnalysisForExperiment", "EXPERIMENT(%s)", experimentId
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/DatasetReportsLoader.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/DatasetReportsLoader.java
index 0c31b7d73a80f1a6ba875ed5dd9d56472230c294..abde749fb22b5e2dd29283b3fc8d5f863c9039f0 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/DatasetReportsLoader.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/DatasetReportsLoader.java
@@ -25,7 +25,6 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IntegerTableCell;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TableModel;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TableModelRow;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateImageParameters;
-import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateSingleImageReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ScreeningConstants;
 
 /**
@@ -42,40 +41,6 @@ public class DatasetReportsLoader
                 ScreeningConstants.PLATE_IMAGE_ANALYSIS_REPORT_KEY, datastoreCode, datasets);
     }
 
-    public static List<PlateSingleImageReference> loadPlateImages(List<String> datasets,
-            String datastoreCode, IExternalDataTable externalDataTable)
-    {
-        TableModel imageParamsReport =
-                externalDataTable.createReportFromDatasets(
-                        ScreeningConstants.PLATE_IMAGE_REPORT_KEY, datastoreCode, datasets);
-        return asPlateImages(imageParamsReport);
-    }
-
-    private static List<PlateSingleImageReference> asPlateImages(TableModel imagePathsReport)
-    {
-        List<PlateSingleImageReference> paths = new ArrayList<PlateSingleImageReference>();
-        List<TableModelRow> rows = imagePathsReport.getRows();
-        for (TableModelRow row : rows)
-        {
-            paths.add(asImagePath(row.getValues()));
-        }
-        return paths;
-    }
-
-    private static PlateSingleImageReference asImagePath(List<ISerializableComparable> values)
-    {
-        PlateSingleImageReference path = new PlateSingleImageReference();
-        int col = 0;
-        path.setDatasetCode(asText(values.get(col++)));
-        path.setWellRow(asNum(values.get(col++)));
-        path.setWellCol(asNum(values.get(col++)));
-        path.setTile(asNum(values.get(col++)));
-        path.setChannel(asNum(values.get(col++)));
-        path.setImageUrl(asText(values.get(col++)));
-        path.setImagePath(asText(values.get(col++)));
-        return path;
-    }
-
     public static List<PlateImageParameters> loadPlateImageParameters(List<String> datasets,
             String datastoreCode, IExternalDataTable externalDataTable)
     {
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateContentLoader.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateContentLoader.java
index 26050512c808437ab9df3055d4f1f5ad80ac19e1..b94e4c7bcb33cbe80fd14f8c915fe51846b25bfc 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateContentLoader.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateContentLoader.java
@@ -45,7 +45,6 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.DatasetReferen
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateContent;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateImageParameters;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateImages;
-import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateSingleImageReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ScreeningConstants;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellLocation;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellMetadata;
@@ -57,15 +56,6 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellMetadata;
  */
 public class PlateContentLoader
 {
-    /**
-     * loads all images from all existing image datasets for the specified plate.
-     */
-    public static List<PlateSingleImageReference> listPlateImages(Session session,
-            IScreeningBusinessObjectFactory businessObjectFactory, TechId plateId)
-    {
-        return new PlateContentLoader(session, businessObjectFactory).loadAllImages(plateId);
-    }
-
     public static TableModel loadImageAnalysisForPlate(Session session,
             IScreeningBusinessObjectFactory businessObjectFactory, TechId plateId)
     {
@@ -160,24 +150,6 @@ public class PlateContentLoader
                 externalDataTable);
     }
 
-    private List<PlateSingleImageReference> loadAllImages(TechId plateId)
-    {
-        IExternalDataTable externalDataTable = createExternalDataTable();
-        List<ExternalDataPE> datasets = loadDatasets(plateId, externalDataTable);
-        List<ExternalDataPE> imageDatasets = ScreeningUtils.filterImageDatasets(datasets);
-        List<PlateSingleImageReference> imagePaths = new ArrayList<PlateSingleImageReference>();
-        if (imageDatasets.size() > 0)
-        {
-            List<String> datasetCodes = extractCodes(imageDatasets);
-            // NOTE: we assume that all datasets for one plate come from the same datastore
-            String dataStoreCode = extractDataStoreCode(imageDatasets);
-            imagePaths =
-                    DatasetReportsLoader.loadPlateImages(datasetCodes, dataStoreCode,
-                            externalDataTable);
-        }
-        return imagePaths;
-    }
-
     private String extractDataStoreCode(List<ExternalDataPE> imageDatasets)
     {
         assert imageDatasets.size() > 0;
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/IScreeningServer.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/IScreeningServer.java
index 88adf7a5d942405ed0e71b1591d892b0bdde7434..e10407ac39a6d0420d445b6ec96288f7f9edbc25 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/IScreeningServer.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/IScreeningServer.java
@@ -38,7 +38,6 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateContent;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateImages;
-import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateSingleImageReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellContent;
 
 /**
@@ -73,15 +72,6 @@ public interface IScreeningServer extends IServer
             TechId geneMaterialId,
             @AuthorizationGuard(guardClass = SpaceIdentifierPredicate.class) ExperimentIdentifier experimentIdentifier);
 
-    /**
-     * loads all images from all existing image datasets (in BDS-HCS format) for the specified
-     * plate.
-     */
-    @Transactional(readOnly = true)
-    @RolesAllowed(RoleSet.OBSERVER)
-    public List<PlateSingleImageReference> listPlateImages(String sessionToken,
-            @AuthorizationGuard(guardClass = SampleTechIdPredicate.class) TechId plateId);
-
     /**
      * Loads all analysis results from all existing image-analysis datasets connected with the
      * specified experiment. It is assumed that all datasets are CSV files with the same headers.
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/PlateSingleImageReference.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/PlateSingleImageReference.java
deleted file mode 100644
index 484331f65f08c6fcf7705ece01fec1b08a8c00eb..0000000000000000000000000000000000000000
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/PlateSingleImageReference.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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.plugin.screening.shared.basic.dto;
-
-import java.io.Serializable;
-
-import com.google.gwt.user.client.rpc.IsSerializable;
-
-/**
- * Reference to a single image acquired for a plate.
- * 
- * @author Tomasz Pylak
- */
-public class PlateSingleImageReference implements IsSerializable, Serializable
-{
-    private static final long serialVersionUID = 1L;
-
-    private String datasetCode;
-
-    private int wellRow;
-
-    private int wellCol;
-
-    private int tile;
-
-    private int channel;
-
-    // url to fetch the image from DSS using HTTP, the hostname and servlet name is not included
-    private String imageUrl;
-
-    // path in the DSS store
-    private String imagePath;
-
-    public int getWellRow()
-    {
-        return wellRow;
-    }
-
-    public void setWellRow(int wellRow)
-    {
-        this.wellRow = wellRow;
-    }
-
-    public int getWellCol()
-    {
-        return wellCol;
-    }
-
-    public void setWellCol(int wellCol)
-    {
-        this.wellCol = wellCol;
-    }
-
-    public int getTile()
-    {
-        return tile;
-    }
-
-    public void setTile(int tile)
-    {
-        this.tile = tile;
-    }
-
-    public int getChannel()
-    {
-        return channel;
-    }
-
-    public void setChannel(int channel)
-    {
-        this.channel = channel;
-    }
-
-    public String getDatasetCode()
-    {
-        return datasetCode;
-    }
-
-    public void setDatasetCode(String datasetCode)
-    {
-        this.datasetCode = datasetCode;
-    }
-
-    public String getImageUrl()
-    {
-        return imageUrl;
-    }
-
-    public void setImageUrl(String imageUrl)
-    {
-        this.imageUrl = imageUrl;
-    }
-
-    public String getImagePath()
-    {
-        return imagePath;
-    }
-
-    public void setImagePath(String imagePath)
-    {
-        this.imagePath = imagePath;
-    }
-}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/ScreeningConstants.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/ScreeningConstants.java
index e626d39f107f1e98e0155a08e785ce7bca7a175c..40d8d451d4fe92ba5b3cfbed259ab3081052d0c6 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/ScreeningConstants.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/ScreeningConstants.java
@@ -27,14 +27,14 @@ package ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto;
  */
 public class ScreeningConstants
 {
+    // name of the data source (configured in service.properties) which allows to access imaging db
+    public static final String IMAGING_DATA_SOURCE = "imaging-db";
+
     // name of the property which stores material (gene) inhibited by the material stored in a well
     public static final String INHIBITOR_PROPERTY_CODE = "INHIBITOR_OF";
 
     // ---- required DSS plugins
 
-    // id of the DSS screening reporting plugin to get the images
-    public static final String PLATE_IMAGE_REPORT_KEY = "plate-image-reporter";
-
     // id of the DSS screening reporting plugin to get the images parameters
     public static final String PLATE_IMAGE_PARAMS_REPORT_KEY = "plate-image-params-reporter";
 
diff --git a/screening/source/sql/postgresql/001/schema-001.sql b/screening/source/sql/postgresql/001/schema-001.sql
index 11f5df79d0cd95e60481487e8f984723a84c1975..5385d12e09badaa1cb0c295aa6186671d329f463 100644
--- a/screening/source/sql/postgresql/001/schema-001.sql
+++ b/screening/source/sql/postgresql/001/schema-001.sql
@@ -59,7 +59,10 @@ CREATE INDEX SPOTS_CONT_IDX ON SPOTS(CONT_ID);
 
 CREATE TABLE DATA_SETS (
   ID BIGSERIAL NOT NULL,
-  PERM_ID CODE NOT NULL,  
+  PERM_ID CODE NOT NULL,
+  -- All images paths are relative to the IMAGES_DIRECTORY folder. 
+  -- The folder path itself is relative to the dataset root directory.
+  IMAGES_DIRECTORY FILE_PATH,  
   
 	FIELDS_WIDTH INTEGER,
 	FIELDS_HEIGHT INTEGER,	
diff --git a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImagingUploadDAOTest.java b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImagingUploadDAOTest.java
index 1d94d01c45116ea9ced5a15671d067e436a752ce..9f7992d56c9233232222d6e6333303490b47cef3 100644
--- a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImagingUploadDAOTest.java
+++ b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImagingUploadDAOTest.java
@@ -109,13 +109,13 @@ public class ImagingUploadDAOTest extends AbstractDBTest
     private void testGetImage(final long datasetId, final long channelId1, final long channelId2)
     {
         ImgImageDTO image1 =
-                dao.getImage(channelId1, datasetId, new Location(X_WELL_COLUMN, Y_WELL_ROW),
+                dao.tryGetImage(channelId1, datasetId, new Location(X_WELL_COLUMN, Y_WELL_ROW),
                         new Location(X_TILE_COLUMN, Y_TILE_ROW));
         assertEquals(PATH1, image1.getFilePath());
         assertEquals(ColorComponent.BLUE, image1.getColorComponent());
 
         ImgImageDTO image2 =
-                dao.getImage(channelId2, datasetId, new Location(X_WELL_COLUMN, Y_WELL_ROW),
+                dao.tryGetImage(channelId2, datasetId, new Location(X_WELL_COLUMN, Y_WELL_ROW),
                         new Location(X_TILE_COLUMN, Y_TILE_ROW));
         assertEquals(PATH2, image2.getFilePath());
         assertEquals(ColorComponent.RED, image2.getColorComponent());
@@ -186,7 +186,7 @@ public class ImagingUploadDAOTest extends AbstractDBTest
         final Integer fieldsWidth = 1;
         final Integer fieldsHeight = 2;
         final ImgDatasetDTO dataset =
-                new ImgDatasetDTO(permId, fieldsHeight, fieldsWidth, containerId);
+                new ImgDatasetDTO(permId, ".", fieldsHeight, fieldsWidth, containerId);
         final long datasetId = dao.addDataset(dataset);
 
         final ImgDatasetDTO loadedDataset = dao.tryGetDatasetByPermId(DS_PERM_ID);