diff --git a/datamover/resource/dependency-structure.ddf b/datamover/resource/dependency-structure.ddf
index 96dfeacc71e6bd1f15fc12888648248582c0d80e..e7f44ccb374f7ac57394539a167c1c783e35d5d9 100644
--- a/datamover/resource/dependency-structure.ddf
+++ b/datamover/resource/dependency-structure.ddf
@@ -8,16 +8,18 @@ check absenceOfPackageCycles > 1 in {package}.*
 
 [fs.common] = ${fs}.common.*
 [fs.impl] = ${fs}.impl.*
+[fs.store] = ${fs}.store.*
 [fs.intf] = ${fs}.intf.*
 [fs.remote] = ${fs}.remote.*
 [fs.main] = ${fs}.* excluding ${fs}.*.*
 
-layer fs.layer0 = [fs.intf] 
-layer fs.layer1 = [fs.common] 
-layer fs.layer2 = [fs.impl] [fs.remote] 
-layer fs.layer3 = [fs.main] 
+layer fs.intf = [fs.intf] 
+layer fs.common = [fs.common] 
+layer fs.store = [fs.store]  
+layer fs.impl = [fs.impl] [fs.remote] 
+layer fs.main = [fs.main] 
 
-check layeringOf fs.layer0 fs.layer1 fs.layer2 fs.layer3
+check layeringOf fs.intf fs.common fs.store fs.impl fs.main
 
 #-------------------------------
 
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/DataMover.java b/datamover/source/java/ch/systemsx/cisd/datamover/DataMover.java
index 8f67b6a9cc21009aba7e4c674f7471b2375c172c..57e087fff2df1037b8b140da46a10c8c53ff2e52 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/DataMover.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/DataMover.java
@@ -27,9 +27,12 @@ import ch.systemsx.cisd.common.utilities.ITerminable;
 import ch.systemsx.cisd.common.utilities.ITriggerable;
 import ch.systemsx.cisd.common.utilities.TimerHelper;
 import ch.systemsx.cisd.common.utilities.TriggeringTimerTask;
+import ch.systemsx.cisd.datamover.common.StoreItem;
+import ch.systemsx.cisd.datamover.filesystem.FileStoreFactory;
 import ch.systemsx.cisd.datamover.filesystem.RemoteMonitoredMoverFactory;
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore;
 import ch.systemsx.cisd.datamover.filesystem.intf.IFileSysOperationsFactory;
-import ch.systemsx.cisd.datamover.utils.FileStore;
+import ch.systemsx.cisd.datamover.filesystem.intf.IStoreHandler;
 import ch.systemsx.cisd.datamover.utils.LocalBufferDirs;
 
 /**
@@ -69,7 +72,7 @@ public class DataMover
 
     private static LocalBufferDirs createLocalBufferDirs(Parameters parameters)
     {
-        return new LocalBufferDirs(parameters.getBufferStore().getPath(), LOCAL_COPY_IN_PROGRESS_DIR,
+        return new LocalBufferDirs(parameters.getBufferDirectoryPath(), LOCAL_COPY_IN_PROGRESS_DIR,
                 LOCAL_COPY_COMPLETE_DIR, LOCAL_READY_TO_MOVE_DIR, LOCAL_TEMP_DIR);
     }
 
@@ -138,18 +141,33 @@ public class DataMover
 
     private DataMoverProcess createOutgoingMovingProcess()
     {
-        final FileStore outgoingStore = parameters.getOutgoingStore();
-        final IPathHandler remoteMover = createRemotePathMover(null, outgoingStore.getPath(), outgoingStore.getHost());
+        final FileStore outgoingStore = parameters.getOutgoingStore(factory);
+        final File readyToMoveDir = bufferDirs.getReadyToMoveDir();
+        final FileStore readyToMoveStore = FileStoreFactory.createLocal(readyToMoveDir, "ready-to-move", factory);
+        final IStoreHandler remoteStoreMover = createRemotePathMover(readyToMoveStore, outgoingStore);
+
         final DirectoryScanningTimerTask outgoingMovingTask =
-                new DirectoryScanningTimerTask(bufferDirs.getReadyToMoveDir(), FileUtilities.ACCEPT_ALL_FILTER,
-                        remoteMover);
+                new DirectoryScanningTimerTask(readyToMoveDir, FileUtilities.ACCEPT_ALL_FILTER,
+                        asPathHandler(remoteStoreMover));
         return new DataMoverProcess(outgoingMovingTask, "Final Destination Mover");
     }
 
-    private IPathHandler createRemotePathMover(String sourceHost, File destinationDirectory, String destinationHost)
+    // TODO 2007-10-10 Tomasz Pylak: remove this when DirectoryScanningTimerTask will work with IStoreHandler. This is a
+    // quick hack.
+    private static IPathHandler asPathHandler(final IStoreHandler storeHandler)
+    {
+        return new IPathHandler()
+            {
+                public void handle(File path)
+                {
+                    storeHandler.handle(new StoreItem(path.getName()));
+                }
+            };
+    }
+
+    private IStoreHandler createRemotePathMover(FileStore source, FileStore destination)
     {
-        return RemoteMonitoredMoverFactory.create(sourceHost, destinationDirectory, destinationHost, factory,
-                parameters);
+        return RemoteMonitoredMoverFactory.create(source, destination, parameters);
     }
 
     private static ITerminable createCompoundTerminable(final ITerminable... terminables)
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/IncomingProcessor.java b/datamover/source/java/ch/systemsx/cisd/datamover/IncomingProcessor.java
index 527fdd901d6232453e6636145d10dd6c314d133d..e64049b119a1723978bb81920f9ee0076fd686a4 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/IncomingProcessor.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/IncomingProcessor.java
@@ -29,15 +29,19 @@ import ch.systemsx.cisd.common.logging.Log4jSimpleLogger;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.common.utilities.DirectoryScanningTimerTask;
+import ch.systemsx.cisd.common.utilities.FileUtilities;
 import ch.systemsx.cisd.common.utilities.IPathHandler;
 import ch.systemsx.cisd.common.utilities.NamePrefixFileFilter;
 import ch.systemsx.cisd.datamover.common.MarkerFile;
+import ch.systemsx.cisd.datamover.common.StoreItem;
+import ch.systemsx.cisd.datamover.filesystem.FileStoreFactory;
 import ch.systemsx.cisd.datamover.filesystem.RemoteMonitoredMoverFactory;
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore;
 import ch.systemsx.cisd.datamover.filesystem.intf.IFileSysOperationsFactory;
 import ch.systemsx.cisd.datamover.filesystem.intf.IPathMover;
-import ch.systemsx.cisd.datamover.filesystem.intf.IReadPathOperations;
 import ch.systemsx.cisd.datamover.filesystem.intf.IRecoverableTimerTaskFactory;
-import ch.systemsx.cisd.datamover.utils.FileStore;
+import ch.systemsx.cisd.datamover.filesystem.intf.IStoreHandler;
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore.ExtendedFileStore;
 import ch.systemsx.cisd.datamover.utils.LocalBufferDirs;
 import ch.systemsx.cisd.datamover.utils.QuietPeriodFileFilter;
 
@@ -60,14 +64,10 @@ public class IncomingProcessor implements IRecoverableTimerTaskFactory
 
     private final IFileSysOperationsFactory factory;
 
-    private final IReadPathOperations incomingReadOperations;
-
     private final IPathMover pathMover;
 
     private final LocalBufferDirs bufferDirs;
 
-    private final boolean isIncomingRemote;
-
     private final FileStore incomingStore;
 
     private final String prefixForIncoming;
@@ -84,9 +84,7 @@ public class IncomingProcessor implements IRecoverableTimerTaskFactory
     {
         this.parameters = parameters;
         this.prefixForIncoming = parameters.getPrefixForIncoming();
-        this.isIncomingRemote = parameters.getTreatIncomingAsRemote();
-        this.incomingStore = parameters.getIncomingStore();
-        this.incomingReadOperations = factory.getReadPathOperations();
+        this.incomingStore = parameters.getIncomingStore(factory);
         this.pathMover = factory.getMover();
         this.factory = factory;
         this.bufferDirs = bufferDirs;
@@ -99,18 +97,32 @@ public class IncomingProcessor implements IRecoverableTimerTaskFactory
 
     private DataMoverProcess create()
     {
-        final IPathHandler pathHandler = createIncomingMovingPathHandler(incomingStore.getHost());
+        final IStoreHandler pathHandler = createIncomingMovingPathHandler();
         final FileFilter filter = createQuietPeriodFilter();
 
+        // TODO 2007-10-10 Tomasz Pylak: refactor not to use incomingStore.getPath()
         final DirectoryScanningTimerTask movingTask =
-                new DirectoryScanningTimerTask(incomingStore.getPath(), filter, pathHandler,
+                new DirectoryScanningTimerTask(incomingStore.getPath(), filter, asPathHandler(pathHandler),
                         NUMBER_OF_ERRORS_IN_LISTING_IGNORED);
         return new DataMoverProcess(movingTask, "Mover of Incoming Data", this);
     }
 
+    // TODO 2007-10-10 Tomasz Pylak: remove this when DirectoryScanningTimerTask will work with IStoreHandler. This is a
+    // quick hack.
+    private static IPathHandler asPathHandler(final IStoreHandler storeHandler)
+    {
+        return new IPathHandler()
+            {
+                public void handle(File path)
+                {
+                    storeHandler.handle(new StoreItem(path.getName()));
+                }
+            };
+    }
+
     private FileFilter createQuietPeriodFilter()
     {
-        FileFilter quitePeriodFilter = new QuietPeriodFileFilter(parameters, incomingReadOperations);
+        FileFilter quitePeriodFilter = new QuietPeriodFileFilter(parameters);
         FileFilter filterDeletionMarkers = new NamePrefixFileFilter(Constants.DELETION_IN_PROGRESS_PREFIX, false);
         FileFilter filter = combineFilters(filterDeletionMarkers, quitePeriodFilter);
         return filter;
@@ -127,53 +139,43 @@ public class IncomingProcessor implements IRecoverableTimerTaskFactory
             };
     }
 
-    private IPathHandler createIncomingMovingPathHandler(final String sourceHostOrNull)
+    private IStoreHandler createIncomingMovingPathHandler()
     {
-        return new IPathHandler()
+        return new IStoreHandler()
             {
-                public void handle(File sourceFile)
+                public void handle(StoreItem sourceItem)
                 {
-                    if (isIncomingRemote)
+                    ExtendedFileStore extendedFileStore = incomingStore.tryAsExtended();
+                    if (extendedFileStore == null)
                     {
-                        moveFromRemoteIncoming(sourceFile, sourceHostOrNull);
+                        moveFromRemoteIncoming(sourceItem);
                     } else
                     {
-                        moveFromLocalIncoming(sourceFile);
+                        moveFromLocalIncoming(extendedFileStore, sourceItem);
                     }
                 }
             };
     }
 
-    private void moveFromLocalIncoming(File source)
+    private void moveFromLocalIncoming(ExtendedFileStore sourceStore, StoreItem sourceItem)
     {
-        final File finalFile = tryMoveLocal(source, bufferDirs.getCopyCompleteDir(), parameters.getPrefixForIncoming());
-        if (finalFile == null)
-        {
-            return;
-        }
+        sourceStore.tryMoveLocal(sourceItem, bufferDirs.getCopyCompleteDir(), parameters.getPrefixForIncoming());
     }
 
-    private void moveFromRemoteIncoming(File source, String sourceHostOrNull)
+    private void moveFromRemoteIncoming(StoreItem sourceItem)
     {
         // 1. move from incoming: copy, delete, create copy-finished-marker
         final File copyInProgressDir = bufferDirs.getCopyInProgressDir();
-        moveFromRemoteToLocal(source, sourceHostOrNull, copyInProgressDir);
-        final File destFile = new File(copyInProgressDir, source.getName());
-        if (destFile.exists() == false)
+        moveFromRemoteToLocal(sourceItem, incomingStore, copyInProgressDir);
+        final File copiedFile = new File(copyInProgressDir, sourceItem.getName());
+        if (copiedFile.exists() == false)
         {
             return;
         }
-        final File copiedFile = new File(copyInProgressDir, source.getName());
-        assert copiedFile.exists() : copiedFile.getAbsolutePath();
-        final File markerFile = MarkerFile.createCopyFinishedMarker(copiedFile);
-        assert markerFile.exists() : markerFile.getAbsolutePath();
 
         // 2. Move to final directory, delete marker
-        final File finalFile = tryMoveFromInProgressToFinished(copiedFile, markerFile, bufferDirs.getCopyCompleteDir());
-        if (finalFile == null)
-        {
-            return;
-        }
+        final File markerFile = MarkerFile.createCopyFinishedMarker(copiedFile);
+        tryMoveFromInProgressToFinished(copiedFile, markerFile, bufferDirs.getCopyCompleteDir());
     }
 
     private File tryMoveFromInProgressToFinished(File copiedFile, File markerFileOrNull, File copyCompleteDir)
@@ -183,7 +185,14 @@ public class IncomingProcessor implements IRecoverableTimerTaskFactory
         {
             if (markerFileOrNull != null)
             {
-                markerFileOrNull.delete(); // process even if marker file could not be deleted
+                if (markerFileOrNull.exists() == false)
+                {
+                    operationLog.error("Could not find expected copy-finished-mrker file "
+                            + markerFileOrNull.getAbsolutePath());
+                } else
+                {
+                    markerFileOrNull.delete(); // process even if marker file could not be deleted
+                }
             }
             return finalFile;
         } else
@@ -192,15 +201,15 @@ public class IncomingProcessor implements IRecoverableTimerTaskFactory
         }
     }
 
-    private void moveFromRemoteToLocal(File source, String sourceHostOrNull, File localDestDir)
+    private void moveFromRemoteToLocal(StoreItem sourceItem, FileStore sourceStore, File localDestDir)
     {
-        createRemotePathMover(sourceHostOrNull, localDestDir, null).handle(source);
+        createRemotePathMover(sourceStore, FileStoreFactory.createLocal(localDestDir, "local", factory)).handle(
+                sourceItem);
     }
 
-    private IPathHandler createRemotePathMover(String sourceHost, File destinationDirectory, String destinationHost)
+    private IStoreHandler createRemotePathMover(FileStore sourceDirectory, FileStore destinationDirectory)
     {
-        return RemoteMonitoredMoverFactory.create(sourceHost, destinationDirectory, destinationHost, factory,
-                parameters);
+        return RemoteMonitoredMoverFactory.create(sourceDirectory, destinationDirectory, parameters);
     }
 
     private File tryMoveLocal(File sourceFile, File destinationDir, String prefixTemplate)
@@ -219,7 +228,7 @@ public class IncomingProcessor implements IRecoverableTimerTaskFactory
             {
                 operationLog.debug("Recovery starts.");
             }
-            if (isIncomingRemote)
+            if (incomingStore.isRemote())
             {
                 recoverIncomingInProgress(bufferDirs.getCopyInProgressDir(), bufferDirs.getCopyCompleteDir());
             }
@@ -231,7 +240,7 @@ public class IncomingProcessor implements IRecoverableTimerTaskFactory
 
         private void recoverIncomingInProgress(File copyInProgressDir, File copyCompleteDir)
         {
-            final File[] files = incomingReadOperations.tryListFiles(copyInProgressDir, errorLog);
+            final File[] files = FileUtilities.tryListFiles(copyInProgressDir, errorLog);
             if (files == null || files.length == 0)
             {
                 return; // directory is empty, no recovery is needed
@@ -273,9 +282,7 @@ public class IncomingProcessor implements IRecoverableTimerTaskFactory
                 } else
                 // no marker
                 {
-                    File incomingDir = incomingStore.getPath();
-                    File originalInIncoming = new File(incomingDir, localCopy.getName());
-                    if (incomingReadOperations.exists(originalInIncoming))
+                    if (incomingStore.exists(new StoreItem(localCopy.getName())))
                     {
                         // partial copy - nothing to do, will be copied again
                     } else
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/LocalProcessor.java b/datamover/source/java/ch/systemsx/cisd/datamover/LocalProcessor.java
index 7fb81ec43e2f6e6fee9d9ec591a06ea9f1031688..359e2f477e3f941097d80791153805876106065e 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/LocalProcessor.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/LocalProcessor.java
@@ -34,7 +34,6 @@ import ch.systemsx.cisd.common.utilities.RegexFileFilter.PathType;
 import ch.systemsx.cisd.datamover.filesystem.intf.IFileSysOperationsFactory;
 import ch.systemsx.cisd.datamover.filesystem.intf.IPathImmutableCopier;
 import ch.systemsx.cisd.datamover.filesystem.intf.IPathMover;
-import ch.systemsx.cisd.datamover.filesystem.intf.IReadPathOperations;
 import ch.systemsx.cisd.datamover.filesystem.intf.IRecoverableTimerTaskFactory;
 
 /**
@@ -57,8 +56,6 @@ public class LocalProcessor implements IPathHandler, IRecoverableTimerTaskFactor
 
     private final IPathMover mover;
 
-    private final IReadPathOperations readOperations;
-
     // input: where the data are moved from (for recovery).
     private final File inputDir;
 
@@ -83,7 +80,6 @@ public class LocalProcessor implements IPathHandler, IRecoverableTimerTaskFactor
         this.extraCopyDirOrNull = parameters.tryGetExtraCopyDir();
         this.copier = factory.getImmutableCopier();
         this.mover = factory.getMover();
-        this.readOperations = factory.getReadPathOperations();
     }
 
     public static final LocalProcessor create(Parameters parameters, File inputDir, File outputDir, File bufferDir,
@@ -123,7 +119,7 @@ public class LocalProcessor implements IPathHandler, IRecoverableTimerTaskFactor
 
     private void recoverTemporaryExtraCopy()
     {
-        final File[] files = readOperations.tryListFiles(tempDir, errorLog);
+        final File[] files = FileUtilities.tryListFiles(tempDir, errorLog);
         if (files == null || files.length == 0)
         {
             return; // directory is empty, no recovery is needed
@@ -233,7 +229,7 @@ public class LocalProcessor implements IPathHandler, IRecoverableTimerTaskFactor
     private boolean doCleansing(File resource)
     {
         final RegexFileFilter cleansingFilter = new RegexFileFilter();
-        final Pattern cleansingRegex = parameters.getCleansingRegex();
+        final Pattern cleansingRegex = parameters.tryGetCleansingRegex();
         if (cleansingRegex != null)
         {
             log(resource, "Doing cleansing");
@@ -252,8 +248,13 @@ public class LocalProcessor implements IPathHandler, IRecoverableTimerTaskFactor
 
     private EFileManipResult doManualIntervention(File resource)
     {
+        final File manualInterventionDir = parameters.tryGetManualInterventionDir();
+        if (manualInterventionDir == null)
+        {
+            return EFileManipResult.CONTINUE;
+        }
         final RegexFileFilter manualInterventionFilter = new RegexFileFilter();
-        final Pattern manualInterventionRegex = parameters.getManualInterventionRegex();
+        final Pattern manualInterventionRegex = parameters.tryGetManualInterventionRegex();
         if (manualInterventionRegex != null)
         {
             manualInterventionFilter.add(PathType.ALL, manualInterventionRegex);
@@ -263,7 +264,6 @@ public class LocalProcessor implements IPathHandler, IRecoverableTimerTaskFactor
         if (filtered)
         {
             log(resource, "Moving to manual intervention directory");
-            File manualInterventionDir = parameters.getManualInterventionDirectory();
             File movedFile = mover.tryMove(resource, manualInterventionDir);
             return (movedFile != null) ? EFileManipResult.STOP : EFileManipResult.FAILURE;
         } else
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/Main.java b/datamover/source/java/ch/systemsx/cisd/datamover/Main.java
index a0d5854a8faee93572b8c963df202e88c9ac210d..b19f412d653314aaff8cf72972146d18565bac89 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/Main.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/Main.java
@@ -27,14 +27,15 @@ import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.common.logging.LogInitializer;
 import ch.systemsx.cisd.common.utilities.BuildAndEnvironmentInfo;
 import ch.systemsx.cisd.common.utilities.ITerminable;
+import ch.systemsx.cisd.datamover.filesystem.FileStoreFactory;
 import ch.systemsx.cisd.datamover.filesystem.FileSysOperationsFactory;
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore;
 import ch.systemsx.cisd.datamover.filesystem.intf.IFileSysOperationsFactory;
 import ch.systemsx.cisd.datamover.filesystem.intf.IPathCopier;
-import ch.systemsx.cisd.datamover.utils.FileStore;
 import ch.systemsx.cisd.datamover.utils.LocalBufferDirs;
 
 /**
- * The main class of the datamover.
+ * The main class of the Datamover.
  * 
  * @author Bernd Rinn
  */
@@ -54,16 +55,16 @@ public class Main
         };
 
     private static final Runnable loggingShutdownHook = new Runnable()
-    {
-        public void run()
         {
-            if (operationLog.isInfoEnabled())
+            public void run()
             {
-                operationLog.info("Datamover is shutting down.");
+                if (operationLog.isInfoEnabled())
+                {
+                    operationLog.info("Datamover is shutting down.");
+                }
             }
-        }
-    };
-        
+        };
+
     private static void initLog()
     {
         LogInitializer.init();
@@ -91,17 +92,28 @@ public class Main
     {
         try
         {
-            IPathCopier copyProcess = new FileSysOperationsFactory(parameters).getCopierNoDeletionRequired();
             ArrayList<FileStore> stores = new ArrayList<FileStore>();
-            stores.add(parameters.getIncomingStore());
-            stores.add(parameters.getBufferStore());
-            stores.add(parameters.getOutgoingStore());
-            stores.add(parameters.getManualInterventionStore());
+            FileSysOperationsFactory factory = new FileSysOperationsFactory(parameters);
+            stores.add(parameters.getIncomingStore(factory));
+            FileStore buferStore =
+                    FileStoreFactory.createLocal(parameters.getBufferDirectoryPath(), Parameters.BUFFER_KIND_DESC,
+                            factory);
+            stores.add(buferStore);
+            stores.add(parameters.getOutgoingStore(factory));
+            if (parameters.tryGetManualInterventionDir() != null)
+            {
+                FileStore dummyStore =
+                        FileStoreFactory.createLocal(parameters.tryGetManualInterventionDir(), "manual intervention",
+                                factory);
+                stores.add(dummyStore);
+            }
             if (parameters.tryGetExtraCopyDir() != null)
             {
-                FileStore dummyStore = new FileStore(parameters.tryGetExtraCopyDir(), "extra-copy", null, false);
+                FileStore dummyStore =
+                        FileStoreFactory.createLocal(parameters.tryGetExtraCopyDir(), "extra-copy", factory);
                 stores.add(dummyStore);
             }
+            IPathCopier copyProcess = factory.getCopier(false);
             SelfTest.check(copyProcess, stores.toArray(new FileStore[] {}));
         } catch (HighLevelException e)
         {
@@ -135,7 +147,7 @@ public class Main
         printInitialLogMessage(parameters);
         selfTest(parameters);
         startupServer(parameters);
-        operationLog.info("datamover ready and waiting for data.");
+        operationLog.info("Datamover ready and waiting for data.");
     }
 
 }
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/Parameters.java b/datamover/source/java/ch/systemsx/cisd/datamover/Parameters.java
index 2ad120462c738810e404a828c0c09eb46eb14d1f..bb54617d265ca18011aa01d4bc3df32a9800477e 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/Parameters.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/Parameters.java
@@ -38,9 +38,11 @@ import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.common.utilities.BuildAndEnvironmentInfo;
 import ch.systemsx.cisd.common.utilities.IExitHandler;
 import ch.systemsx.cisd.common.utilities.SystemExit;
+import ch.systemsx.cisd.datamover.filesystem.FileStoreFactory;
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore;
+import ch.systemsx.cisd.datamover.filesystem.intf.IFileSysOperationsFactory;
 import ch.systemsx.cisd.datamover.intf.IFileSysParameters;
 import ch.systemsx.cisd.datamover.intf.ITimingParameters;
-import ch.systemsx.cisd.datamover.utils.FileStore;
 
 /**
  * The class to process the command line parameters.
@@ -49,7 +51,6 @@ import ch.systemsx.cisd.datamover.utils.FileStore;
  */
 public class Parameters implements ITimingParameters, IFileSysParameters
 {
-
     private static final String SERVICE_PROPERTIES_FILE = "etc/service.properties";
 
     private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, Parameters.class);
@@ -197,7 +198,7 @@ public class Parameters implements ITimingParameters, IFileSysParameters
      */
     @Option(longName = "manual-intervention-dir", metaVar = "DIR", usage = "The local directory to "
             + "store paths that need manual intervention.")
-    private File manualInterventionDirectory = null;
+    private File manualInterventionDirectoryOrNull = null;
 
     /**
      * The directory on the remote side to move the paths to from the buffer directory.
@@ -227,26 +228,6 @@ public class Parameters implements ITimingParameters, IFileSysParameters
             + "moving to outgoing.")
     private Pattern cleansingRegex = null;
 
-    /**
-     * The store where the files come in.
-     */
-    private final FileStore incomingStore;
-
-    /**
-     * The store to buffer the files before copying to outgoing.
-     */
-    private final FileStore bufferStore;
-
-    /**
-     * The store to copy the files to.
-     */
-    private final FileStore outgoingStore;
-
-    /**
-     * The store to copy files to that need manual intervention.
-     */
-    private final FileStore manualInterventionStore;
-
     /**
      * The regular expression to use for deciding whether a path in the incoming directory needs manual intervention.
      */
@@ -316,6 +297,14 @@ public class Parameters implements ITimingParameters, IFileSysParameters
 
     }
 
+    static final String INCOMING_KIND_DESC = "incoming";
+
+    static final String MANUAL_INTERVENTION_KIND_DESC = "manual intervention";
+
+    static final String BUFFER_KIND_DESC = "buffer";
+
+    static final String OUTGOING_KIND_DESC = "outgoing";
+
     Parameters(String[] args)
     {
         this(args, SystemExit.SYSTEM_EXIT);
@@ -339,15 +328,11 @@ public class Parameters implements ITimingParameters, IFileSysParameters
             {
                 throw new ConfigurationFailureException("No 'outgoing-dir' defined.");
             }
-            if (manualInterventionDirectory == null && manualInterventionRegex != null)
+            if (manualInterventionDirectoryOrNull == null && manualInterventionRegex != null)
             {
                 throw new ConfigurationFailureException(
                         "No 'manual-intervention-dir' defined, but 'manual-intervention-regex'.");
             }
-            incomingStore = new FileStore(incomingDirectory, "incoming", incomingHost, treatIncomingAsRemote);
-            bufferStore = new FileStore(bufferDirectory, "buffer", null, false);
-            manualInterventionStore = new FileStore(manualInterventionDirectory, "manual intervention", null, false);
-            outgoingStore = new FileStore(outgoingDirectory, "outgoing", outgoingHost, true);
         } catch (Exception ex)
         {
             outputException(ex);
@@ -357,6 +342,25 @@ public class Parameters implements ITimingParameters, IFileSysParameters
         }
     }
 
+    private static FileStore createStore(File directory, String kind, String hostOrNull, boolean isRemote,
+            IFileSysOperationsFactory factory)
+    {
+        if (hostOrNull != null)
+        {
+            assert isRemote == true;
+            return FileStoreFactory.createRemoteHost(directory, hostOrNull, kind, factory);
+        } else
+        {
+            if (isRemote)
+            {
+                return FileStoreFactory.createRemoteShare(directory, kind, factory);
+            } else
+            {
+                return FileStoreFactory.createLocal(directory, kind, factory);
+            }
+        }
+    }
+
     private void outputException(Exception ex)
     {
         if (ex instanceof HighLevelException || ex instanceof CmdLineException)
@@ -412,7 +416,7 @@ public class Parameters implements ITimingParameters, IFileSysParameters
         }
         if (serviceProperties.getProperty("manual-intervention-dir") != null)
         {
-            manualInterventionDirectory = new File(serviceProperties.getProperty("manual-intervention-dir"));
+            manualInterventionDirectoryOrNull = new File(serviceProperties.getProperty("manual-intervention-dir"));
         }
         if (serviceProperties.getProperty("outgoing-dir") != null)
         {
@@ -548,59 +552,41 @@ public class Parameters implements ITimingParameters, IFileSysParameters
     /**
      * @return The store to monitor for new files and directories to move to the buffer.
      */
-    public FileStore getIncomingStore()
+    public FileStore getIncomingStore(IFileSysOperationsFactory factory)
     {
-        return incomingStore;
-    }
-
-    /**
-     * @return true if directory with incoming data is supposed to be on a remote share. It implies that a special care
-     *         will be taken when coping is performed from that directory.
-     */
-    public boolean getTreatIncomingAsRemote()
-    {
-        return treatIncomingAsRemote;
+        return createStore(incomingDirectory, INCOMING_KIND_DESC, incomingHost, treatIncomingAsRemote, factory);
     }
 
     /**
      * @return The directory for local files and directories manipulations.
      */
-    public FileStore getBufferStore()
+    public File getBufferDirectoryPath()
     {
-        return bufferStore;
+        return bufferDirectory;
     }
 
     /**
      * @return The store to copy the data to.
      */
-    public FileStore getOutgoingStore()
+    public FileStore getOutgoingStore(IFileSysOperationsFactory factory)
     {
-        return outgoingStore;
+        return createStore(outgoingDirectory, OUTGOING_KIND_DESC, outgoingHost, true, factory);
     }
 
     /**
      * @return The directory to move files and directories to that have been quiet in the local data directory for long
      *         enough and that need manual intervention. Note that this directory needs to be on the same file system as
-     *         {@link #getBufferStore}.
-     */
-    public File getManualInterventionDirectory()
-    {
-        return manualInterventionDirectory;
-    }
-
-    /**
-     * @return The store to move files and directories to that have been quiet in the local data directory for long
-     *         enough and that need manual intervention. Note that this directory needs to be on the same file system as
-     *         {@link #getBufferStore}.
+     *         {@link #getBufferDirectoryPath}.
      */
-    public FileStore getManualInterventionStore()
+    public File tryGetManualInterventionDir()
     {
-        return manualInterventionStore;
+        return manualInterventionDirectoryOrNull;
     }
 
     /**
      * @return The directory where we create an additional copy of incoming data or <code>null</code> if it is not
-     *         specified. Note that this directory needs to be on the same file system as {@link #getBufferStore}.
+     *         specified. Note that this directory needs to be on the same file system as
+     *         {@link #getBufferDirectoryPath}.
      */
     public File tryGetExtraCopyDir()
     {
@@ -611,7 +597,7 @@ public class Parameters implements ITimingParameters, IFileSysParameters
      * @return The regular expression to use for cleansing on the incoming directory before moving it to the buffer or
      *         <code>null</code>, if no regular expression for cleansing has been provided.
      */
-    public Pattern getCleansingRegex()
+    public Pattern tryGetCleansingRegex()
     {
         return cleansingRegex;
     }
@@ -621,7 +607,7 @@ public class Parameters implements ITimingParameters, IFileSysParameters
      *         intervention or <code>null</code>, if no regular expression for manual interventionpaths has been
      *         provided.
      */
-    public Pattern getManualInterventionRegex()
+    public Pattern tryGetManualInterventionRegex()
     {
         return manualInterventionRegex;
     }
@@ -654,9 +640,9 @@ public class Parameters implements ITimingParameters, IFileSysParameters
             {
                 operationLog.info(String.format("Outgoing host: '%s'.", outgoingHost));
             }
-            if (null != getManualInterventionDirectory())
+            if (null != tryGetManualInterventionDir())
             {
-                operationLog.info(String.format("Manual interventions directory: '%s'.", manualInterventionDirectory
+                operationLog.info(String.format("Manual interventions directory: '%s'.", tryGetManualInterventionDir()
                         .getAbsolutePath()));
             }
             if (null != extraCopyDirectory)
@@ -671,16 +657,16 @@ public class Parameters implements ITimingParameters, IFileSysParameters
             operationLog.info(String.format("Intervall to wait after failure: %d s.",
                     getIntervalToWaitAfterFailure() / 1000));
             operationLog.info(String.format("Maximum number of retries: %d.", getMaximalNumberOfRetries()));
-            if (getCleansingRegex() != null)
+            if (tryGetCleansingRegex() != null)
             {
                 operationLog.info(String.format("Regular expression used for cleansing before moving: '%s'",
-                        getCleansingRegex().pattern()));
+                        tryGetCleansingRegex().pattern()));
             }
-            if (getManualInterventionRegex() != null)
+            if (tryGetManualInterventionRegex() != null)
             {
                 operationLog.info(String.format(
                         "Regular expression used for deciding whether a path needs manual intervention: '%s'",
-                        getManualInterventionRegex().pattern()));
+                        tryGetManualInterventionRegex().pattern()));
             }
         }
     }
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/SelfTest.java b/datamover/source/java/ch/systemsx/cisd/datamover/SelfTest.java
index 2144ec374dcdc722e60130969255790cd56fa5a7..3159930379b21373499cea42f7a11b4454939c89 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/SelfTest.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/SelfTest.java
@@ -16,7 +16,6 @@
 
 package ch.systemsx.cisd.datamover;
 
-import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
 
 import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
@@ -25,9 +24,8 @@ import ch.systemsx.cisd.common.exceptions.HighLevelException;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.common.logging.LogInitializer;
-import ch.systemsx.cisd.common.utilities.FileUtilities;
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore;
 import ch.systemsx.cisd.datamover.filesystem.intf.IPathCopier;
-import ch.systemsx.cisd.datamover.utils.FileStore;
 
 /**
  * A class that can perform a self test of the data mover.
@@ -43,23 +41,17 @@ public class SelfTest
         LogInitializer.init();
     }
 
-    private static void checkPathRecords(FileStore[] pathRecords, IPathCopier copier)
+    private static void checkPathRecords(FileStore[] pathRecords)
     {
         assert pathRecords != null;
 
         checkPathRecordsContainEachOther(pathRecords);
         for (FileStore pathRecord : pathRecords)
         {
-            if (pathRecord.getPath() == null)
+            String errorMessage = pathRecord.tryCheckDirectoryFullyAccessible();
+            if (errorMessage != null)
             {
-                continue;
-            }
-            if (pathRecord.getHost() == null)
-            {
-                checkDirectoryOnLocalHost(pathRecord);
-            } else
-            {
-                checkDirectoryOnRemoteHost(pathRecord, copier);
+                throw new ConfigurationFailureException(errorMessage);
             }
         }
     }
@@ -70,52 +62,15 @@ public class SelfTest
         {
             for (int j = 0; j < i; ++j)
             {
-                if (StringUtils.equals(store[i].getHost(), store[j].getHost())
-                        && containOneAnother(store[i].getCanonicalPath(), store[j].getCanonicalPath()))
+                if (store[i].isParentDirectory(store[j]) || store[j].isParentDirectory(store[i]))
                 {
                     throw ConfigurationFailureException.fromTemplate("Directory '%s' and '%s' contain each other",
-                            store[i].getCanonicalPath(), store[j].getCanonicalPath());
+                            store[i], store[j]);
                 }
             }
         }
     }
 
-    private static void checkDirectoryOnRemoteHost(FileStore pathRecord, IPathCopier copier)
-            throws ConfigurationFailureException
-    {
-        if (false == copier.exists(pathRecord.getPath(), pathRecord.getHost()))
-
-        {
-            throw ConfigurationFailureException.fromTemplate("Cannot access %s directory '%s' on host '%s'", pathRecord
-                    .getKind(), pathRecord.getCanonicalPath(), pathRecord.getHost());
-        }
-    }
-
-    private static void checkDirectoryOnLocalHost(FileStore pathRecord)
-    {
-        String errorMessage = FileUtilities.checkDirectoryFullyAccessible(pathRecord.getPath(), pathRecord.getKind());
-        if (errorMessage != null)
-        {
-            throw new ConfigurationFailureException(errorMessage);
-        }
-
-    }
-
-    private static boolean containOneAnother(String directory1, String directory2)
-    {
-        if (directory1 == null || directory2 == null)
-        {
-            return false;
-        }
-        if (directory1.length() < directory2.length())
-        {
-            return directory2.startsWith(directory1);
-        } else
-        {
-            return directory1.startsWith(directory2);
-        }
-    }
-
     /**
      * Will perform all checks of the self-test. If the method returns without exception, the self-test can be
      * considered "past", otherwise the exception will have more information on what went wrong. This method performs
@@ -126,7 +81,7 @@ public class SelfTest
         try
         {
             copier.check();
-            checkPathRecords(stores, copier);
+            checkPathRecords(stores);
 
             if (operationLog.isInfoEnabled())
             {
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/common/MarkerFile.java b/datamover/source/java/ch/systemsx/cisd/datamover/common/MarkerFile.java
index f934a77b143a244e3f16f179c5ef26e6238f4ba7..6579c609223978d752a1ff41fae98d775dad5a19 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/common/MarkerFile.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/common/MarkerFile.java
@@ -34,9 +34,9 @@ public class MarkerFile
         return Constants.IS_FINISHED_PREFIX + originalFileName;
     }
 
-    public static File createDeletionInProgressMarker(File parent, String originalFileName)
+    public static StoreItem createDeletionInProgressMarker(StoreItem originalItem)
     {
-        return new File(parent, getDeletionInProgressMarkerName(originalFileName));
+        return new StoreItem(getDeletionInProgressMarkerName(originalItem.getName()));
     }
 
     private static String getDeletionInProgressMarkerName(String originalFileName)
@@ -69,4 +69,14 @@ public class MarkerFile
     {
         return new File(parent, getCopyFinishedMarkerName(originalFileName));
     }
+
+    public static StoreItem createCopyFinishedMarker(StoreItem originalItem)
+    {
+        return new StoreItem(getCopyFinishedMarkerName(originalItem.getName()));
+    }
+
+    public static StoreItem createRequiresDeletionBeforeCreationMarker()
+    {
+        return new StoreItem(".requiresDeletionBeforeCreation");
+    }
 }
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/common/StoreItem.java b/datamover/source/java/ch/systemsx/cisd/datamover/common/StoreItem.java
new file mode 100644
index 0000000000000000000000000000000000000000..160420d17e7d1114a13d4509094787f7a5f47d0d
--- /dev/null
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/common/StoreItem.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2007 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.datamover.common;
+
+/**
+ * @author Tomasz Pylak on Oct 8, 2007
+ */
+public class StoreItem
+{
+    private final String name;
+
+    public StoreItem(String name)
+    {
+        this.name = name;
+    }
+
+    /** Should not be used for logging. Use toString() instead. */
+    public String getName()
+    {
+        return name;
+    }
+
+    @Override
+    public String toString()
+    {
+        return name;
+    }
+}
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/FileStoreFactory.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/FileStoreFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e608bb546523a2ba7260a4052186770b5eed105
--- /dev/null
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/FileStoreFactory.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2007 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.datamover.filesystem;
+
+import java.io.File;
+
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore;
+import ch.systemsx.cisd.datamover.filesystem.intf.IFileSysOperationsFactory;
+import ch.systemsx.cisd.datamover.filesystem.store.FileStoreLocal;
+import ch.systemsx.cisd.datamover.filesystem.store.FileStoreRemote;
+import ch.systemsx.cisd.datamover.filesystem.store.FileStoreRemoteMounted;
+
+/**
+ * @author Tomasz Pylak on Oct 11, 2007
+ */
+public class FileStoreFactory
+{
+    /** use when file store is on a local host */
+    public static final FileStore createLocal(File path, String kind, IFileSysOperationsFactory factory)
+    {
+        return new FileStoreLocal(path, kind, factory);
+    }
+
+    /** use when file store is on a remote share mounted on local host */
+    public static final FileStore createRemoteShare(File path, String kind, IFileSysOperationsFactory factory)
+    {
+        return new FileStoreRemoteMounted(path, kind, factory);
+    }
+
+    /**
+     * use when file store is on a remote share mounted on local host
+     * 
+     * @param factory
+     */
+    public static final FileStore createRemoteHost(File path, String host, String kind,
+            IFileSysOperationsFactory factory)
+    {
+        return new FileStoreRemote(path, host, kind, factory);
+    }
+}
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/FileSysOperationsFactory.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/FileSysOperationsFactory.java
index cd51c56a8501d7f6cfc8f4361296d17f8e98b8f8..a4cb8c7bbc73ef2b2a6451b50f791b50e7da29fc 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/FileSysOperationsFactory.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/FileSysOperationsFactory.java
@@ -17,18 +17,14 @@
 package ch.systemsx.cisd.datamover.filesystem;
 
 import java.io.File;
-import java.io.FileFilter;
-import java.io.IOException;
 
 import org.apache.log4j.Logger;
 
 import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
 import ch.systemsx.cisd.common.exceptions.Status;
 import ch.systemsx.cisd.common.exceptions.StatusFlag;
-import ch.systemsx.cisd.common.logging.ISimpleLogger;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
-import ch.systemsx.cisd.common.utilities.FileUtilities;
 import ch.systemsx.cisd.common.utilities.OSUtilities;
 import ch.systemsx.cisd.datamover.filesystem.impl.RecursiveHardLinkMaker;
 import ch.systemsx.cisd.datamover.filesystem.intf.IFileSysOperationsFactory;
@@ -36,7 +32,6 @@ import ch.systemsx.cisd.datamover.filesystem.intf.IPathCopier;
 import ch.systemsx.cisd.datamover.filesystem.intf.IPathImmutableCopier;
 import ch.systemsx.cisd.datamover.filesystem.intf.IPathMover;
 import ch.systemsx.cisd.datamover.filesystem.intf.IPathRemover;
-import ch.systemsx.cisd.datamover.filesystem.intf.IReadPathOperations;
 import ch.systemsx.cisd.datamover.filesystem.remote.rsync.RsyncCopier;
 import ch.systemsx.cisd.datamover.intf.IFileSysParameters;
 
@@ -54,8 +49,6 @@ public class FileSysOperationsFactory implements IFileSysOperationsFactory
     private static final Logger notificationLog =
             LogFactory.getLogger(LogCategory.NOTIFY, FileSysOperationsFactory.class);
 
-    private static final Logger machineLog = LogFactory.getLogger(LogCategory.MACHINE, FileSysOperationsFactory.class);
-
     final private IFileSysParameters parameters;
 
     public FileSysOperationsFactory(IFileSysParameters parameters)
@@ -70,33 +63,6 @@ public class FileSysOperationsFactory implements IFileSysOperationsFactory
         return new RetryingPathRemover(MAX_RETRIES_ON_FAILURE, MILLIS_TO_SLEEP_ON_FAILURE);
     }
 
-    public IReadPathOperations getReadPathOperations()
-    {
-        return new IReadPathOperations()
-            {
-
-                public boolean exists(File file)
-                {
-                    return file.exists();
-                }
-
-                public long lastChanged(File path)
-                {
-                    return FileUtilities.lastChanged(path);
-                }
-
-                public File[] tryListFiles(File directory, FileFilter filter, ISimpleLogger loggerOrNull)
-                {
-                    return FileUtilities.tryListFiles(directory, filter, loggerOrNull);
-                }
-
-                public File[] tryListFiles(File directory, ISimpleLogger loggerOrNull)
-                {
-                    return FileUtilities.tryListFiles(directory, FileUtilities.ACCEPT_ALL_FILTER, loggerOrNull);
-                }
-            };
-    }
-
     public IPathImmutableCopier getImmutableCopier()
     {
         String lnExec = parameters.getHardLinkExecutable();
@@ -119,7 +85,7 @@ public class FileSysOperationsFactory implements IFileSysOperationsFactory
 
     private IPathImmutableCopier createFakedImmCopier()
     {
-        final IPathCopier normalCopier = suggestPathCopier(false);
+        final IPathCopier normalCopier = getCopier(false);
         return new IPathImmutableCopier()
             {
                 public File tryCopy(File file, File destinationDirectory)
@@ -138,7 +104,7 @@ public class FileSysOperationsFactory implements IFileSysOperationsFactory
             };
     }
 
-    private IPathCopier suggestPathCopier(boolean requiresDeletionBeforeCreation)
+    public IPathCopier getCopier(boolean requiresDeletionBeforeCreation)
     {
         final File rsyncExecutable = findRsyncExecutable(parameters.getRsyncExecutable());
         final File sshExecutable = findSshExecutable(parameters.getSshExecutable());
@@ -198,76 +164,6 @@ public class FileSysOperationsFactory implements IFileSysOperationsFactory
         return sshExecutable;
     }
 
-    /**
-     * @return <code>true</code> if the <var>copyProcess</var> on the file system where the <var>destinationDirectory</var>
-     *         resides requires deleting an existing file before it can be overwritten.
-     */
-    private static boolean requiresDeletionBeforeCreation(IPathCopier copyProcess, File destinationDirectory)
-    {
-        assert copyProcess != null;
-        assert destinationDirectory != null;
-        assert destinationDirectory.isDirectory();
-
-        String fileName = ".requiresDeletionBeforeCreation";
-        final File destinationFile = new File(destinationDirectory, fileName);
-        final File tmpSourceDir = new File(destinationDirectory, ".DataMover-OverrideTest");
-        final File sourceFile = new File(tmpSourceDir, fileName);
-        try
-        {
-            tmpSourceDir.mkdir();
-            sourceFile.createNewFile();
-            destinationFile.createNewFile();
-            // If we have e.g. a Cellera NAS server, the next call will raise an IOException.
-            final boolean OK = Status.OK.equals(copyProcess.copy(sourceFile, destinationDirectory));
-            if (machineLog.isInfoEnabled())
-            {
-                if (OK)
-                {
-                    machineLog.info(String.format("Copier %s on directory '%s' works with overwriting existing files.",
-                            copyProcess.getClass().getSimpleName(), destinationDirectory.getAbsolutePath()));
-                } else
-                {
-                    machineLog.info(String.format(
-                            "Copier %s on directory '%s' requires deletion before creation of existing files.",
-                            copyProcess.getClass().getSimpleName(), destinationDirectory.getAbsolutePath()));
-                }
-            }
-            return (OK == false);
-        } catch (IOException e)
-        {
-            if (machineLog.isInfoEnabled())
-            {
-                machineLog.info(String.format(
-                        "The file system on '%s' requires deletion before creation of existing files.",
-                        destinationDirectory.getAbsolutePath()));
-            }
-            return true;
-        } finally
-        {
-            // We don't check for success because there is nothing we can do if we fail.
-            sourceFile.delete();
-            tmpSourceDir.delete();
-            destinationFile.delete();
-        }
-    }
-
-    public IPathCopier getCopierNoDeletionRequired()
-    {
-        return suggestPathCopier(false);
-    }
-
-    public IPathCopier getCopier(File destinationDirectory)
-    {
-        IPathCopier copyProcess = suggestPathCopier(false);
-        if (requiresDeletionBeforeCreation(copyProcess, destinationDirectory))
-        {
-            return suggestPathCopier(true);
-        } else
-        {
-            return copyProcess;
-        }
-    }
-
     public IPathMover getMover()
     {
         return new RetryingPathMover(MAX_RETRIES_ON_FAILURE, MILLIS_TO_SLEEP_ON_FAILURE);
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/RemoteMonitoredMoverFactory.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/RemoteMonitoredMoverFactory.java
index b6e16c22ca6330d3f8535c59b31d130046cac7ca..a221dbd60f15ad123e914ed629870988f62b1f2e 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/RemoteMonitoredMoverFactory.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/RemoteMonitoredMoverFactory.java
@@ -16,12 +16,9 @@
 
 package ch.systemsx.cisd.datamover.filesystem;
 
-import java.io.File;
-
-import ch.systemsx.cisd.common.utilities.IPathHandler;
-import ch.systemsx.cisd.datamover.filesystem.intf.IFileSysOperationsFactory;
-import ch.systemsx.cisd.datamover.filesystem.intf.IPathCopier;
-import ch.systemsx.cisd.datamover.filesystem.intf.IPathRemover;
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore;
+import ch.systemsx.cisd.datamover.filesystem.intf.IStoreCopier;
+import ch.systemsx.cisd.datamover.filesystem.intf.IStoreHandler;
 import ch.systemsx.cisd.datamover.filesystem.remote.CopyActivityMonitor;
 import ch.systemsx.cisd.datamover.filesystem.remote.RemotePathMover;
 import ch.systemsx.cisd.datamover.intf.ITimingParameters;
@@ -32,24 +29,17 @@ import ch.systemsx.cisd.datamover.intf.ITimingParameters;
 public class RemoteMonitoredMoverFactory
 {
     /**
-     * Creates a handler to move files remotely and monitor the progress
+     * Creates a handler to move files remotely from source to destination and monitor the progress
      * 
-     * @param sourceHost The host to move paths from, or <code>null</code>, if data will be moved from the local file
-     *            system
+     * @param sourceDirectory The directory to move paths from
      * @param destinationDirectory The directory to move paths to.
-     * @param destinationHost The host to move paths to, or <code>null</code>, if <var>destinationDirectory</var> is
-     *            a remote share.
-     * @param fsFactory operations on (remote) file system
      * @param parameters The timing parameters used for monitoring and reporting stall situations.
      */
-    public static final IPathHandler create(String sourceHost, File destinationDirectory, String destinationHost,
-            IFileSysOperationsFactory fsFactory, ITimingParameters parameters)
+    public static final IStoreHandler create(FileStore sourceDirectory, FileStore destinationDirectory,
+            ITimingParameters parameters)
     {
-        final IPathCopier copier = fsFactory.getCopier(destinationDirectory);
-        final CopyActivityMonitor monitor =
-                new CopyActivityMonitor(destinationDirectory, fsFactory.getReadPathOperations(), copier, parameters);
-        final IPathRemover remover = fsFactory.getRemover();
-        return new RemotePathMover(destinationDirectory, destinationHost, monitor, remover, copier, sourceHost,
-                parameters);
+        final IStoreCopier copier = sourceDirectory.getCopier(destinationDirectory);
+        final CopyActivityMonitor monitor = new CopyActivityMonitor(destinationDirectory, copier, parameters);
+        return new RemotePathMover(sourceDirectory, destinationDirectory, copier, monitor, parameters);
     }
 }
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/RetryingPathMover.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/RetryingPathMover.java
index 01eb0d11254deb590e534a5bea257caf13bdfe8b..edcc3d6f6499f5227501ed6fdb6260e267c217be 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/RetryingPathMover.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/RetryingPathMover.java
@@ -37,9 +37,9 @@ class RetryingPathMover implements IPathMover
     private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, RetryingPathMover.class);
 
     private static final Logger notificationLog = LogFactory.getLogger(LogCategory.NOTIFY, RetryingPathMover.class);
-    
+
     private final int maxRetriesOnFailure;
-    
+
     private final long millisToSleepOnFailure;
 
     RetryingPathMover(int maxRetriesOnFailure, long millisToSleepOnFailure)
@@ -47,7 +47,7 @@ class RetryingPathMover implements IPathMover
         this.maxRetriesOnFailure = maxRetriesOnFailure;
         this.millisToSleepOnFailure = millisToSleepOnFailure;
     }
-    
+
     public File tryMove(File sourceFile, File destinationDir)
     {
         return tryMove(sourceFile, destinationDir, "");
@@ -69,13 +69,14 @@ class RetryingPathMover implements IPathMover
         File destFile = new File(destinationPath);
         int failures = 0;
         boolean movedOK = false;
-        while(true)
+        while (true)
         {
             movedOK = sourcePath.renameTo(destFile);
             if (movedOK)
             {
                 break;
-            } else {
+            } else
+            {
                 if (sourcePath.exists() == false)
                 {
                     operationLog.error(String.format("Path '%s' doesn't exist, so it can't be moved to '%s'.",
@@ -138,5 +139,4 @@ class RetryingPathMover implements IPathMover
         return StringUtils.replace(prefixTemplate, "%t", DateFormatUtils.format(System.currentTimeMillis(),
                 "yyyyMMddHHmmss"));
     }
-
 }
\ No newline at end of file
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/RetryingPathRemover.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/RetryingPathRemover.java
index b90373164e2056d09767c1353a37a9394bcff480..584f5a83a02afa31426a2f97dc11cbd310e0aded 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/RetryingPathRemover.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/RetryingPathRemover.java
@@ -46,13 +46,14 @@ final class RetryingPathRemover implements IPathRemover
         }
         int failures = 0;
         boolean deletionOK = false;
-        while(true)
+        while (true)
         {
             deletionOK = FileUtilities.deleteRecursively(path);
             if (deletionOK)
             {
                 break;
-            } else {
+            } else
+            {
                 if (path.exists() == false)
                 {
                     operationLog.warn(String.format("Path '%s' doesn't exist, so it can't be removed.", path));
@@ -73,14 +74,14 @@ final class RetryingPathRemover implements IPathRemover
                 }
             }
         }
-        
+
         if (deletionOK == false)
         {
             notificationLog.error(String.format("Removing path '%s' failed, giving up.", path));
             return STATUS_FAILED_DELETION;
         } else
         {
-            return Status.OK; 
+            return Status.OK;
         }
     }
 }
\ No newline at end of file
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/FileStore.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/FileStore.java
new file mode 100644
index 0000000000000000000000000000000000000000..34cde46930de3db12c2260723b4acdf37193021e
--- /dev/null
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/FileStore.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2007 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.datamover.filesystem.intf;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.commons.lang.StringUtils;
+
+import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
+import ch.systemsx.cisd.common.exceptions.Status;
+import ch.systemsx.cisd.datamover.common.StoreItem;
+
+/**
+ * A class to holds the information about a file store.
+ * 
+ * @author Bernd Rinn
+ * @author Tomasz Pylak
+ */
+public abstract class FileStore
+{
+    protected final File path;
+
+    protected final String hostOrNull;
+
+    protected final String kind;
+
+    protected final boolean remote;
+
+    protected final IFileSysOperationsFactory factory;
+
+    protected FileStore(File path, String hostOrNull, boolean remote, String kind, IFileSysOperationsFactory factory)
+    {
+        assert path != null;
+        assert kind != null;
+        this.path = path;
+        this.kind = kind;
+        this.hostOrNull = hostOrNull;
+        this.remote = remote;
+        this.factory = factory;
+    }
+
+    // TODO 2007-10-10 Tomasz Pylak: change visibility to protected after changing DirectoryScanningTimerTask
+    public final File getPath()
+    {
+        return path;
+    }
+
+    protected final String tryGetHost()
+    {
+        return hostOrNull;
+    }
+
+    protected final String getDescription()
+    {
+        return kind;
+    }
+
+    protected final File getChildFile(StoreItem item)
+    {
+        return new File(path, item.getName());
+    }
+
+    // does not take into account the fact, that the destination cannot be overwritten and must be deleted beforehand
+    protected final IStoreCopier constructStoreCopier(FileStore destinationDirectory,
+            boolean requiresDeletionBeforeCreation)
+    {
+        final IPathCopier copier = factory.getCopier(requiresDeletionBeforeCreation);
+        final String srcHostOrNull = hostOrNull;
+        final String destHostOrNull = destinationDirectory.hostOrNull;
+        final File destPath = destinationDirectory.path;
+        return new IStoreCopier()
+            {
+                public Status copy(StoreItem item)
+                {
+                    File srcItem = getChildFile(item);
+                    if (srcHostOrNull == null)
+                    {
+                        if (destHostOrNull == null)
+                        {
+                            return copier.copy(srcItem, destPath);
+                        } else
+                        {
+                            return copier.copyToRemote(srcItem, destPath, destHostOrNull);
+                        }
+                    } else
+                    {
+                        assert destHostOrNull == null;
+                        return copier.copyFromRemote(srcItem, srcHostOrNull, destPath);
+                    }
+                }
+
+                public boolean terminate()
+                {
+                    return copier.terminate();
+                }
+            };
+    }
+
+    /**
+     * Returns <code>true</code>, if the file store resides on a remote computer and <code>false</code> otherwise.
+     * <p>
+     * Note that the resource does not have to be on a remote host. Instead it can reside on a remote share mounted on
+     * local host via NFS or CIFS.
+     */
+    public final boolean isRemote()
+    {
+        return remote;
+    }
+
+    public boolean isParentDirectory(FileStore child)
+    {
+        return StringUtils.equals(hostOrNull, child.hostOrNull)
+                && getCanonicalPath(child.path).startsWith(getCanonicalPath(path));
+    }
+
+    private String getCanonicalPath(File file)
+    {
+        if (hostOrNull != null)
+        {
+            return file.getPath();
+        }
+        try
+        {
+            return file.getCanonicalPath() + File.separator;
+        } catch (IOException e)
+        {
+            throw EnvironmentFailureException.fromTemplate(e, "Cannot determine canonical form of path '%s'", file
+                    .getPath());
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (obj instanceof FileStore)
+        {
+            FileStore fileStore = (FileStore) obj;
+            boolean sameHost =
+                    (hostOrNull == null ? fileStore.hostOrNull == null : fileStore.hostOrNull != null
+                            && hostOrNull.equals(fileStore.hostOrNull));
+            return sameHost && kind.equals(fileStore.kind) && path.equals(fileStore.path);
+        } else
+        {
+            return false;
+        }
+    }
+
+    // -------------------
+
+    /**
+     * Checks whether this store is a directory and is fully accessible to the program.
+     * 
+     * @return <code>null</code> if the <var>directory</var> is fully accessible and an error message describing the
+     *         problem with the <var>directory</var> otherwise.
+     */
+    public abstract String tryCheckDirectoryFullyAccessible();
+
+    public abstract boolean exists(StoreItem item);
+
+    /**
+     * Returns the last time when there was a write access to <var>resource</var>.
+     * 
+     * @return The time (in milliseconds since the start of the epoch) when <var>resource</var> was last changed.
+     */
+    public abstract long lastChanged(StoreItem item);
+
+    public abstract Status delete(StoreItem item);
+
+    /**
+     * @param destinationDirectory The directory to use as a destination in the copy operation. It must be readable and
+     *            writable. Copier will override the destination item if it already exists.
+     */
+    public abstract IStoreCopier getCopier(FileStore destinationDirectory);
+
+    // public boolean isParentDirectory(FileStoreIntf child);
+
+    public abstract ExtendedFileStore tryAsExtended();
+
+    public static abstract class ExtendedFileStore extends FileStore
+    {
+        protected ExtendedFileStore(File path, String hostOrNull, boolean remote, String kind,
+                IFileSysOperationsFactory factory)
+        {
+            super(path, hostOrNull, remote, kind, factory);
+        }
+
+        public abstract boolean createNewFile(StoreItem item);
+
+        public abstract File tryMoveLocal(StoreItem sourceItem, File destinationDir, String newFilePrefix);
+    }
+}
\ No newline at end of file
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IFileSysOperationsFactory.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IFileSysOperationsFactory.java
index 884995dbd44a430f7e85ba495e6bc9f5bfbdb610..e59f627170e19a5b5fdffb30eac9c9c1c65109d7 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IFileSysOperationsFactory.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IFileSysOperationsFactory.java
@@ -15,7 +15,6 @@
  */
 package ch.systemsx.cisd.datamover.filesystem.intf;
 
-import java.io.File;
 
 /**
  * A role that provides access to the roles which perform file system operations.
@@ -24,13 +23,11 @@ import java.io.File;
  */
 public interface IFileSysOperationsFactory
 {
-    public IPathCopier getCopier(File destinationDirectory);
+    public IPathCopier getCopier(boolean requiresDeletionBeforeCreation);
 
     public IPathImmutableCopier getImmutableCopier();
 
     public IPathRemover getRemover();
-    
-    public IPathMover getMover();
 
-    public IReadPathOperations getReadPathOperations();
+    public IPathMover getMover();
 }
\ No newline at end of file
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IPathCopier.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IPathCopier.java
index 8f67a852b41ac054529951a6ed056b71c3b8e06a..36abcb3c54b9ace47372d59df1de5c86eb22f7a2 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IPathCopier.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IPathCopier.java
@@ -29,35 +29,45 @@ import ch.systemsx.cisd.common.utilities.ITerminable;
  * <i>Note: If the copier is terminated, the <var>destinationDirectory</var> is in an undefined state afterwards.</i>
  * 
  * @author Bernd Rinn
+ * @author Tomasz Pylak
  */
 public interface IPathCopier extends ITerminable, ISelfTestable
 {
 
     /**
-     * @return <code>true</code> iff <var>destinationDirectory</var> on host <var>destinationHost</var> exists.
+     * Copies <var>sourcePath</var> to <var>destinationDir</var>.
+     * 
+     * @param sourcePath The source to copy. Can be a file or a directory. It needs to exist and be readable.
+     * @param destinationDirectory The directory to use as a destination in the copy operation. It must be readable and
+     *            writable. If <var>destinationDir/sourcePath</var> exists, it will be overwritten.
+     * @return The status of the operation, {@link Status#OK} if everything went OK.
      */
-    public boolean exists(File destinationDirectory, String destinationHost);
+    Status copy(File sourcePath, File destinationDirectory);
 
     /**
-     * Copies <var>sourcePath</var> to <var>destinationDir</var>.
+     * Copies <var>sourcePath</var> to <var>destinationDir</var> on <var>destinationHost</var>.
      * 
      * @param sourcePath The source to copy. Can be a file or a directory. It needs to exist and be readable.
      * @param destinationDirectory The directory to use as a destination in the copy operation. It must be readable and
      *            writable. If <var>destinationDir/sourcePath</var> exists, it will be overwritten.
+     * @param destinationHost The host where the <var>destinationDirectory</var> resides or null if it is local.
      * @return The status of the operation, {@link Status#OK} if everything went OK.
      */
-    public Status copy(File sourcePath, File destinationDirectory);
+    Status copyToRemote(File sourcePath, File destinationDirectory, String destinationHost);
 
     /**
-     * Copies <var>sourcePath</var> to <var>destinationDir</var> on <var>destinationHost</var>.
+     * Copies <var>sourcePath</var> on <var>sourceHost</var> to <var>destinationDir</var>.
      * 
      * @param sourcePath The source to copy. Can be a file or a directory. It needs to exist and be readable.
-     * @param sourceHost The host where the <var>sourcePath</var> resides or null if it is local.
+     * @param sourceHost The host where the <var>sourcePath</var> resides
      * @param destinationDirectory The directory to use as a destination in the copy operation. It must be readable and
      *            writable. If <var>destinationDir/sourcePath</var> exists, it will be overwritten.
-     * @param destinationHost The host where the <var>destinationDirectory</var> resides  or null if it is local.
      * @return The status of the operation, {@link Status#OK} if everything went OK.
      */
-    public Status copy(File sourcePath, String sourceHost, File destinationDirectory, String destinationHost);
+    Status copyFromRemote(File sourcePath, String sourceHost, File destinationDirectory);
 
+    /**
+     * @return <code>true</code> iff <var>destinationDirectory</var> on host <var>destinationHost</var> exists.
+     */
+    public boolean existsRemotely(File destinationDirectory, String destinationHost);
 }
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IReadPathOperations.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IReadPathOperations.java
deleted file mode 100644
index e4f12636828dbaf26d7ad59ff92321806223f40a..0000000000000000000000000000000000000000
--- a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IReadPathOperations.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2007 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.datamover.filesystem.intf;
-
-import java.io.File;
-import java.io.FileFilter;
-
-import ch.systemsx.cisd.common.logging.ISimpleLogger;
-
-/**
- * Allows for checking the status of the file system. Performs only read-only operations.
- * 
- * @author Tomasz Pylak on Aug 27, 2007
- */
-public interface IReadPathOperations
-{
-    /**
-     * lists files in a given directory matching a given filter
-     * 
-     * @param loggerOrNull logger, if <code>null</code> than no logging occurs
-     */
-    File[] tryListFiles(File directory, FileFilter filter, ISimpleLogger loggerOrNull);
-
-    /** lists all files in a given directory, logs errors */
-    File[] tryListFiles(File directory, ISimpleLogger logger);
-
-    /** checks if a file exists */
-    boolean exists(File file);
-
-    /**
-     * Returns the last time when there was a write access to <var>resource</var>.
-     * 
-     * @param resource The path to check for last write activity.
-     * @return The time (in milliseconds since the start of the epoch) when <var>resource</var> was last changed.
-     */
-    long lastChanged(File resource);
-
-}
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IStoreCopier.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IStoreCopier.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d289a364d67a9a4b24f6a225c94d710882f0be7
--- /dev/null
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IStoreCopier.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2007 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.datamover.filesystem.intf;
+
+import ch.systemsx.cisd.common.exceptions.Status;
+import ch.systemsx.cisd.common.utilities.ITerminable;
+import ch.systemsx.cisd.datamover.common.StoreItem;
+
+/**
+ * @author Tomasz Pylak on Oct 10, 2007
+ */
+public interface IStoreCopier extends ITerminable
+{
+    Status copy(StoreItem item);
+}
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IStoreHandler.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IStoreHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..1870ed04f029cf958da58fbccb94199f5a9a4874
--- /dev/null
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IStoreHandler.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2007 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.datamover.filesystem.intf;
+
+import ch.systemsx.cisd.datamover.common.StoreItem;
+
+
+/**
+ * @author Tomasz Pylak on Oct 9, 2007
+ */
+public interface IStoreHandler
+{
+    void handle(StoreItem item);
+}
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/remote/CopyActivityMonitor.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/remote/CopyActivityMonitor.java
index 1c465603619d91c9e5e0dd14a460754762c5b337..672a61b1220279ccc0a12f408f5a30aa6d7d16e1 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/remote/CopyActivityMonitor.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/remote/CopyActivityMonitor.java
@@ -16,7 +16,6 @@
 
 package ch.systemsx.cisd.datamover.filesystem.remote;
 
-import java.io.File;
 import java.util.Timer;
 import java.util.TimerTask;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -28,7 +27,8 @@ import org.apache.log4j.Logger;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.common.utilities.ITerminable;
-import ch.systemsx.cisd.datamover.filesystem.intf.IReadPathOperations;
+import ch.systemsx.cisd.datamover.common.StoreItem;
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore;
 import ch.systemsx.cisd.datamover.intf.ITimingParameters;
 
 /**
@@ -44,9 +44,7 @@ public class CopyActivityMonitor
 
     private static final Logger machineLog = LogFactory.getLogger(LogCategory.MACHINE, CopyActivityMonitor.class);
 
-    private final File destinationDirectory;
-
-    private final IReadPathOperations readOperations;
+    private final FileStore destinationDirectory;
 
     private final long checkIntervallMillis;
 
@@ -78,7 +76,7 @@ public class CopyActivityMonitor
     /**
      * A <code>null</code> reference means: no monitoring.
      */
-    private final AtomicReference<File> pathToBeCopied;
+    private final AtomicReference<StoreItem> pathToBeCopied;
 
     /**
      * The time in milliseconds since start of the epoch when the monitored path has last been changed.
@@ -94,25 +92,22 @@ public class CopyActivityMonitor
      * Creates a monitor.
      * 
      * @param destinationDirectory The directory to monitor for write access.
-     * @param readOperations Provides read-only access to the file system.
      * @param copyProcess The {@link ITerminable} representing the copy process. This will get terminated if the copy
      *            process gets stuck.
      * @param timingParameters The {@link ITimingParameters} to get the check interval and the inactivity period from.
      */
-    public CopyActivityMonitor(File destinationDirectory, IReadPathOperations readOperations, ITerminable copyProcess,
+    public CopyActivityMonitor(FileStore destinationDirectory, ITerminable copyProcess,
             ITimingParameters timingParameters)
     {
         this.monitoredPathLastChecked = new AtomicLong(0);
         this.monitoredPathLastChanged = new AtomicLong(0);
-        this.pathToBeCopied = new AtomicReference<File>(null);
+        this.pathToBeCopied = new AtomicReference<StoreItem>(null);
 
         assert destinationDirectory != null;
-        assert readOperations != null;
         assert copyProcess != null;
         assert timingParameters != null;
 
         this.destinationDirectory = destinationDirectory;
-        this.readOperations = readOperations;
         this.checkIntervallMillis = timingParameters.getCheckIntervalMillis();
 
         assert this.checkIntervallMillis > 0;
@@ -158,7 +153,7 @@ public class CopyActivityMonitor
      * @param newPathToBeCopied The path that will be copied to the destination directory and whose write progress
      *            should be monitored.
      */
-    public void start(File newPathToBeCopied)
+    public void start(StoreItem newPathToBeCopied)
     {
         assert newPathToBeCopied != null;
 
@@ -187,7 +182,6 @@ public class CopyActivityMonitor
 
         private ActivityMonitoringTimerTask()
         {
-            assert readOperations != null;
             assert pathToBeCopied != null;
             assert monitoredPathLastChanged != null;
             assert destinationDirectory != null;
@@ -196,8 +190,8 @@ public class CopyActivityMonitor
         @Override
         public void run()
         {
-            final File path = pathToBeCopied.get();
-            if (path == null)
+            final StoreItem item = pathToBeCopied.get();
+            if (item == null)
             {
                 return;
             }
@@ -209,24 +203,24 @@ public class CopyActivityMonitor
 
             try
             {
-                final File pathToCheck = new File(destinationDirectory, path.getName());
                 if (operationLog.isTraceEnabled())
                 {
-                    operationLog.trace(String.format("Asking checker %s for last change time of path '%s'.",
-                            readOperations.getClass().getName(), pathToCheck));
+                    operationLog.trace(String.format("Asking for last change time of '%s' inside '%s'.", item,
+                            destinationDirectory));
                 }
-                if (readOperations.exists(pathToCheck) == false)
+                if (destinationDirectory.exists(item) == false)
                 {
-                    operationLog.warn(String.format("File or directory '%s' does not (yet?) exist.", pathToCheck));
+                    operationLog.warn(String.format("File or directory '%s' inside '%s' does not (yet?) exist.", item,
+                            destinationDirectory));
                     monitoredPathLastChecked.set(System.currentTimeMillis());
                     return;
                 }
-                final long lastChangedAsFoundByPathChecker = readOperations.lastChanged(pathToCheck);
+                final long lastChangedAsFoundByPathChecker = destinationDirectory.lastChanged(item);
                 if (operationLog.isTraceEnabled())
                 {
                     operationLog.trace(String.format(
-                            "Checker %s reported last changed time of path '%s' to be %3$tF %3$tT.", readOperations
-                                    .getClass().getName(), pathToCheck.getPath(), lastChangedAsFoundByPathChecker));
+                            "Reported last changed time of '%s' inside '%s' to be %3$tF %3$tT.", item,
+                            destinationDirectory, lastChangedAsFoundByPathChecker));
                 }
                 if (terminated.get()) // Don't modify the time variables any more if we got terminated.
                 {
@@ -303,7 +297,7 @@ public class CopyActivityMonitor
         @Override
         public void run()
         {
-            final File path = pathToBeCopied.get();
+            final StoreItem path = pathToBeCopied.get();
             if (path == null)
             {
                 return;
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/remote/RemotePathMover.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/remote/RemotePathMover.java
index aaec1d8915ecea68c7e997c7d6a33d7345f09422..d62cde273ae2c2657985af9ecdf64bfd6549defc 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/remote/RemotePathMover.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/remote/RemotePathMover.java
@@ -16,20 +16,18 @@
 
 package ch.systemsx.cisd.datamover.filesystem.remote;
 
-import java.io.File;
-import java.io.IOException;
-
 import org.apache.log4j.Logger;
 
 import ch.systemsx.cisd.common.exceptions.Status;
 import ch.systemsx.cisd.common.exceptions.StatusFlag;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
-import ch.systemsx.cisd.common.utilities.FileUtilities;
-import ch.systemsx.cisd.common.utilities.IPathHandler;
 import ch.systemsx.cisd.datamover.common.MarkerFile;
-import ch.systemsx.cisd.datamover.filesystem.intf.IPathCopier;
-import ch.systemsx.cisd.datamover.filesystem.intf.IPathRemover;
+import ch.systemsx.cisd.datamover.common.StoreItem;
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore;
+import ch.systemsx.cisd.datamover.filesystem.intf.IStoreCopier;
+import ch.systemsx.cisd.datamover.filesystem.intf.IStoreHandler;
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore.ExtendedFileStore;
 import ch.systemsx.cisd.datamover.intf.ITimingParameters;
 
 /**
@@ -38,7 +36,7 @@ import ch.systemsx.cisd.datamover.intf.ITimingParameters;
  * 
  * @author Bernd Rinn
  */
-public final class RemotePathMover implements IPathHandler
+public final class RemotePathMover implements IStoreHandler
 {
 
     private static final String START_COPYING_PATH_TEMPLATE = "Start copying path '%s' to '%s'.";
@@ -56,10 +54,10 @@ public final class RemotePathMover implements IPathHandler
 
     private static final String REMOVING_LOCAL_PATH_FAILED_TEMPLATE = "Removing local path '%s' failed (%s).";
 
-    private static final String FAILED_TO_CREATE_MARK_FILE_TEMPLATE = "Failed to create mark file '%s'";
+    private static final String FAILED_TO_CREATE_FILE_TEMPLATE = "Failed to create file '%s' in '%s'";
 
-    private static final String FAILED_TO_COPY_MARK_FILE_TO_REMOTE_TEMPLATE =
-            "Failed to copy mark file '%s' to remote (%s)";
+    private static final String FAILED_TO_COPY_FILE_TO_REMOTE_TEMPLATE =
+            "Failed to copy file '%s' from '%s' to remote (%s)";
 
     private static final Logger machineLog = LogFactory.getLogger(LogCategory.MACHINE, RemotePathMover.class);
 
@@ -67,15 +65,11 @@ public final class RemotePathMover implements IPathHandler
 
     private static final Logger notificationLog = LogFactory.getLogger(LogCategory.NOTIFY, RemotePathMover.class);
 
-    private final File destinationDirectory;
-
-    private final String destinationHost;
-
-    private final IPathCopier copier;
+    private final FileStore sourceDirectory;
 
-    private final IPathRemover remover;
+    private final FileStore destinationDirectory;
 
-    private final String sourceHost;
+    private final IStoreCopier copier;
 
     private final CopyActivityMonitor monitor;
 
@@ -86,33 +80,27 @@ public final class RemotePathMover implements IPathHandler
     /**
      * Creates a <var>PathRemoteMover</var>.
      * 
+     * @param sourceDirectory The directory to move paths from.
      * @param destinationDirectory The directory to move paths to.
-     * @param destinationHost The host to move paths to, or <code>null</code>, if <var>destinationDirectory</var> is
-     *            a remote share.
+     * @param copier Copies items from source to destination
      * @param monitor The activity monitor to inform about actions.
-     * @param remover Allows to remove files.
-     * @param copier Allows to copy files
-     * @param sourceHost The host to move paths from, or <code>null</code>, if data will be moved from the local file
-     *            system
      * @param timingParameters The timing parametes used for monitoring and reporting stall situations.
      */
-    public RemotePathMover(File destinationDirectory, String destinationHost, CopyActivityMonitor monitor,
-            IPathRemover remover, IPathCopier copier, String sourceHost, ITimingParameters timingParameters)
+    public RemotePathMover(FileStore sourceDirectory, FileStore destinationDirectory, IStoreCopier copier,
+            CopyActivityMonitor monitor, ITimingParameters timingParameters)
     {
+        assert sourceDirectory != null;
         assert destinationDirectory != null;
         assert monitor != null;
-        assert remover != null;
-        assert copier != null;
         assert timingParameters != null;
-        assert FileUtilities.checkDirectoryFullyAccessible(destinationDirectory, "destination") == null : FileUtilities
-                .checkDirectoryFullyAccessible(destinationDirectory, "destination");
+        String errorMsg = destinationDirectory.tryCheckDirectoryFullyAccessible();
+        assert errorMsg == null : errorMsg;
+        assert sourceDirectory.tryAsExtended() != null || destinationDirectory.tryAsExtended() != null;
 
+        this.sourceDirectory = sourceDirectory;
         this.destinationDirectory = destinationDirectory;
-        this.destinationHost = destinationHost;
-        this.monitor = monitor;
         this.copier = copier;
-        this.remover = remover;
-        this.sourceHost = sourceHost;
+        this.monitor = monitor;
         this.intervallToWaitAfterFailure = timingParameters.getIntervalToWaitAfterFailure();
         this.maximalNumberOfRetries = timingParameters.getMaximalNumberOfRetries();
 
@@ -120,19 +108,18 @@ public final class RemotePathMover implements IPathHandler
         assert maximalNumberOfRetries >= 0;
     }
 
-    public void handle(File path)
+    public void handle(StoreItem item)
     {
-        if (isDeletionInProgress(path))
+        if (isDeletionInProgress(item))
         {
             // This is a recovery situation: we have been interrupted removing the path and now finish the job.
             if (operationLog.isInfoEnabled())
             {
                 operationLog.info(String.format(
-                        "Detected recovery situation: '%s' has been interrupted in deletion phase, finishing up.", path
-                                .getAbsolutePath()));
+                        "Detected recovery situation: '%s' has been interrupted in deletion phase, finishing up.",
+                        getSrcPath(item)));
             }
-            remove(path);
-            markAsFinished(path);
+            removeAndMark(item);
             return;
         }
         int tryCount = 0;
@@ -142,33 +129,30 @@ public final class RemotePathMover implements IPathHandler
             {
                 if (tryCount > 0) // This is a retry
                 {
-                    operationLog.info(String.format(START_COPYING_PATH_RETRY_TEMPLATE, path.getPath(),
-                            destinationDirectory.getPath(), tryCount));
+                    operationLog.info(String.format(START_COPYING_PATH_RETRY_TEMPLATE, getSrcPath(item),
+                            destinationDirectory, tryCount));
                 } else
                 {
-                    operationLog.info(String.format(START_COPYING_PATH_TEMPLATE, path.getPath(), destinationDirectory
-                            .getPath()));
+                    operationLog.info(String
+                            .format(START_COPYING_PATH_TEMPLATE, getSrcPath(item), destinationDirectory));
                 }
             }
             final long startTime = System.currentTimeMillis();
-            monitor.start(path);
-            final Status copyStatus = copier.copy(path, sourceHost, destinationDirectory, destinationHost);
-            monitor.stop();
+            final Status copyStatus = copyAndMonitor(item);
             if (StatusFlag.OK.equals(copyStatus.getFlag()))
             {
                 if (operationLog.isInfoEnabled())
                 {
                     final long endTime = System.currentTimeMillis();
-                    operationLog.info(String.format(FINISH_COPYING_PATH_TEMPLATE, path.getPath(), destinationDirectory
-                            .getPath(), (endTime - startTime) / 1000.0));
+                    operationLog.info(String.format(FINISH_COPYING_PATH_TEMPLATE, getSrcPath(item),
+                            destinationDirectory, (endTime - startTime) / 1000.0));
                 }
-                remove(path);
-                markAsFinished(path);
+                removeAndMark(item);
                 return;
             } else
             {
-                operationLog.warn(String.format(COPYING_PATH_TO_REMOTE_FAILED, path.getPath(), destinationDirectory
-                        .getPath(), copyStatus));
+                operationLog.warn(String.format(COPYING_PATH_TO_REMOTE_FAILED, getSrcPath(item), destinationDirectory,
+                        copyStatus));
                 if (StatusFlag.FATAL_ERROR.equals(copyStatus.getFlag()))
                 {
                     break;
@@ -189,122 +173,143 @@ public final class RemotePathMover implements IPathHandler
             }
         } while (true);
 
-        notificationLog.error(String.format(MOVING_PATH_TO_REMOTE_FAILED_TEMPLATE, path, destinationDirectory));
+        notificationLog.error(String.format(MOVING_PATH_TO_REMOTE_FAILED_TEMPLATE, getSrcPath(item),
+                destinationDirectory));
+    }
+
+    private Status copyAndMonitor(StoreItem item)
+    {
+        monitor.start(item);
+        final Status copyStatus = copier.copy(item);
+        monitor.stop();
+        return copyStatus;
+    }
+
+    private void removeAndMark(StoreItem item)
+    {
+        remove(item);
+        markAsFinished(item);
     }
 
-    private void remove(File path)
+    private void remove(StoreItem sourceItem)
     {
-        final File removalInProgressMarkerFile = tryMarkAsDeletionInProgress(path);
-        final Status removalStatus = remover.remove(path);
-        removeMarkerFile(removalInProgressMarkerFile);
+        final StoreItem removalInProgressMarkerFile = tryMarkAsDeletionInProgress(sourceItem);
+        final Status removalStatus = sourceDirectory.delete(sourceItem);
+        removeDeletionMarkerFile(removalInProgressMarkerFile);
+
         if (Status.OK.equals(removalStatus) == false)
         {
-            notificationLog.error(String.format(REMOVING_LOCAL_PATH_FAILED_TEMPLATE, path, removalStatus));
+            notificationLog.error(String.format(REMOVING_LOCAL_PATH_FAILED_TEMPLATE, getSrcPath(sourceItem),
+                    removalStatus));
         } else if (operationLog.isInfoEnabled())
         {
-            operationLog.info(String.format(REMOVED_PATH_TEMPLATE, path.getPath()));
+            operationLog.info(String.format(REMOVED_PATH_TEMPLATE, getSrcPath(sourceItem)));
         }
     }
 
-    private boolean isDeletionInProgress(File path)
+    private boolean isDeletionInProgress(StoreItem item)
     {
-        final File markDeletionInProgressMarkerFile = getDeletionInProgressMarkerFile(path);
-        return markDeletionInProgressMarkerFile.exists();
+        StoreItem markDeletionInProgressMarkerFile = MarkerFile.createDeletionInProgressMarker(item);
+        return getDeletionMarkerStore().exists(markDeletionInProgressMarkerFile);
     }
 
-    private File tryMarkAsDeletionInProgress(File path)
+    private StoreItem tryMarkAsDeletionInProgress(StoreItem item)
     {
-        final File markDeletionInProgressMarkerFile = getDeletionInProgressMarkerFile(path);
-        if (markLocal(markDeletionInProgressMarkerFile))
+        final StoreItem markDeletionInProgressMarkerFile = MarkerFile.createDeletionInProgressMarker(item);
+        if (createFileInside(getDeletionMarkerStore(), markDeletionInProgressMarkerFile))
         {
             return markDeletionInProgressMarkerFile;
         } else
         {
-            machineLog.error(String.format("Cannot create deletion-in-progress marker file for path '%s' [%s]", path
-                    .getAbsolutePath(), markDeletionInProgressMarkerFile.getAbsolutePath()));
+            machineLog.error(String.format("Cannot create deletion-in-progress marker file for path '%s' [%s]", item,
+                    markDeletionInProgressMarkerFile));
             return null;
         }
     }
 
-    private File getDeletionInProgressMarkerFile(File path)
+    private void removeDeletionMarkerFile(StoreItem markerOrNull)
     {
-        // When destinationHost == null, we put the marker directory in the destination directory, otherwise in
-        // the source directory
-        final File markerParentDirectory = (destinationHost == null) ? destinationDirectory : path.getParentFile();
-        final File markDeletionInProgressMarkerFile =
-                MarkerFile.createDeletionInProgressMarker(markerParentDirectory, path.getName());
-        return markDeletionInProgressMarkerFile;
-    }
-
-    private void removeMarkerFile(File markerFileOrNull)
-    {
-        if (markerFileOrNull != null)
+        if (markerOrNull != null)
         {
-            final boolean removalOK = markerFileOrNull.delete();
-            if (removalOK == false)
+            final Status status = getDeletionMarkerStore().delete(markerOrNull);
+            if (status.equals(Status.OK) == false)
             {
-                machineLog.error(String.format("Cannot remove marker file '%s'", markerFileOrNull.getAbsolutePath()));
+                machineLog.error(String.format("Cannot remove marker file '%s'", getPath(destinationDirectory,
+                        markerOrNull)));
             }
         }
     }
 
-    private boolean markAsFinished(File path)
+    private ExtendedFileStore getDeletionMarkerStore()
     {
-        final File markFinishedFile = MarkerFile.createCopyFinishedMarker(destinationDirectory, path.getName());
-        if (destinationHost == null)
-        {
-            return markLocal(markFinishedFile);
-        } else
+        ExtendedFileStore fileStore = destinationDirectory.tryAsExtended();
+        if (fileStore == null)
         {
-            return markOnSourceLocalAndCopyToRemoteDestination(markFinishedFile);
+            fileStore = sourceDirectory.tryAsExtended();
         }
+        assert fileStore != null;
+        return fileStore;
     }
 
-    private boolean markLocal(File markerFile)
+    // Creates a finish-marker inside destination directory.
+    private boolean markAsFinished(StoreItem item)
     {
-        try
+        StoreItem markerItem = MarkerFile.createCopyFinishedMarker(item);
+        ExtendedFileStore extendedFileStore = destinationDirectory.tryAsExtended();
+        if (extendedFileStore != null)
         {
-            markerFile.createNewFile();
-            final boolean success = markerFile.exists();
-            if (success == false)
-            {
-                machineLog.error(String.format(FAILED_TO_CREATE_MARK_FILE_TEMPLATE, markerFile.getAbsoluteFile()));
-            }
-            return success;
-        } catch (IOException e)
+            // We create the marker directly inside the destination directory
+            return createFileInside(extendedFileStore, markerItem);
+        } else
         {
-            machineLog.error(String.format(FAILED_TO_CREATE_MARK_FILE_TEMPLATE, markerFile.getAbsoluteFile()), e);
-            return false;
+            // When destination is remote, we put the item directory in the source directory and copy it to destination.
+            extendedFileStore = sourceDirectory.tryAsExtended();
+            assert extendedFileStore != null;
+            return markOnSourceLocalAndCopyToRemoteDestination(extendedFileStore, markerItem);
         }
     }
 
-    private boolean markOnSourceLocalAndCopyToRemoteDestination(File markerFile)
+    private boolean markOnSourceLocalAndCopyToRemoteDestination(ExtendedFileStore sourceFileStore, StoreItem markerFile)
     {
-        final File localMarkerFile = new File(markerFile.getParent(), markerFile.getName());
         try
         {
-            localMarkerFile.createNewFile();
-            monitor.start(localMarkerFile);
-            final Status copyStatus = copier.copy(localMarkerFile, sourceHost, destinationDirectory, destinationHost);
-            monitor.stop();
+            if (createFileInside(sourceFileStore, markerFile) == false)
+            {
+                return false;
+            }
+            final Status copyStatus = copyAndMonitor(markerFile);
             if (StatusFlag.OK.equals(copyStatus.getFlag()))
             {
                 return true;
             } else
             {
-                machineLog.error(String.format(FAILED_TO_COPY_MARK_FILE_TO_REMOTE_TEMPLATE, localMarkerFile
-                        .getAbsoluteFile(), copyStatus.toString()));
+                machineLog.error(String.format(FAILED_TO_COPY_FILE_TO_REMOTE_TEMPLATE, markerFile, sourceFileStore,
+                        copyStatus.toString()));
                 return false;
             }
-
-        } catch (IOException e)
-        {
-            machineLog.error(String.format(FAILED_TO_CREATE_MARK_FILE_TEMPLATE, localMarkerFile.getAbsoluteFile()), e);
-            return false;
         } finally
         {
-            localMarkerFile.delete();
+            sourceFileStore.delete(markerFile);
+        }
+    }
+
+    private static boolean createFileInside(ExtendedFileStore directory, StoreItem item)
+    {
+        boolean success = directory.createNewFile(item);
+        if (success == false)
+        {
+            machineLog.error(String.format(FAILED_TO_CREATE_FILE_TEMPLATE, item, directory));
         }
+        return success;
     }
 
+    private String getSrcPath(StoreItem item)
+    {
+        return getPath(sourceDirectory, item);
+    }
+
+    private static String getPath(FileStore directory, StoreItem item)
+    {
+        return item + " inside " + directory;
+    }
 }
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/remote/rsync/RsyncCopier.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/remote/rsync/RsyncCopier.java
index d736155cbdd79e8e1232eefd85c341048e20a9ac..7a658f30588f2777fb473950db00f704c8107200 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/remote/rsync/RsyncCopier.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/remote/rsync/RsyncCopier.java
@@ -32,9 +32,8 @@ import ch.systemsx.cisd.common.exceptions.Status;
 import ch.systemsx.cisd.common.exceptions.StatusFlag;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
-import ch.systemsx.cisd.common.utilities.ProcessExecutionHelper;
-import ch.systemsx.cisd.common.utilities.FileUtilities;
 import ch.systemsx.cisd.common.utilities.OSUtilities;
+import ch.systemsx.cisd.common.utilities.ProcessExecutionHelper;
 import ch.systemsx.cisd.datamover.filesystem.intf.IPathCopier;
 import ch.systemsx.cisd.datamover.filesystem.remote.rsync.RsyncVersionChecker.RsyncVersion;
 
@@ -86,21 +85,21 @@ public class RsyncCopier implements IPathCopier
      * Constructs an <code>RsyncCopier</code>.
      * 
      * @param rsyncExecutable The <code>rsync</code> binary to call for copying.
-     * @param sshExecutable The <code>ssh</code> binary to use for creating tunnels, or <code>null</code>, if no
-     *            <code>ssh</code> is available on this machine.
+     * @param sshExecutableOrNull The <code>ssh</code> binary to use for creating tunnels, or <code>null</code>, if
+     *            no <code>ssh</code> is available on this machine.
      * @param destinationDirectoryRequiresDeletionBeforeCreation If <code>true</code>, already existing files and
      *            directories on the remote side will be deleted before starting the copy process (no overwriting of
      *            paths).
      */
-    public RsyncCopier(File rsyncExecutable, File sshExecutable,
+    public RsyncCopier(File rsyncExecutable, File sshExecutableOrNull, 
             boolean destinationDirectoryRequiresDeletionBeforeCreation, boolean overwrite, String... cmdLineFlags)
     {
         assert rsyncExecutable != null && rsyncExecutable.exists();
-        assert sshExecutable == null || rsyncExecutable.exists();
+        assert sshExecutableOrNull == null || rsyncExecutable.exists();
 
         this.rsyncExecutable = rsyncExecutable.getAbsolutePath();
         this.rsyncVersion = RsyncVersionChecker.getVersion(rsyncExecutable.getAbsolutePath());
-        this.sshExecutable = (sshExecutable != null) ? sshExecutable.getPath() : null;
+        this.sshExecutable = (sshExecutableOrNull != null) ? sshExecutableOrNull.getPath() : null;
         this.destinationDirectoryRequiresDeletionBeforeCreation = destinationDirectoryRequiresDeletionBeforeCreation;
         this.copyProcessReference = new AtomicReference<Process>(null);
         this.overwrite = overwrite;
@@ -130,30 +129,30 @@ public class RsyncCopier implements IPathCopier
         return copy(sourcePath, null, destinationDirectory, null);
     }
 
-    public Status copy(File sourcePath, String sourceHost, File destinationDirectory, String destinationHost)
+    public Status copyFromRemote(File sourcePath, String sourceHost, File destinationDirectory)
+    {
+        return copy(sourcePath, sourceHost, destinationDirectory, null);
+    }
+
+    public Status copyToRemote(File sourcePath, File destinationDirectory, String destinationHost)
+    {
+        return copy(sourcePath, null, destinationDirectory, destinationHost);
+    }
+
+    private Status copy(File sourcePath, String sourceHostOrNull, File destinationDirectory,
+            String destinationHostOrNull)
     {
         assert sourcePath != null;
-        assert sourceHost != null || sourcePath.exists() : logNonExistent(sourcePath);
+        assert sourceHostOrNull != null || sourcePath.exists() : logNonExistent(sourcePath);
         assert destinationDirectory != null;
-        assert destinationHost != null || destinationDirectory.isDirectory() : logNonExistent(sourcePath);
-        assert sourceHost == null || destinationHost == null; // only one side can be remote
+        assert destinationHostOrNull != null || destinationDirectory.isDirectory() : logNonExistent(sourcePath);
+        assert sourceHostOrNull == null || destinationHostOrNull == null; // only one side can be remote
 
-        final File destinationPath = new File(destinationDirectory, sourcePath.getName());
-        if (destinationDirectoryRequiresDeletionBeforeCreation && destinationPath.exists())
-        {
-            if (operationLog.isDebugEnabled())
-            {
-                operationLog.debug(String.format(
-                        "Remove path '%s' since it exists and the remote file system doesn't support overwriting.",
-                        destinationPath));
-            }
-            // TODO 2007-08-13 Tomasz Pylak: Bernd, what if destinationHost is set?
-            FileUtilities.deleteRecursively(destinationPath);
-        }
         try
         {
             final ProcessBuilder copyProcessBuilder =
-                    new ProcessBuilder(createCommandLine(sourcePath, sourceHost, destinationDirectory, destinationHost));
+                    new ProcessBuilder(createCommandLine(sourcePath, sourceHostOrNull, destinationDirectory,
+                            destinationHostOrNull));
             copyProcessBuilder.redirectErrorStream(true);
             if (operationLog.isDebugEnabled())
             {
@@ -344,7 +343,7 @@ public class RsyncCopier implements IPathCopier
         }
     }
 
-    public boolean exists(File destinationDirectory, String destinationHost)
+    public boolean existsRemotely(File destinationDirectory, String destinationHost)
     {
         assert destinationDirectory != null && destinationDirectory.isDirectory();
         assert destinationHost != null;
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/store/FileStoreLocal.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/store/FileStoreLocal.java
new file mode 100644
index 0000000000000000000000000000000000000000..284d9ed053888ddea9951ee6e4535df3c24fbb21
--- /dev/null
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/store/FileStoreLocal.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2007 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.datamover.filesystem.store;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.log4j.Logger;
+
+import ch.systemsx.cisd.common.exceptions.Status;
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogFactory;
+import ch.systemsx.cisd.common.utilities.FileUtilities;
+import ch.systemsx.cisd.datamover.common.MarkerFile;
+import ch.systemsx.cisd.datamover.common.StoreItem;
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore;
+import ch.systemsx.cisd.datamover.filesystem.intf.IFileSysOperationsFactory;
+import ch.systemsx.cisd.datamover.filesystem.intf.IPathMover;
+import ch.systemsx.cisd.datamover.filesystem.intf.IPathRemover;
+import ch.systemsx.cisd.datamover.filesystem.intf.IStoreCopier;
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore.ExtendedFileStore;
+
+/**
+ * @author Tomasz Pylak on Oct 9, 2007
+ */
+public class FileStoreLocal extends ExtendedFileStore
+{
+    private static final Logger machineLog = LogFactory.getLogger(LogCategory.MACHINE, FileStoreLocal.class);
+
+    private final IPathMover mover;
+
+    private final IPathRemover remover;
+
+    public FileStoreLocal(File file, String desription, IFileSysOperationsFactory factory)
+    {
+        super(file, null, false, desription, factory);
+        this.remover = factory.getRemover();
+        this.mover = factory.getMover();
+    }
+
+    @Override
+    public Status delete(StoreItem item)
+    {
+        return remover.remove(getChildFile(item));
+    }
+
+    @Override
+    public boolean exists(StoreItem item)
+    {
+        return getChildFile(item).exists();
+    }
+
+    @Override
+    public long lastChanged(StoreItem item)
+    {
+        return FileUtilities.lastChanged(getChildFile(item));
+    }
+
+    @Override
+    public String tryCheckDirectoryFullyAccessible()
+    {
+        return FileUtilities.checkDirectoryFullyAccessible(super.getPath(), super.getDescription());
+    }
+
+    @Override
+    public IStoreCopier getCopier(FileStore destinationDirectory)
+    {
+        boolean requiresDeletion = false;
+        final IStoreCopier simpleCopier = constructStoreCopier(destinationDirectory, requiresDeletion);
+        if (requiresDeletionBeforeCreation(destinationDirectory, simpleCopier))
+        {
+            requiresDeletion = true;
+            return constructStoreCopier(destinationDirectory, requiresDeletion);
+        } else
+        {
+            return simpleCopier;
+        }
+    }
+
+    @Override
+    public ExtendedFileStore tryAsExtended()
+    {
+        return this;
+    }
+
+    @Override
+    public boolean createNewFile(StoreItem item)
+    {
+        try
+        {
+            File itemFile = getChildFile(item);
+            itemFile.createNewFile();
+            return itemFile.exists(); // success also when file existed before
+        } catch (IOException ex)
+        {
+            ex.printStackTrace();
+            return false;
+        }
+    }
+
+    @Override
+    public File tryMoveLocal(StoreItem sourceItem, File destinationDir, String newFilePrefix)
+    {
+        return mover.tryMove(getChildFile(sourceItem), destinationDir, newFilePrefix);
+    }
+
+    @Override
+    public String toString()
+    {
+        String pathStr = super.path.getPath();
+        return "[local fs]" + pathStr;
+    }
+
+    // ------
+
+    /**
+     * @return <code>true</code> if the <var>simpleCopier</var> on the file system where the
+     *         <var>destinationDirectory</var> resides requires deleting an existing file before it can be overwritten.
+     */
+    protected boolean requiresDeletionBeforeCreation(FileStore destinationDirectory, final IStoreCopier simpleCopier)
+    {
+        StoreItem item = MarkerFile.createRequiresDeletionBeforeCreationMarker();
+        createNewFile(item);
+        simpleCopier.copy(item);
+        boolean requiresDeletion;
+        try
+        {
+            // If we have e.g. a Cellera NAS server, the next call will raise an IOException.
+            requiresDeletion = Status.OK.equals(simpleCopier.copy(item)) == false;
+            logCopierOverwriteState(destinationDirectory, requiresDeletion);
+        } catch (Exception e)
+        {
+            logFIleSystemNeedsOverwrite(destinationDirectory);
+            requiresDeletion = true;
+        } finally
+        {
+            // We don't check for success because there is nothing we can do if we fail.
+            delete(item);
+            destinationDirectory.delete(item);
+        }
+        return requiresDeletion;
+    }
+
+    private static void logFIleSystemNeedsOverwrite(FileStore destinationDirectory)
+    {
+        if (machineLog.isInfoEnabled())
+        {
+            machineLog.info(String.format(
+                    "The file system on '%s' requires deletion before creation of existing files.",
+                    destinationDirectory));
+        }
+    }
+
+    private static void logCopierOverwriteState(FileStore destinationDirectory, boolean requiresDeletion)
+    {
+        if (machineLog.isInfoEnabled())
+        {
+            if (requiresDeletion)
+            {
+                machineLog.info(String.format(
+                        "Copier on directory '%s' requires deletion before creation of existing files.",
+                        destinationDirectory));
+            } else
+            {
+                machineLog.info(String.format("Copier on directory '%s' works with overwriting existing files.",
+                        destinationDirectory));
+            }
+        }
+    }
+}
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/store/FileStoreRemote.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/store/FileStoreRemote.java
new file mode 100644
index 0000000000000000000000000000000000000000..14399bd3fdb8032f2c26dd5066555e10280f8029
--- /dev/null
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/store/FileStoreRemote.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2007 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.datamover.filesystem.store;
+
+import java.io.File;
+
+import ch.systemsx.cisd.common.exceptions.Status;
+import ch.systemsx.cisd.datamover.common.StoreItem;
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore;
+import ch.systemsx.cisd.datamover.filesystem.intf.IFileSysOperationsFactory;
+import ch.systemsx.cisd.datamover.filesystem.intf.IStoreCopier;
+
+/**
+ * @author Tomasz Pylak on Oct 9, 2007
+ */
+public class FileStoreRemote extends FileStore
+{
+    public FileStoreRemote(File path, String host, String kind, IFileSysOperationsFactory factory)
+    {
+        super(path, host, true, kind, factory);
+    }
+
+    @Override
+    public ExtendedFileStore tryAsExtended()
+    {
+        return null;
+    }
+
+    @Override
+    public Status delete(StoreItem item)
+    {
+        // TODO 2007-10-09 implement ssh tunneling mode
+        return Status.OK;
+    }
+
+    @Override
+    public boolean exists(StoreItem item)
+    {
+        return factory.getCopier(false).existsRemotely(path, hostOrNull);
+    }
+
+    @Override
+    public IStoreCopier getCopier(FileStore destinationDirectory)
+    {
+        boolean requiresDeletion = false;
+        return constructStoreCopier(destinationDirectory, requiresDeletion);
+    }
+
+    @Override
+    public long lastChanged(StoreItem item)
+    {
+        // TODO 2007-10-09 implement ssh tunneling mode
+        return 0;
+    }
+
+    @Override
+    public String tryCheckDirectoryFullyAccessible()
+    {
+        // TODO 2007-10-09 implement ssh tunneling mode. E.g. check if directory exists
+        return null;
+    }
+
+    @Override
+    public String toString()
+    {
+        String pathStr = path.getPath();
+        return "[remote fs]" + hostOrNull + ":" + pathStr;
+    }
+}
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/store/FileStoreRemoteMounted.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/store/FileStoreRemoteMounted.java
new file mode 100644
index 0000000000000000000000000000000000000000..e00c5332421339c29f79e1981bf329c1d7e9a3eb
--- /dev/null
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/store/FileStoreRemoteMounted.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2007 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.datamover.filesystem.store;
+
+import java.io.File;
+
+import ch.systemsx.cisd.common.exceptions.Status;
+import ch.systemsx.cisd.datamover.common.StoreItem;
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore;
+import ch.systemsx.cisd.datamover.filesystem.intf.IFileSysOperationsFactory;
+import ch.systemsx.cisd.datamover.filesystem.intf.IStoreCopier;
+
+/**
+ * @author Tomasz Pylak on Oct 9, 2007
+ */
+public class FileStoreRemoteMounted extends FileStore
+{
+    private final FileStoreLocal localImpl;
+
+    public FileStoreRemoteMounted(File file, String desription, IFileSysOperationsFactory factory)
+    {
+        super(file, null, true, desription, factory);
+        this.localImpl = new FileStoreLocal(file, desription, factory);
+    }
+
+    @Override
+    public ExtendedFileStore tryAsExtended()
+    {
+        return null;
+    }
+
+    @Override
+    public IStoreCopier getCopier(FileStore destinationDirectory)
+    {
+        boolean requiresDeletion = false;
+        return constructStoreCopier(destinationDirectory, requiresDeletion);
+    }
+
+    @Override
+    public String toString()
+    {
+        String pathStr = path.getPath();
+        return "[mounted remote fs]" + pathStr;
+    }
+
+    @Override
+    public Status delete(StoreItem item)
+    {
+        return localImpl.delete(item);
+    }
+
+    @Override
+    public boolean exists(StoreItem item)
+    {
+        return localImpl.exists(item);
+    }
+
+    @Override
+    public long lastChanged(StoreItem item)
+    {
+        return localImpl.lastChanged(item);
+    }
+
+    @Override
+    public String tryCheckDirectoryFullyAccessible()
+    {
+        return localImpl.tryCheckDirectoryFullyAccessible();
+    }
+}
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/utils/FileStore.java b/datamover/source/java/ch/systemsx/cisd/datamover/utils/FileStore.java
deleted file mode 100644
index afb60e57afd198430216f5066b489bdf288b65a9..0000000000000000000000000000000000000000
--- a/datamover/source/java/ch/systemsx/cisd/datamover/utils/FileStore.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright 2007 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.datamover.utils;
-
-import java.io.File;
-import java.io.IOException;
-
-import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
-
-/**
- * A class to holds the information about a file store.
- * 
- * @author Bernd Rinn
- */
-public class FileStore
-{
-    private final File path;
-
-    private final String canonicalPath;
-
-    private final String kind;
-
-    private final String host;
-
-    private final boolean remote;
-
-    public FileStore(File path, String kind, String host, boolean remote)
-    {
-        this.path = path;
-        this.kind = kind;
-        if (path != null)
-        {
-            this.canonicalPath = getCanonicalPath(path, host);
-        } else
-        {
-            this.canonicalPath = null;
-        }
-        this.host = host;
-        this.remote = remote;
-    }
-
-    private static String getCanonicalPath(File path, String host)
-    {
-        if (host != null)
-        {
-            return path.getPath();
-        }
-        try
-        {
-            return path.getCanonicalPath() + File.separator;
-        } catch (IOException e)
-        {
-            throw EnvironmentFailureException.fromTemplate(e, "Cannot determine canonical form of path '%s'", path
-                    .getPath());
-        }
-    }
-
-    /**
-     * Returns the remote host, if copying is done via an ssh tunnel, or <code>null</code> otherwise.
-     */
-    public String getHost()
-    {
-        return host;
-    }
-
-    /**
-     * Returns the path of the file store. If {@link #getHost()} is <code>null</code>, and only then, it may be
-     * interpreted to be on the local host.
-     */
-    public File getPath()
-    {
-        return path;
-    }
-
-    /**
-     * Returns the canonical form of the path as a string, if {@link #getHost()} is <code>null</code>. Note that the
-     * canonical name is only available if {@link #getHost()} is <code>null</code>, otherwise the normal path (as
-     * returned by {@link File#getPath()}) will be returned by this method.
-     */
-    public String getCanonicalPath()
-    {
-        return canonicalPath;
-    }
-
-    /**
-     * Returns the kind of file store (used for error and log messages).
-     */
-    public String getKind()
-    {
-        return kind;
-    }
-
-    /**
-     * Returns <code>true</code>, if the file store resides on a remote computer and <code>false</code> otherwise.
-     * <p>
-     * Note that this method can return <code>true</code> despite {@link #getHost()} returning <code>null</code>.
-     * In this case the file store is on a remote share mounted on local host via NFS or CIFS.
-     */
-    public boolean isRemote()
-    {
-        return remote;
-    }
-
-}
\ No newline at end of file
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/utils/QuietPeriodFileFilter.java b/datamover/source/java/ch/systemsx/cisd/datamover/utils/QuietPeriodFileFilter.java
index 4ef46b413f7b2206c9fd0469e03848f2a2a8699c..65f37023b6068fc7753cd09e90b545c55656a7dd 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/utils/QuietPeriodFileFilter.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/utils/QuietPeriodFileFilter.java
@@ -19,7 +19,7 @@ package ch.systemsx.cisd.datamover.utils;
 import java.io.File;
 import java.io.FileFilter;
 
-import ch.systemsx.cisd.datamover.filesystem.intf.IReadPathOperations;
+import ch.systemsx.cisd.common.utilities.FileUtilities;
 import ch.systemsx.cisd.datamover.intf.ITimingParameters;
 
 /**
@@ -29,31 +29,23 @@ import ch.systemsx.cisd.datamover.intf.ITimingParameters;
  */
 public class QuietPeriodFileFilter implements FileFilter
 {
-
     private final long quietPeriodMillis;
 
-    private final IReadPathOperations readOperations;
-
     /**
      * Creates a <var>QuietPeriodFileFilter</var>.
      * 
      * @param timingParameters The timing paramter object to get the quiet period from.
-     * @param readOperations Used to check when a pathname was changed.
      */
-    public QuietPeriodFileFilter(ITimingParameters timingParameters, IReadPathOperations readOperations)
+    public QuietPeriodFileFilter(ITimingParameters timingParameters)
     {
         assert timingParameters != null;
-        assert readOperations != null;
-
         this.quietPeriodMillis = timingParameters.getQuietPeriodMillis();
-        this.readOperations = readOperations;
-
         assert quietPeriodMillis > 0;
     }
 
     public boolean accept(File pathname)
     {
-        return (System.currentTimeMillis() - readOperations.lastChanged(pathname)) > quietPeriodMillis;
+        return (System.currentTimeMillis() - FileUtilities.lastChanged(pathname)) > quietPeriodMillis;
     }
 
 }
diff --git a/datamover/sourceTest/java/ch/systemsx/cisd/datamover/MainTest.java b/datamover/sourceTest/java/ch/systemsx/cisd/datamover/MainTest.java
index bb1d350a8fa5e65429a9cdbd38ee7a3616a29776..29d6161c9b601df58e6210f9c9c7905189a4d18f 100644
--- a/datamover/sourceTest/java/ch/systemsx/cisd/datamover/MainTest.java
+++ b/datamover/sourceTest/java/ch/systemsx/cisd/datamover/MainTest.java
@@ -18,8 +18,9 @@ package ch.systemsx.cisd.datamover;
 
 import static ch.systemsx.cisd.datamover.testhelper.FileSystemHelper.assertEmptyDir;
 import static ch.systemsx.cisd.datamover.testhelper.FileSystemHelper.createDir;
-
-import static org.testng.AssertJUnit.*;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertTrue;
 
 import java.io.File;
 import java.io.IOException;
@@ -49,7 +50,7 @@ public class MainTest
     // time needed be a single test to complete. After this time we kill data mover and check if results are correct. It
     // can happen, that tests running on slow machines fail because they need more time. Than this constant should be
     // adjusted.
-    private static final long DATA_MOVER_COMPLETION_TIME = 5000;
+    private static final long DATA_MOVER_COMPLETION_TIME = 6000;
 
     private static final long DATA_MOVER_COMPLETION_TIME_LONG = 7000;
 
@@ -207,8 +208,8 @@ public class MainTest
     {
         return createList("--incoming-dir", dirs.incoming.getPath(), "--buffer-dir", dirs.buffer.getPath(),
                 "--outgoing-dir", dirs.outgoing.getPath(), "--check-interval", Integer.toString(CHECK_INTERVAL),
-                "--check-interval-internal", Integer.toString(CHECK_INTERVAL_INTERNAL),
-                "--quiet-period", Integer.toString(QUIET_PERIOD), "--treat-incoming-as-remote");
+                "--check-interval-internal", Integer.toString(CHECK_INTERVAL_INTERNAL), "--quiet-period", Integer
+                        .toString(QUIET_PERIOD), "--treat-incoming-as-remote");
     }
 
     private static ArrayList<String> getManualInterventionParameters(ExternalDirs dirs, String filteredName)
@@ -268,8 +269,8 @@ public class MainTest
 
     private static LocalBufferDirs createBufferDirs(Parameters parameters)
     {
-        return new LocalBufferDirs(parameters.getBufferStore().getPath(), "test-copy-in-progress",
-                "test-copy-complete", "test-ready-to-move", "test-temp");
+        return new LocalBufferDirs(parameters.getBufferDirectoryPath(), "test-copy-in-progress", "test-copy-complete",
+                "test-ready-to-move", "test-temp");
     }
 
     private static interface IFSPreparator
@@ -409,7 +410,7 @@ public class MainTest
     }
 
     @Test(groups =
-        { "slow" }, dataProvider = "delays")
+        { "slowXX" }, dataProvider = "delays")
     // recovery after failure when data are copied to 'copy-completed', but before deletion has been finished
     public void testRecoveryIncomingCompleteDeletionInProgress(long delay) throws Exception
     {
@@ -631,7 +632,7 @@ public class MainTest
     }
 
     @Test(groups =
-        { "slow" })
+        { "xxx" })
     // normal work-flow tests, no extra copy is created
     public void testWholePipelineNoExtraCopy() throws Exception
     {
diff --git a/datamover/sourceTest/java/ch/systemsx/cisd/datamover/ParametersTest.java b/datamover/sourceTest/java/ch/systemsx/cisd/datamover/ParametersTest.java
index 04e5deda98f8f6b9d93072dda31f84174cced3a1..a865e5e82eb8eb10565667a1145fe92a8de3ece7 100644
--- a/datamover/sourceTest/java/ch/systemsx/cisd/datamover/ParametersTest.java
+++ b/datamover/sourceTest/java/ch/systemsx/cisd/datamover/ParametersTest.java
@@ -27,6 +27,11 @@ import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import ch.systemsx.cisd.common.utilities.SystemExit;
+import ch.systemsx.cisd.datamover.filesystem.FileStoreFactory;
+import ch.systemsx.cisd.datamover.filesystem.FileSysOperationsFactory;
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore;
+import ch.systemsx.cisd.datamover.filesystem.intf.IFileSysOperationsFactory;
+import ch.systemsx.cisd.datamover.intf.IFileSysParameters;
 
 /**
  * Test cases for the {@link Parameters} class.
@@ -83,7 +88,7 @@ public class ParametersTest
     {
         final String CLEANSING_REGEX = "[0-9]+";
         final Parameters parameters = parse("--cleansing-regex", CLEANSING_REGEX);
-        assertEquals(CLEANSING_REGEX, parameters.getCleansingRegex().pattern());
+        assertEquals(CLEANSING_REGEX, parameters.tryGetCleansingRegex().pattern());
     }
 
     @Test(expectedExceptions = RuntimeException.class)
@@ -98,7 +103,7 @@ public class ParametersTest
     {
         final String LOCAL_DATADIR = ".." + File.separator + "test_it_data";
         final Parameters parameters = parse("--incoming-dir", LOCAL_DATADIR);
-        assertEquals(LOCAL_DATADIR, parameters.getIncomingStore().getPath().getPath());
+        assertEquals(createIncomingStore(LOCAL_DATADIR, null, parameters), getIncomingStore(parameters));
     }
 
     @Test
@@ -106,23 +111,7 @@ public class ParametersTest
     {
         final String LOCAL_TEMPDIR = "test_it_tmp";
         final Parameters parameters = parse("--buffer-dir", LOCAL_TEMPDIR);
-        assertEquals(LOCAL_TEMPDIR, parameters.getBufferStore().getPath().getPath());
-    }
-
-    @Test
-    public void testSetRemoteDirLong() throws Exception
-    {
-        final String REMOTE_DATADIR = "test_it_remote";
-        final Parameters parameters = parse("--outgoing-dir", REMOTE_DATADIR);
-        assertEquals(REMOTE_DATADIR, parameters.getOutgoingStore().getPath().getPath());
-    }
-
-    @Test
-    public void testSetRemoteHostLong() throws Exception
-    {
-        final String REMOTE_HOST = "test_it_remote";
-        final Parameters parameters = parse("--outgoing-host", REMOTE_HOST);
-        assertEquals(REMOTE_HOST, parameters.getOutgoingStore().getHost());
+        assertEquals(LOCAL_TEMPDIR, parameters.getBufferDirectoryPath().getPath());
     }
 
     @Test
@@ -227,14 +216,14 @@ public class ParametersTest
         final Parameters parameters = parse();
         assertFalse(parameters.isRsyncOverwrite());
     }
-    
+
     @Test
     public void testSetRsyncOverwrite()
     {
         final Parameters parameters = parse("--rsync-overwrite");
         assertTrue(parameters.isRsyncOverwrite());
     }
-    
+
     @Test
     public void testSetMandatoryOptions() throws Exception
     {
@@ -243,9 +232,9 @@ public class ParametersTest
         final String REMOTE_DATADIR = "rrr";
         final Parameters parameters =
                 parse("--incoming-dir", LOCAL_DATADIR, "--buffer-dir", LOCAL_TEMPDIR, "--outgoing-dir", REMOTE_DATADIR);
-        assertEquals(LOCAL_DATADIR, parameters.getIncomingStore().getPath().getPath());
-        assertEquals(LOCAL_TEMPDIR, parameters.getBufferStore().getPath().getPath());
-        assertEquals(REMOTE_DATADIR, parameters.getOutgoingStore().getPath().getPath());
+        assertEquals(createIncomingStore(LOCAL_DATADIR, null, parameters), getIncomingStore(parameters));
+        assertEquals(LOCAL_TEMPDIR, parameters.getBufferDirectoryPath().getPath());
+        assertEquals(createOutgoingStore(REMOTE_DATADIR, null, parameters), getOutgoingStore(parameters));
     }
 
     @Test
@@ -264,17 +253,58 @@ public class ParametersTest
                 parse("--incoming-dir", LOCAL_DATADIR, "--buffer-dir", LOCAL_TEMPDIR, "--outgoing-dir", REMOTE_DATADIR,
                         "--outgoing-host", REMOTE_HOST, "--check-interval", Integer.toString(CHECK_INTERVAL),
                         "--quiet-period", Integer.toString(QUIET_PERIOD), "--treat-incoming-as-remote",
-                        "--incoming-host", REMOTE_INCOMING_HOST, "--extra-copy-dir", EXTRA_COPY_DIR, "--rsync-overwrite");
-        assertEquals(LOCAL_DATADIR, parameters.getIncomingStore().getPath().getPath());
-        assertEquals(REMOTE_INCOMING_HOST, parameters.getIncomingStore().getHost());
-        assertEquals(LOCAL_TEMPDIR, parameters.getBufferStore().getPath().getPath());
-        assertEquals(REMOTE_DATADIR, parameters.getOutgoingStore().getPath().getPath());
-        assertEquals(REMOTE_HOST, parameters.getOutgoingStore().getHost());
+                        "--incoming-host", REMOTE_INCOMING_HOST, "--extra-copy-dir", EXTRA_COPY_DIR,
+                        "--rsync-overwrite");
+        FileStore incomingStoreExpected = createIncomingStore(LOCAL_DATADIR, REMOTE_INCOMING_HOST, parameters);
+        FileStore incomingStore = getIncomingStore(parameters);
+        FileStore outgoingStoreExpected = createOutgoingStore(REMOTE_DATADIR, REMOTE_HOST, parameters);
+        FileStore outgoingStore = getOutgoingStore(parameters);
+
+        assertEquals(incomingStoreExpected, incomingStore);
+        assertEquals(LOCAL_TEMPDIR, parameters.getBufferDirectoryPath().getPath());
+        assertEquals(outgoingStoreExpected, outgoingStore);
         assertEquals(EXTRA_COPY_DIR, parameters.tryGetExtraCopyDir().getPath());
         assertEquals(1000 * CHECK_INTERVAL, parameters.getCheckIntervalMillis());
         assertEquals(1000 * QUIET_PERIOD, parameters.getQuietPeriodMillis());
-        assertEquals(true, parameters.getTreatIncomingAsRemote());
+        assertTrue(incomingStore.isRemote());
         assertTrue(parameters.isRsyncOverwrite());
     }
 
+    private FileStore getIncomingStore(Parameters parameters)
+    {
+        IFileSysOperationsFactory factory = new FileSysOperationsFactory(parameters);
+        return parameters.getIncomingStore(factory);
+    }
+
+    private FileStore getOutgoingStore(Parameters parameters)
+    {
+        IFileSysOperationsFactory factory = new FileSysOperationsFactory(parameters);
+        return parameters.getOutgoingStore(factory);
+    }
+
+    private static FileStore createIncomingStore(final String path, final String hostOrNull,
+            IFileSysParameters parameters)
+    {
+        return createStore(path, hostOrNull, Parameters.INCOMING_KIND_DESC, parameters);
+    }
+
+    private static FileStore createOutgoingStore(final String path, final String hostOrNull,
+            IFileSysParameters parameters)
+    {
+        return createStore(path, hostOrNull, Parameters.OUTGOING_KIND_DESC, parameters);
+    }
+
+    private static FileStore createStore(final String path, final String hostOrNull, String kind,
+            IFileSysParameters parameters)
+    {
+        IFileSysOperationsFactory factory = new FileSysOperationsFactory(parameters);
+        File file = new File(path);
+        if (hostOrNull == null)
+        {
+            return FileStoreFactory.createLocal(file, kind, factory);
+        } else
+        {
+            return FileStoreFactory.createRemoteHost(file, hostOrNull, kind, factory);
+        }
+    }
 }
diff --git a/datamover/sourceTest/java/ch/systemsx/cisd/datamover/SelfTestTest.java b/datamover/sourceTest/java/ch/systemsx/cisd/datamover/SelfTestTest.java
index 3b313e4a4535abd4fbaf38d0dc6403ada87bc3b6..ac3abd78fcaa66f42338528baaf0a30441f21d8d 100644
--- a/datamover/sourceTest/java/ch/systemsx/cisd/datamover/SelfTestTest.java
+++ b/datamover/sourceTest/java/ch/systemsx/cisd/datamover/SelfTestTest.java
@@ -23,11 +23,14 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
+import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 import ch.systemsx.cisd.common.exceptions.Status;
 import ch.systemsx.cisd.common.logging.LogInitializer;
 import ch.systemsx.cisd.common.utilities.FileUtilities;
+import ch.systemsx.cisd.datamover.filesystem.FileStoreFactory;
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore;
 import ch.systemsx.cisd.datamover.filesystem.intf.IPathCopier;
-import ch.systemsx.cisd.datamover.utils.FileStore;
+import ch.systemsx.cisd.datamover.testhelper.FileOperationsUtil;
 
 /**
  * Test cases for the {@link SelfTest}.
@@ -43,19 +46,17 @@ public class SelfTestTest
 
     private static final File incomingDirectory = new File(workingDirectory, "local/incoming");
 
-    private static final FileStore incomingStore = new FileStore(incomingDirectory, "incoming", null, false);
+    private static final FileStore incomingStore = createLocalStore(incomingDirectory, "incoming");
 
     private static final File bufferDirectory = new File(workingDirectory, "local/buffer");
 
-    private static final FileStore bufferStore = new FileStore(bufferDirectory, "buffer", null, false);
+    private static final FileStore bufferStore = createLocalStore(bufferDirectory, "buffer");
 
     private static final File outgoingDirectory = new File(workingDirectory, "outgoing");
 
-    private static final FileStore outgoingStore = new FileStore(outgoingDirectory, "outgoing", null, false);
+    private static final FileStore outgoingStore = createLocalStore(outgoingDirectory, "outgoing");
 
-    private static final FileStore dummyStore = new FileStore(null, "dummy", null, false);
-
-    private static final IPathCopier mockCopier = new MockPathCopier(false);
+    private static final IPathCopier mockCopier = createMockCopier();
 
     // ////////////////////////////////////////
     // Initialization methods.
@@ -87,71 +88,52 @@ public class SelfTestTest
     // Mocks.
     //
 
-    private static class MockPathCopier implements IPathCopier
+    private static IPathCopier createMockCopier()
     {
-        private final boolean existRemote;
-
-        File destinationDirectoryQueried;
-
-        String destinationHostQueried;
-
-        MockPathCopier(boolean existRemote)
-        {
-            this.existRemote = existRemote;
-        }
-
-        public Status copy(File sourcePath, File destinationDirectory)
-        {
-            throw new AssertionError();
-        }
-
-        public Status copy(File sourcePath, String sourceHost, File destinationDirectory, String destinationHost)
-        {
-            throw new AssertionError();
-        }
-
-        public boolean exists(File destinationDirectory, String destinationHost)
-        {
-            assert destinationDirectoryQueried == null;
-            assert destinationHostQueried == null;
-            assert destinationDirectory != null;
-            assert destinationHost != null;
-
-            destinationDirectoryQueried = destinationDirectory;
-            destinationHostQueried = destinationHost;
-            return existRemote;
-        }
-
-        public boolean terminate()
-        {
-            return false;
-        }
-
-        public void check()
-        {
-        }
-
+        return new IPathCopier()
+            {
+                public Status copy(File sourcePath, File destinationDirectory)
+                {
+                    throw new AssertionError();
+                }
+
+                public Status copyFromRemote(File sourcePath, String sourceHost, File destinationDirectory)
+                {
+                    throw new AssertionError();
+                }
+
+                public Status copyToRemote(File sourcePath, File destinationDirectory, String destinationHost)
+                {
+                    throw new AssertionError();
+                }
+
+                public boolean existsRemotely(File destinationDirectory, String destinationHost)
+                {
+                    throw new AssertionError();
+                }
+
+                public boolean terminate()
+                {
+                    return true;
+                }
+
+                public void check() throws EnvironmentFailureException, ConfigurationFailureException
+                {
+                }
+
+            };
     }
 
     // ////////////////////////////////////////
     // Test cases.
     //
 
-    @Test
-    public void testHappyCaseWithRemoteShare()
-    {
-        SelfTest.check(mockCopier, incomingStore, bufferStore, outgoingStore, dummyStore);
-    }
-
     @Test
     public void testHappyCaseWithRemoteHost()
     {
         final String outgoingHost = "some_remote_host";
-        final FileStore remoteHostOutgoingStore = new FileStore(outgoingDirectory, "outgoing", outgoingHost, true);
-        final MockPathCopier myMockCopier = new MockPathCopier(true);
-        SelfTest.check(myMockCopier, incomingStore, bufferStore, remoteHostOutgoingStore, dummyStore);
-        assert outgoingHost.equals(myMockCopier.destinationHostQueried);
-        assert outgoingDirectory.equals(myMockCopier.destinationDirectoryQueried);
+        final FileStore remoteHostOutgoingStore = createRemoteStore(outgoingDirectory, outgoingHost, "outgoing");
+        SelfTest.check(mockCopier, remoteHostOutgoingStore);
     }
 
     @Test(expectedExceptions = ConfigurationFailureException.class)
@@ -164,7 +146,7 @@ public class SelfTestTest
     public void testContainingPaths()
     {
         final File illegalBufferDirectory = new File(incomingDirectory, "temp");
-        final FileStore illegalBufferStore = new FileStore(illegalBufferDirectory, "buffer", null, false);
+        final FileStore illegalBufferStore = createLocalStore(illegalBufferDirectory, "buffer");
         SelfTest.check(mockCopier, incomingStore, illegalBufferStore, outgoingStore);
     }
 
@@ -172,16 +154,18 @@ public class SelfTestTest
     public void testNonExistentPaths()
     {
         final File nonExistentIncomingDirectory = new File(workingDirectory, "data");
-        final FileStore nonExistentIncomingStore = new FileStore(nonExistentIncomingDirectory, "incoming", null, false);
+        final FileStore nonExistentIncomingStore = createLocalStore(nonExistentIncomingDirectory, "incoming");
         SelfTest.check(mockCopier, nonExistentIncomingStore, bufferStore, outgoingStore);
     }
 
-    @Test(expectedExceptions = ConfigurationFailureException.class)
-    public void testRemoteHostAndDirectoryDoesNotExist()
+    private FileStore createRemoteStore(File path, String host, String description)
+    {
+        return FileStoreFactory.createRemoteHost(path, host, description, FileOperationsUtil.createTestFatory());
+    }
+
+    private static FileStore createLocalStore(File path, String description)
     {
-        final String remoteHost = "some_remote_host";
-        final FileStore outgoingStoreWithRemoteHost = new FileStore(outgoingDirectory, "outgoing", remoteHost, true);
-        SelfTest.check(mockCopier, incomingStore, bufferStore, outgoingStoreWithRemoteHost);
+        return FileStoreFactory.createLocal(path, description, FileOperationsUtil.createTestFatory());
     }
 
 }
diff --git a/datamover/sourceTest/java/ch/systemsx/cisd/datamover/filesystem/impl/RecursiveHardLinkMakerTest.java b/datamover/sourceTest/java/ch/systemsx/cisd/datamover/filesystem/impl/RecursiveHardLinkMakerTest.java
index a46aa677872ed4cc505c85a8654b662b30eb25ec..8f1773492b9aac9ea8c01478f0c1d2e89a6b6333 100644
--- a/datamover/sourceTest/java/ch/systemsx/cisd/datamover/filesystem/impl/RecursiveHardLinkMakerTest.java
+++ b/datamover/sourceTest/java/ch/systemsx/cisd/datamover/filesystem/impl/RecursiveHardLinkMakerTest.java
@@ -31,7 +31,6 @@ import org.testng.annotations.Test;
 import ch.systemsx.cisd.common.logging.LogInitializer;
 import ch.systemsx.cisd.common.utilities.CollectionIO;
 import ch.systemsx.cisd.common.utilities.FileUtilities;
-import ch.systemsx.cisd.datamover.filesystem.impl.RecursiveHardLinkMaker;
 import ch.systemsx.cisd.datamover.filesystem.intf.IPathImmutableCopier;
 
 /**
diff --git a/datamover/sourceTest/java/ch/systemsx/cisd/datamover/filesystem/remote/CopyActivityMonitorTest.java b/datamover/sourceTest/java/ch/systemsx/cisd/datamover/filesystem/remote/CopyActivityMonitorTest.java
index bf49a0061cdb2a089edb51b45b5a0d7cec7fefe6..c993b7b932b6a08923d4b4f7f12bbf0a861ccf17 100644
--- a/datamover/sourceTest/java/ch/systemsx/cisd/datamover/filesystem/remote/CopyActivityMonitorTest.java
+++ b/datamover/sourceTest/java/ch/systemsx/cisd/datamover/filesystem/remote/CopyActivityMonitorTest.java
@@ -17,7 +17,6 @@
 package ch.systemsx.cisd.datamover.filesystem.remote;
 
 import java.io.File;
-import java.io.FileFilter;
 
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
@@ -25,17 +24,20 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import ch.systemsx.cisd.common.exceptions.CheckedExceptionTunnel;
-import ch.systemsx.cisd.common.logging.ISimpleLogger;
+import ch.systemsx.cisd.common.exceptions.Status;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.common.logging.LogInitializer;
 import ch.systemsx.cisd.common.logging.LogMonitoringAppender;
 import ch.systemsx.cisd.common.utilities.ITerminable;
 import ch.systemsx.cisd.common.utilities.StoringUncaughtExceptionHandler;
-import ch.systemsx.cisd.datamover.filesystem.FileSysOperationsFactory;
-import ch.systemsx.cisd.datamover.filesystem.intf.IReadPathOperations;
-import ch.systemsx.cisd.datamover.intf.IFileSysParameters;
+import ch.systemsx.cisd.datamover.common.StoreItem;
+import ch.systemsx.cisd.datamover.filesystem.intf.FileStore;
+import ch.systemsx.cisd.datamover.filesystem.intf.IFileSysOperationsFactory;
+import ch.systemsx.cisd.datamover.filesystem.intf.IStoreCopier;
+import ch.systemsx.cisd.datamover.filesystem.store.FileStoreLocal;
 import ch.systemsx.cisd.datamover.intf.ITimingParameters;
+import ch.systemsx.cisd.datamover.testhelper.FileOperationsUtil;
 
 /**
  * Test cases for the {@link CopyActivityMonitor} class.
@@ -84,63 +86,14 @@ public class CopyActivityMonitorTest
         }
     }
 
-    private static class ReadOperationsOriginalImpl implements IReadPathOperations
+    private static interface LastChangedChecker
     {
-        private final IReadPathOperations impl;
-
-        public ReadOperationsOriginalImpl()
-        {
-            final IFileSysParameters dummyFileSysParameters = new IFileSysParameters()
-            {
-                public String getHardLinkExecutable()
-                {
-                    return null;
-                }
-
-                public String getRsyncExecutable()
-                {
-                    return null;
-                }
-
-                public String getSshExecutable()
-                {
-                    return null;
-                }
-
-                public boolean isRsyncOverwrite()
-                {
-                    return false;
-                }
-                
-            };
-            this.impl = new FileSysOperationsFactory(dummyFileSysParameters).getReadPathOperations();
-        }
-
-        public long lastChanged(File path)
-        {
-            return impl.lastChanged(path);
-        }
-
-        public boolean exists(File file)
-        {
-            return impl.exists(file);
-        }
-
-        public File[] tryListFiles(File directory, FileFilter filter, ISimpleLogger loggerOrNull)
-        {
-            return impl.tryListFiles(directory, filter, loggerOrNull);
-        }
-
-        public File[] tryListFiles(File directory, ISimpleLogger logger)
-        {
-            return impl.tryListFiles(directory, logger);
-        }
+        public long lastChanged(StoreItem item);
     }
 
-    private final class HappyPathLastChangedChecker extends ReadOperationsOriginalImpl
+    private final class HappyPathLastChangedChecker implements LastChangedChecker
     {
-        @Override
-        public long lastChanged(File path)
+        public long lastChanged(StoreItem item)
         {
             return System.currentTimeMillis() - INACTIVITY_PERIOD_MILLIS / 2;
         }
@@ -182,6 +135,55 @@ public class CopyActivityMonitorTest
         }
     }
 
+    private FileStore asFileStore(File directory, final LastChangedChecker checker)
+    {
+        IFileSysOperationsFactory factory = FileOperationsUtil.createTestFatory();
+        return asFileStore(directory, checker, factory);
+    }
+
+    private FileStore asFileStore(File directory, final LastChangedChecker checker, IFileSysOperationsFactory factory)
+    {
+        final FileStoreLocal localImpl = new FileStoreLocal(directory, "input-test", factory);
+        return new FileStore(directory, null, false, "input-test", factory)
+            {
+                @Override
+                public Status delete(StoreItem item)
+                {
+                    return localImpl.delete(item);
+                }
+
+                @Override
+                public boolean exists(StoreItem item)
+                {
+                    return localImpl.exists(item);
+                }
+
+                @Override
+                public long lastChanged(StoreItem item)
+                {
+                    return checker.lastChanged(item);
+                }
+
+                @Override
+                public String tryCheckDirectoryFullyAccessible()
+                {
+                    return localImpl.tryCheckDirectoryFullyAccessible();
+                }
+
+                @Override
+                public ExtendedFileStore tryAsExtended()
+                {
+                    return localImpl.tryAsExtended();
+                }
+
+                @Override
+                public IStoreCopier getCopier(FileStore destinationDirectory)
+                {
+                    return localImpl.getCopier(destinationDirectory);
+                }
+            };
+    }
+
     // ////////////////////////////////////////
     // Initialization methods.
     //
@@ -218,15 +220,13 @@ public class CopyActivityMonitorTest
         { "slow" })
     public void testHappyPath() throws Throwable
     {
-        final IReadPathOperations checker = new HappyPathLastChangedChecker();
+        final LastChangedChecker checker = new HappyPathLastChangedChecker();
         final ITerminable dummyTerminable = new DummyTerminable();
         final ITimingParameters parameters = new MyTimingParameters(0);
         final CopyActivityMonitor monitor =
-                new CopyActivityMonitor(workingDirectory, checker, dummyTerminable, parameters);
-        final File directory = new File(workingDirectory, "some-directory");
-        directory.mkdir();
-        directory.deleteOnExit();
-        monitor.start(directory);
+                new CopyActivityMonitor(asFileStore(workingDirectory, checker), dummyTerminable, parameters);
+        StoreItem item = createDirectoryInside(workingDirectory);
+        monitor.start(item);
         Thread.sleep(INACTIVITY_PERIOD_MILLIS * 15);
         monitor.stop();
     }
@@ -235,24 +235,23 @@ public class CopyActivityMonitorTest
         { "slow" })
     public void testCopyStalled() throws Throwable
     {
-        final IReadPathOperations checker = new PathLastChangedCheckerStalled();
+        final LastChangedChecker checker = new PathLastChangedCheckerStalled();
         final MockTerminable copyProcess = new MockTerminable();
         final ITimingParameters parameters = new MyTimingParameters(0);
-        final CopyActivityMonitor monitor = new CopyActivityMonitor(workingDirectory, checker, copyProcess, parameters);
-        final File file = new File(workingDirectory, "some-directory");
-        file.mkdir();
-        monitor.start(file);
+        final CopyActivityMonitor monitor =
+                new CopyActivityMonitor(asFileStore(workingDirectory, checker), copyProcess, parameters);
+        StoreItem item = createDirectoryInside(workingDirectory);
+        monitor.start(item);
         Thread.sleep(INACTIVITY_PERIOD_MILLIS * 15);
         monitor.stop();
         assert copyProcess.isTerminated();
     }
 
-    private final class SimulateShortInterruptionChangedChecker extends ReadOperationsOriginalImpl
+    private final class SimulateShortInterruptionChangedChecker implements LastChangedChecker
     {
         private int numberOfTimesCalled = 0;
 
-        @Override
-        public long lastChanged(File path)
+        public long lastChanged(StoreItem item)
         {
             ++numberOfTimesCalled;
             if (numberOfTimesCalled == 2)
@@ -269,33 +268,32 @@ public class CopyActivityMonitorTest
     }
 
     /**
-     * This test case catches a case that I first hadn't thought of: since we use <code>rsync</code> (or
-     * <code>xcopy</code>) in a mode where at the end of copying a file they set the "last modified" time back to the
-     * one of the source file, there is a short time interval after finishing copying one file anst starting copying the
-     * next file where the copy monitor could be tempted to trigger false alarm: the just finished file will have
-     * already the "last modified" time of the source file (which is when the data produce finished writing the source
-     * file). In fact everything is fine but still the copy process will be cancelled.
+     * This test case catches a case that I first hadn't thought of: since we use <code>rsync</code> in a mode where
+     * at the end of copying a file they set the "last modified" time back to the one of the source file, there is a
+     * short time interval after finishing copying one file anst starting copying the next file where the copy monitor
+     * could be tempted to trigger false alarm: the just finished file will have already the "last modified" time of the
+     * source file (which is when the data produce finished writing the source file). In fact everything is fine but
+     * still the copy process will be cancelled.
      */
     @Test(groups =
         { "slow" })
     public void testCopySeemsStalledButActuallyIsFine() throws Throwable
     {
-        final IReadPathOperations checker = new SimulateShortInterruptionChangedChecker();
+        final LastChangedChecker checker = new SimulateShortInterruptionChangedChecker();
         final MockTerminable copyProcess = new MockTerminable();
         final ITimingParameters parameters = new MyTimingParameters(0);
-        final CopyActivityMonitor monitor = new CopyActivityMonitor(workingDirectory, checker, copyProcess, parameters);
-        final File file = new File(workingDirectory, "some-directory");
-        file.mkdir();
-        monitor.start(file);
+        final CopyActivityMonitor monitor =
+                new CopyActivityMonitor(asFileStore(workingDirectory, checker), copyProcess, parameters);
+        StoreItem item = createDirectoryInside(workingDirectory);
+        monitor.start(item);
         Thread.sleep(INACTIVITY_PERIOD_MILLIS * 15);
         monitor.stop();
         assert copyProcess.isTerminated() == false;
     }
 
-    private final class PathLastChangedCheckerStalled extends ReadOperationsOriginalImpl
+    private final class PathLastChangedCheckerStalled implements LastChangedChecker
     {
-        @Override
-        public long lastChanged(File path)
+        public long lastChanged(StoreItem item)
         {
             return System.currentTimeMillis() - INACTIVITY_PERIOD_MILLIS * 2;
         }
@@ -308,14 +306,13 @@ public class CopyActivityMonitorTest
         LogMonitoringAppender appender =
                 LogMonitoringAppender.addAppender(LogCategory.OPERATION, "Activity monitor got terminated");
         LogFactory.getLogger(LogCategory.OPERATION, CopyActivityMonitor.class).addAppender(appender);
-        final IReadPathOperations checker = new PathLastChangedCheckerStuck();
+        final LastChangedChecker checker = new PathLastChangedCheckerStuck();
         final MockTerminable copyProcess = new MockTerminable();
         final ITimingParameters parameters = new MyTimingParameters(0);
-        final CopyActivityMonitor monitor = new CopyActivityMonitor(workingDirectory, checker, copyProcess, parameters);
-        final File directory = new File(workingDirectory, "some-directory");
-        directory.mkdir();
-        directory.deleteOnExit();
-        monitor.start(directory);
+        final CopyActivityMonitor monitor =
+                new CopyActivityMonitor(asFileStore(workingDirectory, checker), copyProcess, parameters);
+        StoreItem item = createDirectoryInside(workingDirectory);
+        monitor.start(item);
         Thread.sleep(INACTIVITY_PERIOD_MILLIS * 15);
         monitor.stop();
         LogMonitoringAppender.removeAppender(appender);
@@ -323,12 +320,20 @@ public class CopyActivityMonitorTest
         appender.verifyLogHasHappened();
     }
 
-    private final class PathLastChangedCheckerStuck extends ReadOperationsOriginalImpl
+    private StoreItem createDirectoryInside(File parentDir)
+    {
+        StoreItem item = new StoreItem("some-directory");
+        final File directory = new File(parentDir, item.getName());
+        directory.mkdir();
+        directory.deleteOnExit();
+        return item;
+    }
+
+    private final class PathLastChangedCheckerStuck implements LastChangedChecker
     {
         private boolean interrupted = false;
 
-        @Override
-        public long lastChanged(File path)
+        public long lastChanged(StoreItem item)
         {
             try
             {
diff --git a/datamover/sourceTest/java/ch/systemsx/cisd/datamover/filesystem/remote/rsync/RsyncCopierTest.java b/datamover/sourceTest/java/ch/systemsx/cisd/datamover/filesystem/remote/rsync/RsyncCopierTest.java
index 0f341df87852c677961dd662a392e1ee5695d7b8..22c20afe83522544b08f243880c40221381a6008 100644
--- a/datamover/sourceTest/java/ch/systemsx/cisd/datamover/filesystem/remote/rsync/RsyncCopierTest.java
+++ b/datamover/sourceTest/java/ch/systemsx/cisd/datamover/filesystem/remote/rsync/RsyncCopierTest.java
@@ -36,7 +36,6 @@ import ch.systemsx.cisd.common.logging.LogInitializer;
 import ch.systemsx.cisd.common.utilities.CollectionIO;
 import ch.systemsx.cisd.common.utilities.FileUtilities;
 import ch.systemsx.cisd.common.utilities.StoringUncaughtExceptionHandler;
-import ch.systemsx.cisd.datamover.filesystem.remote.rsync.RsyncCopier;
 
 /**
  * Test cases for the {@link RsyncCopier} class.
@@ -103,8 +102,8 @@ public class RsyncCopierTest
         { "requires_unix" })
     public void testRsyncOK() throws IOException, InterruptedException
     {
-        final File dummyRsyncBinary = createRsync(0);
-        final RsyncCopier copier = new RsyncCopier(dummyRsyncBinary, null, false, false);
+        final File buggyRsyncBinary = createRsync(0);
+        final RsyncCopier copier = new RsyncCopier(buggyRsyncBinary, null, false, false);
         Status status = copier.copy(sourceFile, destinationDirectory);
         assert Status.OK == status;
     }
@@ -114,8 +113,8 @@ public class RsyncCopierTest
     public void testRsyncRetriableFailure() throws IOException, InterruptedException
     {
         final int exitValue = 11;
-        final File dummyRsyncBinary = createRsync(exitValue);
-        final RsyncCopier copier = new RsyncCopier(dummyRsyncBinary, null, false, false);
+        final File buggyRsyncBinary = createRsync(exitValue);
+        final RsyncCopier copier = new RsyncCopier(buggyRsyncBinary, null, false, false);
         Status status = copier.copy(sourceFile, destinationDirectory);
         assertEquals(StatusFlag.RETRIABLE_ERROR, status.getFlag());
         assertEquals(RsyncExitValueTranslator.getMessage(exitValue), status.getMessage());
@@ -126,8 +125,8 @@ public class RsyncCopierTest
     public void testRsyncFatalFailure() throws IOException, InterruptedException
     {
         final int exitValue = 1;
-        final File dummyRsyncBinary = createRsync(exitValue);
-        final RsyncCopier copier = new RsyncCopier(dummyRsyncBinary, null, false, false);
+        final File buggyRsyncBinary = createRsync(exitValue);
+        final RsyncCopier copier = new RsyncCopier(buggyRsyncBinary, null, false, false);
         Status status = copier.copy(sourceFile, destinationDirectory);
         assertEquals(StatusFlag.FATAL_ERROR, status.getFlag());
         assertEquals(RsyncExitValueTranslator.getMessage(exitValue), status.getMessage());
diff --git a/datamover/sourceTest/java/ch/systemsx/cisd/datamover/testhelper/FileOperationsUtil.java b/datamover/sourceTest/java/ch/systemsx/cisd/datamover/testhelper/FileOperationsUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..d5d30bc3552f98d034c40e3b97b3615a918c6617
--- /dev/null
+++ b/datamover/sourceTest/java/ch/systemsx/cisd/datamover/testhelper/FileOperationsUtil.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2007 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.datamover.testhelper;
+
+import ch.systemsx.cisd.datamover.filesystem.FileSysOperationsFactory;
+import ch.systemsx.cisd.datamover.filesystem.intf.IFileSysOperationsFactory;
+import ch.systemsx.cisd.datamover.intf.IFileSysParameters;
+
+/**
+ * @author Tomasz Pylak on Oct 11, 2007
+ */
+public class FileOperationsUtil
+{
+    public final static IFileSysOperationsFactory createTestFatory()
+    {
+        return new FileSysOperationsFactory(createDummyFileSysParameters());
+    }
+
+    private static IFileSysParameters createDummyFileSysParameters()
+    {
+        return new IFileSysParameters()
+            {
+                public String getHardLinkExecutable()
+                {
+                    return null;
+                }
+
+                public String getRsyncExecutable()
+                {
+                    return null;
+                }
+
+                public String getSshExecutable()
+                {
+                    return null;
+                }
+
+                public boolean isRsyncOverwrite()
+                {
+                    return false;
+                }
+            };
+    }
+}
diff --git a/datamover/sourceTest/java/ch/systemsx/cisd/datamover/testhelper/FileStructEngine.java b/datamover/sourceTest/java/ch/systemsx/cisd/datamover/testhelper/FileStructEngine.java
index 6230616db2e02f673198371991e463f79fc6b9a5..04baf32946eed8d99376827113ec87cd044a4f4e 100644
--- a/datamover/sourceTest/java/ch/systemsx/cisd/datamover/testhelper/FileStructEngine.java
+++ b/datamover/sourceTest/java/ch/systemsx/cisd/datamover/testhelper/FileStructEngine.java
@@ -16,17 +16,21 @@
 
 package ch.systemsx.cisd.datamover.testhelper;
 
+import static ch.systemsx.cisd.datamover.testhelper.FileSystemHelper.assertDirExists;
+import static ch.systemsx.cisd.datamover.testhelper.FileSystemHelper.createDir;
+import static ch.systemsx.cisd.datamover.testhelper.FileSystemHelper.createEmptyFile;
+import static ch.systemsx.cisd.datamover.testhelper.FileSystemHelper.createFile;
+import static org.testng.AssertJUnit.assertTrue;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-import static org.testng.AssertJUnit.*;
-import static ch.systemsx.cisd.datamover.testhelper.FileSystemHelper.*;
-
 import ch.systemsx.cisd.common.utilities.CollectionIO;
 import ch.systemsx.cisd.datamover.common.MarkerFile;
+import ch.systemsx.cisd.datamover.common.StoreItem;
 
 /**
  * Immutable helper for creating a sample directory structure and manipulating it.
@@ -87,7 +91,8 @@ public class FileStructEngine
 
     private static File createDeletionInProgressMarkerFile(File parentDir, String originalName)
     {
-        return MarkerFile.createDeletionInProgressMarker(parentDir, originalName);
+        StoreItem marker = MarkerFile.createDeletionInProgressMarker(new StoreItem(originalName));
+        return new File(parentDir, marker.getName());
     }
 
     private static List<String> createSampleFileContent()