diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataSetRegistrationAlgorithm.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataSetRegistrationAlgorithm.java
index 9ad70f4b99a20d21594d4e2a3da0f2e1e574b7ef..d51c91f3a817f02a793a4e16b193cf417565a799 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataSetRegistrationAlgorithm.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataSetRegistrationAlgorithm.java
@@ -120,7 +120,7 @@ public class DataSetRegistrationAlgorithm
 
         public DataSetRegistrationAlgorithmState(File incomingDataSetFile,
                 IEncapsulatedOpenBISService openBisService,
-                IDelegatedActionWithResult<Boolean> cleanAftrewardsAction,
+                IDelegatedActionWithResult<Boolean> cleanAfterwardsAction,
                 IPreRegistrationAction preRegistrationAction,
                 IPostRegistrationAction postRegistrationAction,
                 DataSetInformation dataSetInformation, IDataStoreStrategy dataStoreStrategy,
@@ -131,7 +131,7 @@ public class DataSetRegistrationAlgorithm
         {
             this.incomingDataSetFile = incomingDataSetFile;
             this.openBisService = openBisService;
-            this.cleanAfterwardsAction = cleanAftrewardsAction;
+            this.cleanAfterwardsAction = cleanAfterwardsAction;
             this.preRegistrationAction = preRegistrationAction;
             this.postRegistrationAction = postRegistrationAction;
             this.dataSetInformation = dataSetInformation;
@@ -204,7 +204,8 @@ public class DataSetRegistrationAlgorithm
     }
 
     public DataSetRegistrationAlgorithm(DataSetRegistrationAlgorithmState state,
-            IRollbackDelegate rollbackDelegate, IDataSetInApplicationServerRegistrator applicationServerRegistrator)
+            IRollbackDelegate rollbackDelegate,
+            IDataSetInApplicationServerRegistrator applicationServerRegistrator)
     {
         this.state = state;
         this.rollbackDelegate = rollbackDelegate;
@@ -352,7 +353,8 @@ public class DataSetRegistrationAlgorithm
         return state.cleanAfterwardsAction.execute();
     }
 
-    private static class DefaultApplicationServerRegistrator implements IDataSetInApplicationServerRegistrator
+    private static class DefaultApplicationServerRegistrator implements
+            IDataSetInApplicationServerRegistrator
     {
         private final IEncapsulatedOpenBISService openBisService;
 
@@ -629,4 +631,9 @@ public class DataSetRegistrationAlgorithm
     {
         return operationLog;
     }
+
+    public File getIncomingDataSetFile()
+    {
+        return state.incomingDataSetFile;
+    }
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataSetRegistrationHelper.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataSetRegistrationHelper.java
index 89bff7aa93e128479b04ac3c67ffcec3a769b28e..4d3c71b09431d9671ae59b0840e3484ea65df772 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataSetRegistrationHelper.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataSetRegistrationHelper.java
@@ -17,13 +17,9 @@
 package ch.systemsx.cisd.etlserver;
 
 import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
 import java.util.List;
 import java.util.concurrent.locks.Lock;
 
-import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
 
@@ -86,7 +82,8 @@ public abstract class DataSetRegistrationHelper implements
     public DataSetRegistrationHelper(File incomingDataSetFile,
             IDelegatedActionWithResult<Boolean> cleanAftrewardsAction,
             IPreRegistrationAction preRegistrationAction,
-            IPostRegistrationAction postRegistrationAction, IDataSetInApplicationServerRegistrator appServerRegistrator)
+            IPostRegistrationAction postRegistrationAction,
+            IDataSetInApplicationServerRegistrator appServerRegistrator)
     {
         DataSetInformation dataSetInformation = extractDataSetInformation(incomingDataSetFile);
         IDataStoreStrategy dataStoreStrategy =
@@ -188,28 +185,6 @@ public abstract class DataSetRegistrationHelper implements
         }
     }
 
-    protected final void writeThrowable(final Throwable throwable)
-    {
-        final String fileName = incomingDataSetFile.getName() + ".exception";
-        final File file =
-                new File(registrationAlgorithm.getBaseDirectoryHolder().getTargetFile()
-                        .getParentFile(), fileName);
-        FileWriter writer = null;
-        try
-        {
-            writer = new FileWriter(file);
-            throwable.printStackTrace(new PrintWriter(writer));
-        } catch (final IOException e)
-        {
-            TransferredDataSetHandler.operationLog.warn(String.format(
-                    "Could not write out the exception '%s' in file '%s'.", fileName,
-                    file.getAbsolutePath()), e);
-        } finally
-        {
-            IOUtils.closeQuietly(writer);
-        }
-    }
-
     public void rollback(DataSetRegistrationAlgorithm algo, Throwable ex)
     {
         rollback(ex);
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataSetRegistrationRollbacker.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataSetRegistrationRollbacker.java
new file mode 100644
index 0000000000000000000000000000000000000000..7e80abcbf93eaf8f53f072dda634b141f64ac280
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataSetRegistrationRollbacker.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2011 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.etlserver;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
+
+import ch.systemsx.cisd.common.filesystem.FileUtilities;
+import ch.systemsx.cisd.common.logging.Log4jSimpleLogger;
+import ch.systemsx.cisd.etlserver.IStorageProcessor.UnstoreDataAction;
+
+/**
+ * Utility class that rollsback a data set registration.
+ * 
+ * @author Chandrasekhar Ramakrishnan
+ */
+public class DataSetRegistrationRollbacker
+{
+    private final boolean stopped;
+
+    private final DataSetRegistrationAlgorithm registrationAlgorithm;
+
+    private final File incomingDataSetFile;
+
+    private final Logger notificationLog;
+
+    private final Logger operationLog;
+
+    private final Throwable throwable;
+
+    public DataSetRegistrationRollbacker(boolean stopped,
+            DataSetRegistrationAlgorithm registrationAlgorithm, File incomingDataSetFile,
+            Logger notificationLog, Logger operationLog, Throwable throwable)
+    {
+        this.stopped = stopped;
+        this.registrationAlgorithm = registrationAlgorithm;
+        this.incomingDataSetFile = incomingDataSetFile;
+        this.notificationLog = notificationLog;
+        this.operationLog = operationLog;
+        this.throwable = throwable;
+    }
+
+    public void doRollback() throws Error
+    {
+        if (stopped)
+        {
+            Thread.interrupted(); // Ensure the thread's interrupted state is cleared.
+            getOperationLog().warn(
+                    String.format("Requested to stop registration of data set '%s'",
+                            registrationAlgorithm.getDataSetInformation()));
+        } else
+        {
+            getNotificationLog().error(
+                    String.format(registrationAlgorithm.getErrorMessageTemplate(),
+                            registrationAlgorithm.getDataSetInformation()), throwable);
+        }
+        // Errors which are not AssertionErrors leave the system in a state that we don't
+        // know and can't trust. Thus we will not perform any operations any more in this
+        // case.
+        if (throwable instanceof Error && throwable instanceof AssertionError == false)
+        {
+            throw (Error) throwable;
+        }
+        UnstoreDataAction action = registrationAlgorithm.rollbackStorageProcessor(throwable);
+        if (stopped == false)
+        {
+            if (action == UnstoreDataAction.MOVE_TO_ERROR)
+            {
+                final File baseDirectory =
+                        registrationAlgorithm.createBaseDirectory(
+                                TransferredDataSetHandler.ERROR_DATA_STRATEGY,
+                                registrationAlgorithm.getStoreRoot(),
+                                registrationAlgorithm.getDataSetInformation());
+                registrationAlgorithm.setBaseDirectoryHolder(new BaseDirectoryHolder(
+                        TransferredDataSetHandler.ERROR_DATA_STRATEGY, baseDirectory,
+                        incomingDataSetFile));
+                boolean moveInCaseOfErrorOk =
+                        FileRenamer.renameAndLog(incomingDataSetFile, registrationAlgorithm
+                                .getBaseDirectoryHolder().getTargetFile());
+                writeThrowable();
+                if (moveInCaseOfErrorOk)
+                {
+                    registrationAlgorithm.clean();
+                }
+            } else if (action == UnstoreDataAction.DELETE)
+            {
+                FileUtilities.deleteRecursively(incomingDataSetFile, new Log4jSimpleLogger(
+                        TransferredDataSetHandler.operationLog));
+            }
+        }
+    }
+
+    private Logger getNotificationLog()
+    {
+        return notificationLog;
+    }
+
+    private Logger getOperationLog()
+    {
+        return operationLog;
+    }
+
+    protected final void writeThrowable()
+    {
+        final String fileName = incomingDataSetFile.getName() + ".exception";
+        final File file =
+                new File(registrationAlgorithm.getBaseDirectoryHolder().getTargetFile()
+                        .getParentFile(), fileName);
+        FileWriter writer = null;
+        try
+        {
+            writer = new FileWriter(file);
+            throwable.printStackTrace(new PrintWriter(writer));
+        } catch (final IOException e)
+        {
+            TransferredDataSetHandler.operationLog.warn(String.format(
+                    "Could not write out the exception '%s' in file '%s'.", fileName,
+                    file.getAbsolutePath()), e);
+        } finally
+        {
+            IOUtils.closeQuietly(writer);
+        }
+    }
+}
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataStoreStrategyKey.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataStoreStrategyKey.java
index c162760ea48deea523738641973784fbac7c0e5e..5aa0504c3a8b92b07768a0cb474ce8291afe4428 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataStoreStrategyKey.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataStoreStrategyKey.java
@@ -23,13 +23,13 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation;
  * 
  * @author Christian Ribeaud
  */
-enum DataStoreStrategyKey
+public enum DataStoreStrategyKey
 {
     /**
-     * This <code>IDataStoreStrategy</code> implementation if for data set that has been
-     * identified as <i>unidentified</i>, meaning that, for instance, no experiment could be mapped
-     * to the one found in given {@link DataSetInformation} (if we try to find out the sample to
-     * which this data set should be registered through the experiment).
+     * This <code>IDataStoreStrategy</code> implementation if for data set that has been identified
+     * as <i>unidentified</i>, meaning that, for instance, no experiment could be mapped to the one
+     * found in given {@link DataSetInformation} (if we try to find out the sample to which this
+     * data set should be registered through the experiment).
      */
     UNIDENTIFIED,
     /**
@@ -39,10 +39,10 @@ enum DataStoreStrategyKey
      */
     IDENTIFIED,
     /**
-     * This <code>IDataStoreStrategy</code> implementation if for data set that has been
-     * identified as <i>invalid</i>, meaning that the data set itself or its
-     * <code>Master Plate</code> code is not registered in the database. So there is no
-     * possibility to link the data set to an already existing sample.
+     * This <code>IDataStoreStrategy</code> implementation if for data set that has been identified
+     * as <i>invalid</i>, meaning that the data set itself or its <code>Master Plate</code> code is
+     * not registered in the database. So there is no possibility to link the data set to an already
+     * existing sample.
      */
     INVALID,
     /**
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/IDataStoreStrategy.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/IDataStoreStrategy.java
index c5ea87451e33b1b865e490e5d9188fac49b4c56e..ed58161928a1471c626553ed66fa7bb8e37f43ed 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/IDataStoreStrategy.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/IDataStoreStrategy.java
@@ -26,7 +26,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetType;
  * 
  * @author Christian Ribeaud
  */
-interface IDataStoreStrategy
+public interface IDataStoreStrategy
 {
 
     /**
@@ -44,7 +44,8 @@ interface IDataStoreStrategy
             final DataSetType dataSetType);
 
     /**
-     * Create the target path for given <var>baseDirectory</var> and given <var>incomingDataSetPath</var>.
+     * Create the target path for given <var>baseDirectory</var> and given
+     * <var>incomingDataSetPath</var>.
      * <p>
      * Note that each call either produces a new <i>target path</i> or throws an exception if
      * computed <i>target path</i> already exists.
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/PropertiesBasedETLServerPlugin.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/PropertiesBasedETLServerPlugin.java
index 2f788d8332f4ef085218591dc26e89b5a2a25eee..d14eb8de180be243ea2586e88a8795691b3097f1 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/PropertiesBasedETLServerPlugin.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/PropertiesBasedETLServerPlugin.java
@@ -74,7 +74,10 @@ public class PropertiesBasedETLServerPlugin extends ETLServerPlugin
 
     private static final Properties EMPTY_PROPERTIES = new Properties();
 
-    private final static <T> T create(final Class<T> superClazz, final Properties properties,
+    /**
+     * Utility method to create objects from keys in the properties file.
+     */
+    public final static <T> T create(final Class<T> superClazz, final Properties properties,
             final String keyPrefix, final boolean withSubset, final Object... arguments)
     {
         final String className = properties.getProperty(keyPrefix);
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/TransferredDataSetHandler.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/TransferredDataSetHandler.java
index ef7a50a0cccb16d8fef77984e25f98250f5def56..2ec9fc42222de05dc544598c92d8d140c64cf5f7 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/TransferredDataSetHandler.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/TransferredDataSetHandler.java
@@ -29,15 +29,14 @@ import ch.systemsx.cisd.base.exceptions.InterruptedExceptionUnchecked;
 import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 import ch.systemsx.cisd.common.filesystem.FileOperations;
-import ch.systemsx.cisd.common.filesystem.FileUtilities;
 import ch.systemsx.cisd.common.filesystem.IFileOperations;
-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.mail.IMailClient;
 import ch.systemsx.cisd.common.utilities.IDelegatedActionWithResult;
 import ch.systemsx.cisd.etlserver.DataSetRegistrationAlgorithm.IDataSetInApplicationServerRegistrator;
-import ch.systemsx.cisd.etlserver.IStorageProcessor.UnstoreDataAction;
+import ch.systemsx.cisd.etlserver.registrator.MarkerFileUtility;
+import ch.systemsx.cisd.etlserver.registrator.TopLevelDataSetChecker;
 import ch.systemsx.cisd.etlserver.utils.PostRegistrationExecutor;
 import ch.systemsx.cisd.etlserver.utils.PreRegistrationExecutor;
 import ch.systemsx.cisd.etlserver.validation.IDataSetValidator;
@@ -63,7 +62,7 @@ public final class TransferredDataSetHandler extends AbstractTopLevelDataSetRegi
     static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION,
             TransferredDataSetHandler.class);
 
-    static final NamedDataStrategy ERROR_DATA_STRATEGY = new NamedDataStrategy(
+    public static final NamedDataStrategy ERROR_DATA_STRATEGY = new NamedDataStrategy(
             DataStoreStrategyKey.ERROR);
 
     private final IStoreRootDirectoryHolder storeRootDirectoryHolder;
@@ -104,6 +103,8 @@ public final class TransferredDataSetHandler extends AbstractTopLevelDataSetRegi
 
     private final IPostRegistrationAction postRegistrationAction;
 
+    private final MarkerFileUtility markerFileUtility;
+
     /**
      * The designated constructor.
      * 
@@ -111,44 +112,8 @@ public final class TransferredDataSetHandler extends AbstractTopLevelDataSetRegi
      */
     public TransferredDataSetHandler(TopLevelDataSetRegistratorGlobalState globalState)
     {
-        super(globalState);
-
-        this.dssCode = globalState.getDssCode();
-        assert dssCode != null : "Unspecified data store code";
-
-        IETLServerPlugin plugin =
-                ETLServerPluginFactory.getPluginForThread(globalState.getThreadParameters());
-        assert plugin != null : "IETLServerPlugin implementation can not be null.";
-
-        storeRootDirectoryHolder = plugin.getStorageProcessor();
-        assert storeRootDirectoryHolder != null : "Given store root directory holder can not be null.";
-
-        this.limsService = globalState.getOpenBisService();
-        assert limsService != null : "IEncapsulatedLimsService implementation can not be null.";
-
-        this.mailClient = globalState.getMailClient();
-        assert mailClient != null : "IMailClient implementation can not be null.";
-
-        this.dataSetInfoExtractor = plugin.getDataSetInfoExtractor();
-        this.typeExtractor = plugin.getTypeExtractor();
-        this.storageProcessor = plugin.getStorageProcessor();
-        dataSetHandler = plugin.getDataSetHandler(this, limsService);
-        if (dataSetHandler instanceof IDataSetHandlerWithMailClient)
-        {
-            ((IDataSetHandlerWithMailClient) dataSetHandler).initializeMailClient(mailClient);
-        }
-        this.dataSetValidator = globalState.getDataSetValidator();
-        this.dataStrategyStore = new DataStrategyStore(this.limsService, mailClient);
-        this.notifySuccessfulRegistration = globalState.isNotifySuccessfulRegistration();
-        this.registrationLock = new ReentrantLock();
-        this.fileOperations = FileOperations.getMonitoredInstanceForCurrentThread();
-        this.useIsFinishedMarkerFile = globalState.isUseIsFinishedMarkerFile();
-        this.deleteUnidentified = globalState.isDeleteUnidentified();
-        this.preRegistrationAction =
-                PreRegistrationExecutor.create(globalState.getPreRegistrationScriptOrNull());
-        this.postRegistrationAction =
-                PostRegistrationExecutor.create(globalState.getPostRegistrationScriptOrNull());
-
+        this(globalState, ETLServerPluginFactory.getPluginForThread(globalState
+                .getThreadParameters()));
     }
 
     /**
@@ -196,6 +161,10 @@ public final class TransferredDataSetHandler extends AbstractTopLevelDataSetRegi
         this.postRegistrationAction =
                 PostRegistrationExecutor.create(globalState.getPostRegistrationScriptOrNull());
 
+        this.markerFileUtility =
+                new MarkerFileUtility(operationLog, notificationLog, fileOperations,
+                        storeRootDirectoryHolder);
+
     }
 
     /**
@@ -221,8 +190,7 @@ public final class TransferredDataSetHandler extends AbstractTopLevelDataSetRegi
 
     public List<DataSetInformation> handleDataSet(final File dataSet)
     {
-        final DataSetRegistrationHelper registrationHelper =
-                createRegistrationHelper(dataSet);
+        final DataSetRegistrationHelper registrationHelper = createRegistrationHelper(dataSet);
         return new DataSetRegistrationAlgorithmRunner(registrationHelper).runAlgorithm();
     }
 
@@ -248,27 +216,8 @@ public final class TransferredDataSetHandler extends AbstractTopLevelDataSetRegi
 
     public final void check() throws ConfigurationFailureException, EnvironmentFailureException
     {
-        final File storeRootDirectory = storeRootDirectoryHolder.getStoreRootDirectory();
-        storeRootDirectory.mkdirs();
-        if (operationLog.isDebugEnabled())
-        {
-            operationLog.debug("Checking store root directory '"
-                    + storeRootDirectory.getAbsolutePath() + "'.");
-        }
-        final String errorMessage =
-                fileOperations.checkDirectoryFullyAccessible(storeRootDirectory, "store root");
-        if (errorMessage != null)
-        {
-            if (fileOperations.exists(storeRootDirectory) == false)
-            {
-                throw EnvironmentFailureException.fromTemplate(
-                        "Store root directory '%s' does not exist.",
-                        storeRootDirectory.getAbsolutePath());
-            } else
-            {
-                throw new ConfigurationFailureException(errorMessage);
-            }
-        }
+        new TopLevelDataSetChecker(operationLog, storeRootDirectoryHolder, fileOperations)
+                .runCheck();
     }
 
     public boolean isRemote()
@@ -300,8 +249,8 @@ public final class TransferredDataSetHandler extends AbstractTopLevelDataSetRegi
         }
     }
 
-    private DataSetRegistrationHelper createRegistrationHelper(
-            File dataSet, DataSetInformation dataSetInformation,
+    private DataSetRegistrationHelper createRegistrationHelper(File dataSet,
+            DataSetInformation dataSetInformation,
             DataSetRegistrationAlgorithm.IDataSetInApplicationServerRegistrator registrator)
     {
         if (useIsFinishedMarkerFile)
@@ -393,46 +342,12 @@ public final class TransferredDataSetHandler extends AbstractTopLevelDataSetRegi
      */
     private final File getIncomingDataSetPathFromMarker(final File isFinishedPath)
     {
-        final File incomingDataSetPath =
-                FileUtilities.removePrefixFromFileName(isFinishedPath, IS_FINISHED_PREFIX);
-        if (operationLog.isDebugEnabled())
-        {
-            operationLog.debug(String.format(
-                    "Getting incoming data set path '%s' from is-finished path '%s'",
-                    incomingDataSetPath, isFinishedPath));
-        }
-        final String errorMsg =
-                fileOperations.checkPathFullyAccessible(incomingDataSetPath, "incoming data set");
-        if (errorMsg != null)
-        {
-            fileOperations.delete(isFinishedPath);
-            throw EnvironmentFailureException.fromTemplate(String.format(
-                    "Error moving path '%s' from '%s' to '%s': %s", incomingDataSetPath.getName(),
-                    incomingDataSetPath.getParent(),
-                    storeRootDirectoryHolder.getStoreRootDirectory(), errorMsg));
-        }
-        return incomingDataSetPath;
+        return markerFileUtility.getIncomingDataSetPathFromMarker(isFinishedPath);
     }
 
     private boolean deleteAndLogIsFinishedMarkerFile(File isFinishedFile)
     {
-        if (fileOperations.exists(isFinishedFile) == false)
-        {
-            return false;
-        }
-        final boolean ok = fileOperations.delete(isFinishedFile);
-        final String absolutePath = isFinishedFile.getAbsolutePath();
-        if (ok == false)
-        {
-            notificationLog.error(String.format("Removing file '%s' failed.", absolutePath));
-        } else
-        {
-            if (operationLog.isDebugEnabled())
-            {
-                operationLog.debug(String.format("File '%s' has been removed.", absolutePath));
-            }
-        }
-        return ok;
+        return markerFileUtility.deleteAndLogIsFinishedMarkerFile(isFinishedFile);
     }
 
     private class RegistrationHelper extends DataSetRegistrationHelper
@@ -554,52 +469,9 @@ public final class TransferredDataSetHandler extends AbstractTopLevelDataSetRegi
         protected void rollback(final Throwable throwable) throws Error
         {
             stopped |= throwable instanceof InterruptedExceptionUnchecked;
-            if (stopped)
-            {
-                Thread.interrupted(); // Ensure the thread's interrupted state is cleared.
-                getOperationLog().warn(
-                        String.format("Requested to stop registration of data set '%s'",
-                                registrationAlgorithm.getDataSetInformation()));
-            } else
-            {
-                getNotificationLog().error(
-                        String.format(registrationAlgorithm.getErrorMessageTemplate(),
-                                registrationAlgorithm.getDataSetInformation()), throwable);
-            }
-            // Errors which are not AssertionErrors leave the system in a state that we don't
-            // know and can't trust. Thus we will not perform any operations any more in this
-            // case.
-            if (throwable instanceof Error && throwable instanceof AssertionError == false)
-            {
-                throw (Error) throwable;
-            }
-            UnstoreDataAction action = registrationAlgorithm.rollbackStorageProcessor(throwable);
-            if (stopped == false)
-            {
-                if (action == UnstoreDataAction.MOVE_TO_ERROR)
-                {
-                    final File baseDirectory =
-                            registrationAlgorithm.createBaseDirectory(
-                                    TransferredDataSetHandler.ERROR_DATA_STRATEGY,
-                                    registrationAlgorithm.getStoreRoot(),
-                                    registrationAlgorithm.getDataSetInformation());
-                    registrationAlgorithm.setBaseDirectoryHolder(new BaseDirectoryHolder(
-                            TransferredDataSetHandler.ERROR_DATA_STRATEGY, baseDirectory,
-                            incomingDataSetFile));
-                    boolean moveInCaseOfErrorOk =
-                            FileRenamer.renameAndLog(incomingDataSetFile, registrationAlgorithm
-                                    .getBaseDirectoryHolder().getTargetFile());
-                    writeThrowable(throwable);
-                    if (moveInCaseOfErrorOk)
-                    {
-                        registrationAlgorithm.clean();
-                    }
-                } else if (action == UnstoreDataAction.DELETE)
-                {
-                    FileUtilities.deleteRecursively(incomingDataSetFile, new Log4jSimpleLogger(
-                            TransferredDataSetHandler.operationLog));
-                }
-            }
+
+            new DataSetRegistrationRollbacker(stopped, registrationAlgorithm, incomingDataSetFile,
+                    notificationLog, operationLog, throwable).doRollback();
         }
     }
 
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/AbstractOmniscientTopLevelDataSetRegistrator.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/AbstractOmniscientTopLevelDataSetRegistrator.java
new file mode 100644
index 0000000000000000000000000000000000000000..dbf96adc5b554f36f43008a470adaee80ea66fd9
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/AbstractOmniscientTopLevelDataSetRegistrator.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2011 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.etlserver.registrator;
+
+import static ch.systemsx.cisd.etlserver.IStorageProcessor.STORAGE_PROCESSOR_KEY;
+
+import java.io.File;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.log4j.Logger;
+
+import ch.systemsx.cisd.base.exceptions.InterruptedExceptionUnchecked;
+import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
+import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
+import ch.systemsx.cisd.common.filesystem.FileOperations;
+import ch.systemsx.cisd.common.filesystem.IFileOperations;
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogFactory;
+import ch.systemsx.cisd.common.utilities.IDelegatedActionWithResult;
+import ch.systemsx.cisd.etlserver.AbstractTopLevelDataSetRegistrator;
+import ch.systemsx.cisd.etlserver.DataSetRegistrationAlgorithm;
+import ch.systemsx.cisd.etlserver.DataSetRegistrationAlgorithm.IRollbackDelegate;
+import ch.systemsx.cisd.etlserver.DataSetRegistrationRollbacker;
+import ch.systemsx.cisd.etlserver.DataStrategyStore;
+import ch.systemsx.cisd.etlserver.IDataStrategyStore;
+import ch.systemsx.cisd.etlserver.IPostRegistrationAction;
+import ch.systemsx.cisd.etlserver.IPreRegistrationAction;
+import ch.systemsx.cisd.etlserver.IStorageProcessor;
+import ch.systemsx.cisd.etlserver.PropertiesBasedETLServerPlugin;
+import ch.systemsx.cisd.etlserver.TopLevelDataSetRegistratorGlobalState;
+import ch.systemsx.cisd.etlserver.utils.PostRegistrationExecutor;
+import ch.systemsx.cisd.etlserver.utils.PreRegistrationExecutor;
+import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation;
+import ch.systemsx.cisd.openbis.generic.shared.dto.NewExternalData;
+
+/**
+ * @author Chandrasekhar Ramakrishnan
+ */
+public abstract class AbstractOmniscientTopLevelDataSetRegistrator extends
+        AbstractTopLevelDataSetRegistrator implements IRollbackDelegate
+{
+    static final Logger notificationLog = LogFactory.getLogger(LogCategory.NOTIFY,
+            AbstractOmniscientTopLevelDataSetRegistrator.class);
+
+    static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION,
+            AbstractOmniscientTopLevelDataSetRegistrator.class);
+
+    protected static class OmniscientTopLevelDataSetRegistratorState
+    {
+        private final TopLevelDataSetRegistratorGlobalState globalState;
+
+        private final IStorageProcessor storageProcessor;
+
+        private final ReentrantLock registrationLock;
+
+        private final IFileOperations fileOperations;
+
+        private final IPreRegistrationAction preRegistrationAction;
+
+        private final IPostRegistrationAction postRegistrationAction;
+
+        private final IDataStrategyStore dataStrategyStore;
+
+        private final MarkerFileUtility markerFileUtility;
+
+        private OmniscientTopLevelDataSetRegistratorState(
+                TopLevelDataSetRegistratorGlobalState globalState,
+                IStorageProcessor storageProcessor, ReentrantLock registrationLock,
+                IFileOperations fileOperations)
+        {
+            this.globalState = globalState;
+            this.storageProcessor = storageProcessor;
+            this.registrationLock = registrationLock;
+            this.fileOperations = fileOperations;
+            this.dataStrategyStore =
+                    new DataStrategyStore(globalState.getOpenBisService(),
+                            globalState.getMailClient());
+            this.preRegistrationAction =
+                    PreRegistrationExecutor.create(globalState.getPreRegistrationScriptOrNull());
+            this.postRegistrationAction =
+                    PostRegistrationExecutor.create(globalState.getPostRegistrationScriptOrNull());
+            this.markerFileUtility =
+                    new MarkerFileUtility(operationLog, notificationLog, fileOperations,
+                            storageProcessor);
+        }
+
+        public TopLevelDataSetRegistratorGlobalState getGlobalState()
+        {
+            return globalState;
+        }
+
+        public IStorageProcessor getStorageProcessor()
+        {
+            return storageProcessor;
+        }
+
+        public ReentrantLock getRegistrationLock()
+        {
+            return registrationLock;
+        }
+
+        public IFileOperations getFileOperations()
+        {
+            return fileOperations;
+        }
+
+        public IPreRegistrationAction getPreRegistrationAction()
+        {
+            return preRegistrationAction;
+        }
+
+        public IPostRegistrationAction getPostRegistrationAction()
+        {
+            return postRegistrationAction;
+        }
+
+        public IDataStrategyStore getDataStrategyStore()
+        {
+            return dataStrategyStore;
+        }
+
+        public MarkerFileUtility getMarkerFileUtility()
+        {
+            return markerFileUtility;
+        }
+    }
+
+    private final OmniscientTopLevelDataSetRegistratorState state;
+
+    private boolean stopped;
+
+    /**
+     * @param globalState
+     */
+    protected AbstractOmniscientTopLevelDataSetRegistrator(
+            TopLevelDataSetRegistratorGlobalState globalState)
+    {
+        super(globalState);
+
+        state =
+                new OmniscientTopLevelDataSetRegistratorState(globalState,
+                        PropertiesBasedETLServerPlugin.create(IStorageProcessor.class, globalState
+                                .getThreadParameters().getThreadProperties(),
+                                STORAGE_PROCESSOR_KEY, true), new ReentrantLock(),
+                        FileOperations.getMonitoredInstanceForCurrentThread());
+
+    }
+
+    public OmniscientTopLevelDataSetRegistratorState getRegistratorState()
+    {
+        return state;
+    }
+
+    public Lock getRegistrationLock()
+    {
+        return state.registrationLock;
+    }
+
+    public void handle(File incomingDataSetFileOrIsFinishedFile)
+    {
+        if (stopped)
+        {
+            return;
+        }
+        final File isFinishedFile = incomingDataSetFileOrIsFinishedFile;
+        final File incomingDataSetFile;
+        final IDelegatedActionWithResult<Boolean> cleanAfterwardsAction;
+
+        if (getGlobalState().isUseIsFinishedMarkerFile())
+        {
+            incomingDataSetFile =
+                    state.getMarkerFileUtility().getIncomingDataSetPathFromMarker(isFinishedFile);
+            cleanAfterwardsAction = new IDelegatedActionWithResult<Boolean>()
+                {
+                    public Boolean execute()
+                    {
+                        return state.getMarkerFileUtility().deleteAndLogIsFinishedMarkerFile(
+                                isFinishedFile);
+                    }
+                };
+        } else
+        {
+            incomingDataSetFile = incomingDataSetFileOrIsFinishedFile;
+            cleanAfterwardsAction = new IDelegatedActionWithResult<Boolean>()
+                {
+                    public Boolean execute()
+                    {
+                        return true; // do nothing
+                    }
+                };
+        }
+
+        DataSetRegistrationService service =
+                new DataSetRegistrationService(this, cleanAfterwardsAction);
+        handleDataSet(incomingDataSetFile, service);
+    }
+
+    public boolean isStopped()
+    {
+        return stopped;
+    }
+
+    public boolean isRemote()
+    {
+        return true;
+    }
+
+    //
+    // ISelfTestable
+    //
+    public final void check() throws ConfigurationFailureException, EnvironmentFailureException
+    {
+        new TopLevelDataSetChecker(operationLog, state.storageProcessor, state.fileOperations)
+                .runCheck();
+    }
+
+    /**
+     * For subclasses to override.
+     */
+    public void rollback(DataSetRegistrationAlgorithm registrationAlgorithm, Throwable throwable)
+    {
+        updateStopped(throwable instanceof InterruptedExceptionUnchecked);
+
+        new DataSetRegistrationRollbacker(stopped, registrationAlgorithm,
+                registrationAlgorithm.getIncomingDataSetFile(), notificationLog, operationLog,
+                throwable).doRollback();
+    }
+
+    public void registerDataSetInApplicationServer(DataSetInformation dataSetInformation,
+            NewExternalData data) throws Throwable
+    {
+        getGlobalState().getOpenBisService().registerDataSet(dataSetInformation, data);
+    }
+
+    /**
+     * Update the value of stopped using the argument.
+     * <p>
+     * To be called by subclasses.
+     */
+    protected void updateStopped(boolean update)
+    {
+        stopped |= update;
+    }
+
+    /**
+     * For subclasses to override.
+     */
+    protected abstract void handleDataSet(File dataSetFile, DataSetRegistrationService service);
+}
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/DataSetRegistrationDetails.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/DataSetRegistrationDetails.java
new file mode 100644
index 0000000000000000000000000000000000000000..045830a6f214f02a817a94f1d852cef6aae3f5bb
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/DataSetRegistrationDetails.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2011 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.etlserver.registrator;
+
+import java.io.File;
+
+import ch.systemsx.cisd.etlserver.ITypeExtractor;
+import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.FileFormatType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.LocatorType;
+
+/**
+ * @author Chandrasekhar Ramakrishnan
+ */
+public class DataSetRegistrationDetails implements ITypeExtractor
+{
+    private FileFormatType fileFormatType;
+
+    private boolean measuredData;
+
+    private String processorType;
+
+    private DataSetType dataSetType;
+
+    private LocatorType locatorType;
+
+    private DataSetInformation dataSetInformation;
+
+    public FileFormatType getFileFormatType(File incomingDataSetPath)
+    {
+        return fileFormatType;
+    }
+
+    public boolean isMeasuredData(File incomingDataSetPath)
+    {
+        return measuredData;
+    }
+
+    public String getProcessorType(File incomingDataSetPath)
+    {
+        return processorType;
+    }
+
+    public DataSetType getDataSetType(File incomingDataSetPath)
+    {
+        return dataSetType;
+    }
+
+    public LocatorType getLocatorType(File incomingDataSetPath)
+    {
+        return locatorType;
+    }
+
+    public FileFormatType getFileFormatType()
+    {
+        return fileFormatType;
+    }
+
+    public void setFileFormatType(FileFormatType fileFormatType)
+    {
+        this.fileFormatType = fileFormatType;
+    }
+
+    public boolean isMeasuredData()
+    {
+        return measuredData;
+    }
+
+    public void setMeasuredData(boolean measuredData)
+    {
+        this.measuredData = measuredData;
+    }
+
+    public String getProcessorType()
+    {
+        return processorType;
+    }
+
+    public void setProcessorType(String processorType)
+    {
+        this.processorType = processorType;
+    }
+
+    public DataSetType getDataSetType()
+    {
+        return dataSetType;
+    }
+
+    public void setDataSetType(DataSetType dataSetType)
+    {
+        this.dataSetType = dataSetType;
+    }
+
+    public LocatorType getLocatorType()
+    {
+        return locatorType;
+    }
+
+    public void setLocatorType(LocatorType locatorType)
+    {
+        this.locatorType = locatorType;
+    }
+
+    public DataSetInformation getDataSetInformation()
+    {
+        return dataSetInformation;
+    }
+
+    public void setDataSetInformation(DataSetInformation dataSetInformation)
+    {
+        this.dataSetInformation = dataSetInformation;
+    }
+}
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/DataSetRegistrationService.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/DataSetRegistrationService.java
new file mode 100644
index 0000000000000000000000000000000000000000..d3a460abee489e20308e19d5056bc814e86f6cec
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/DataSetRegistrationService.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2011 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.etlserver.registrator;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import ch.systemsx.cisd.common.utilities.IDelegatedActionWithResult;
+import ch.systemsx.cisd.etlserver.DataSetRegistrationAlgorithm;
+import ch.systemsx.cisd.etlserver.DataSetRegistrationAlgorithm.DataSetRegistrationAlgorithmState;
+import ch.systemsx.cisd.etlserver.DataSetRegistrationAlgorithm.IDataSetInApplicationServerRegistrator;
+import ch.systemsx.cisd.etlserver.DataSetRegistrationAlgorithm.IRollbackDelegate;
+import ch.systemsx.cisd.etlserver.IDataStoreStrategy;
+import ch.systemsx.cisd.etlserver.TopLevelDataSetRegistratorGlobalState;
+import ch.systemsx.cisd.etlserver.registrator.AbstractOmniscientTopLevelDataSetRegistrator.OmniscientTopLevelDataSetRegistratorState;
+import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation;
+import ch.systemsx.cisd.openbis.generic.shared.dto.NewExternalData;
+
+/**
+ * A service that registers many files as individual data sets in one transaction.
+ * 
+ * @author Chandrasekhar Ramakrishnan
+ */
+public class DataSetRegistrationService implements IRollbackDelegate
+{
+    private final AbstractOmniscientTopLevelDataSetRegistrator registrator;
+
+    private final OmniscientTopLevelDataSetRegistratorState registratorState;
+
+    private final IDelegatedActionWithResult<Boolean> globalCleanAfterwardsAction;
+
+    private final ArrayList<DataSetRegistrationAlgorithm> dataSetRegistrations =
+            new ArrayList<DataSetRegistrationAlgorithm>();
+
+    /**
+     * A data set that will be created but might not yet exist.
+     * 
+     * @author Chandrasekhar Ramakrishnan
+     */
+    public class FutureDataSet
+    {
+        private final String code;
+
+        public FutureDataSet(String code)
+        {
+            this.code = code;
+        }
+
+        public String getCode()
+        {
+            return code;
+        }
+    }
+
+    public DataSetRegistrationService(AbstractOmniscientTopLevelDataSetRegistrator registrator,
+            IDelegatedActionWithResult<Boolean> globalCleanAfterwardsAction)
+    {
+        this.registrator = registrator;
+        this.registratorState = registrator.getRegistratorState();
+        this.globalCleanAfterwardsAction = globalCleanAfterwardsAction;
+    }
+
+    /**
+     * Factory method that creates a new registration details object.
+     */
+    public DataSetRegistrationDetails createRegistrationDetails()
+    {
+        return new DataSetRegistrationDetails();
+    }
+
+    /**
+     * Factory method that creates a new data set information object.
+     */
+    public DataSetInformation createDataSetInformation()
+    {
+        return new DataSetInformation();
+    }
+
+    /**
+     * Queue registration a data set and return a future for the data set that will be created.
+     */
+    public FutureDataSet queueDataSetRegistration(File dataSetFile,
+            DataSetRegistrationDetails details)
+    {
+        DataSetRegistrationAlgorithm registration =
+                createRegistrationAlgorithm(dataSetFile, details);
+        dataSetRegistrations.add(registration);
+
+        FutureDataSet future =
+                new FutureDataSet(registration.getDataSetInformation().getDataSetCode());
+        return future;
+    }
+
+    public void commit()
+    {
+
+        globalCleanAfterwardsAction.execute();
+    }
+
+    public void abort()
+    {
+
+    }
+
+    private DataSetRegistrationAlgorithm createRegistrationAlgorithm(File incomingDataSetFile,
+            DataSetRegistrationDetails details)
+    {
+        final TopLevelDataSetRegistratorGlobalState globalState = registratorState.getGlobalState();
+        final IDelegatedActionWithResult<Boolean> cleanAfterwardsAction =
+                new IDelegatedActionWithResult<Boolean>()
+                    {
+                        public Boolean execute()
+                        {
+                            return true; // do nothing
+                        }
+                    };
+
+        IDataStoreStrategy dataStoreStrategy =
+                registratorState.getDataStrategyStore().getDataStoreStrategy(
+                        details.getDataSetInformation(), incomingDataSetFile);
+
+        DataSetRegistrationAlgorithmState state =
+                new DataSetRegistrationAlgorithmState(incomingDataSetFile,
+                        globalState.getOpenBisService(), cleanAfterwardsAction,
+                        registratorState.getPreRegistrationAction(),
+                        registratorState.getPostRegistrationAction(),
+                        details.getDataSetInformation(), dataStoreStrategy, details,
+                        registratorState.getStorageProcessor(),
+                        registratorState.getFileOperations(), globalState.getDataSetValidator(),
+                        globalState.getMailClient(), globalState.isDeleteUnidentified(),
+                        registratorState.getRegistrationLock(), globalState.getDssCode(),
+                        globalState.isNotifySuccessfulRegistration());
+        return new DataSetRegistrationAlgorithm(state, this,
+                new DefaultApplicationServerRegistrator(registrator,
+                        details.getDataSetInformation()));
+    }
+
+    public void rollback(DataSetRegistrationAlgorithm algorithm, Throwable ex)
+    {
+        registrator.rollback(algorithm, ex);
+    }
+
+    private static class DefaultApplicationServerRegistrator implements
+            IDataSetInApplicationServerRegistrator
+    {
+        private final AbstractOmniscientTopLevelDataSetRegistrator registrator;
+
+        private final DataSetInformation dataSetInformation;
+
+        DefaultApplicationServerRegistrator(AbstractOmniscientTopLevelDataSetRegistrator registrator,
+                DataSetInformation dataSetInformation)
+        {
+            this.dataSetInformation = dataSetInformation;
+            this.registrator = registrator;
+        }
+
+        public void registerDataSetInApplicationServer(NewExternalData data) throws Throwable
+        {
+            registrator.registerDataSetInApplicationServer(dataSetInformation, data);
+        }
+
+    }
+}
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/MarkerFileUtility.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/MarkerFileUtility.java
new file mode 100644
index 0000000000000000000000000000000000000000..4761e4390c47021dc2ba4bdf7f573203c2031081
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/MarkerFileUtility.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2011 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.etlserver.registrator;
+
+import static ch.systemsx.cisd.common.Constants.IS_FINISHED_PREFIX;
+
+import java.io.File;
+
+import org.apache.log4j.Logger;
+
+import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
+import ch.systemsx.cisd.common.filesystem.FileUtilities;
+import ch.systemsx.cisd.common.filesystem.IFileOperations;
+import ch.systemsx.cisd.etlserver.IStoreRootDirectoryHolder;
+
+/**
+ * @author Chandrasekhar Ramakrishnan
+ */
+public class MarkerFileUtility
+{
+    private final Logger operationLog;
+
+    private final Logger notificationLog;
+
+    private final IFileOperations fileOperations;
+
+    private final IStoreRootDirectoryHolder storeRootDirectoryHolder;
+
+    public MarkerFileUtility(Logger operationLog, Logger notificationLog,
+            IFileOperations fileOperations, IStoreRootDirectoryHolder storeRootDirectoryHolder)
+    {
+        this.operationLog = operationLog;
+        this.notificationLog = notificationLog;
+        this.fileOperations = fileOperations;
+        this.storeRootDirectoryHolder = storeRootDirectoryHolder;
+    }
+
+    /**
+     * From given <var>isFinishedPath</var> gets the incoming data set path and checks it.
+     * 
+     * @return <code>null</code> if a problem has happened. Otherwise a useful and usable incoming
+     *         data set path is returned.
+     */
+    public final File getIncomingDataSetPathFromMarker(final File isFinishedPath)
+    {
+        final File incomingDataSetPath =
+                FileUtilities.removePrefixFromFileName(isFinishedPath, IS_FINISHED_PREFIX);
+        if (operationLog.isDebugEnabled())
+        {
+            operationLog.debug(String.format(
+                    "Getting incoming data set path '%s' from is-finished path '%s'",
+                    incomingDataSetPath, isFinishedPath));
+        }
+        final String errorMsg =
+                fileOperations.checkPathFullyAccessible(incomingDataSetPath, "incoming data set");
+        if (errorMsg != null)
+        {
+            fileOperations.delete(isFinishedPath);
+            throw EnvironmentFailureException.fromTemplate(String.format(
+                    "Error moving path '%s' from '%s' to '%s': %s", incomingDataSetPath.getName(),
+                    incomingDataSetPath.getParent(),
+                    storeRootDirectoryHolder.getStoreRootDirectory(), errorMsg));
+        }
+        return incomingDataSetPath;
+    }
+
+    public boolean deleteAndLogIsFinishedMarkerFile(File isFinishedFile)
+    {
+        if (fileOperations.exists(isFinishedFile) == false)
+        {
+            return false;
+        }
+        final boolean ok = fileOperations.delete(isFinishedFile);
+        final String absolutePath = isFinishedFile.getAbsolutePath();
+        if (ok == false)
+        {
+            notificationLog.error(String.format("Removing file '%s' failed.", absolutePath));
+        } else
+        {
+            if (operationLog.isDebugEnabled())
+            {
+                operationLog.debug(String.format("File '%s' has been removed.", absolutePath));
+            }
+        }
+        return ok;
+    }
+}
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/TopLevelDataSetChecker.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/TopLevelDataSetChecker.java
new file mode 100644
index 0000000000000000000000000000000000000000..2c9059e65a55451299e05adbb858391371d95ad7
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/registrator/TopLevelDataSetChecker.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2011 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.etlserver.registrator;
+
+import java.io.File;
+
+import org.apache.log4j.Logger;
+
+import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
+import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
+import ch.systemsx.cisd.common.filesystem.IFileOperations;
+import ch.systemsx.cisd.etlserver.IStoreRootDirectoryHolder;
+
+/**
+ * @author Chandrasekhar Ramakrishnan
+ */
+public class TopLevelDataSetChecker
+{
+    private final Logger operationLog;
+
+    private final IStoreRootDirectoryHolder storeRootDirectoryHolder;
+
+    private final IFileOperations fileOperations;
+
+    public TopLevelDataSetChecker(Logger operationLog,
+            IStoreRootDirectoryHolder storeRootDirectoryHolder, IFileOperations fileOperations)
+    {
+        this.operationLog = operationLog;
+        this.storeRootDirectoryHolder = storeRootDirectoryHolder;
+        this.fileOperations = fileOperations;
+    }
+
+    public void runCheck()
+    {
+        
+        final File storeRootDirectory = storeRootDirectoryHolder.getStoreRootDirectory();
+        storeRootDirectory.mkdirs();
+        if (operationLog.isDebugEnabled())
+        {
+            operationLog.debug("Checking store root directory '"
+                    + storeRootDirectory.getAbsolutePath() + "'.");
+        }
+        final String errorMessage =
+                fileOperations.checkDirectoryFullyAccessible(storeRootDirectory, "store root");
+        if (errorMessage != null)
+        {
+            if (fileOperations.exists(storeRootDirectory) == false)
+            {
+                throw EnvironmentFailureException.fromTemplate(
+                        "Store root directory '%s' does not exist.",
+                        storeRootDirectory.getAbsolutePath());
+            } else
+            {
+                throw new ConfigurationFailureException(errorMessage);
+            }
+        }
+    }
+}
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/DataSetRegistrationAlgorithmTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/DataSetRegistrationAlgorithmTest.java
index 99f2c62ad3c33629d4b75f19b57c3e903b16ad5b..c2c1ed66a764274a7a1c7831a13a1d568dcc4994 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/DataSetRegistrationAlgorithmTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/DataSetRegistrationAlgorithmTest.java
@@ -84,7 +84,7 @@ public class DataSetRegistrationAlgorithmTest extends AbstractFileSystemTestCase
 
     private IDataSetInApplicationServerRegistrator appServerRegistrator;
 
-    private IDelegatedActionWithResult<Boolean> cleanAftrewardsAction;
+    private IDelegatedActionWithResult<Boolean> cleanAfterwardsAction;
 
     private IPreRegistrationAction preRegistrationAction;
 
@@ -122,7 +122,7 @@ public class DataSetRegistrationAlgorithmTest extends AbstractFileSystemTestCase
         openBisService = context.mock(IEncapsulatedOpenBISService.class);
         rollbackDelegate = context.mock(IRollbackDelegate.class);
         appServerRegistrator = context.mock(IDataSetInApplicationServerRegistrator.class);
-        cleanAftrewardsAction = context.mock(IDelegatedActionWithResult.class);
+        cleanAfterwardsAction = context.mock(IDelegatedActionWithResult.class);
         preRegistrationAction = context.mock(IPreRegistrationAction.class);
         postRegistrationAction = context.mock(IPostRegistrationAction.class);
 
@@ -437,7 +437,7 @@ public class DataSetRegistrationAlgorithmTest extends AbstractFileSystemTestCase
         context.checking(new Expectations()
             {
                 {
-                    exactly(2).of(cleanAftrewardsAction).execute();
+                    exactly(2).of(cleanAfterwardsAction).execute();
                     will(returnValue(Boolean.TRUE));
                 }
             });
@@ -457,11 +457,10 @@ public class DataSetRegistrationAlgorithmTest extends AbstractFileSystemTestCase
     private void createAlgorithmAndState(boolean shouldDeleteUnidentified,
             boolean shouldNotifySuccessfulRegistration)
     {
-
         String dataStoreCode = DATA_STORE_CODE;
         DataSetRegistrationAlgorithmState state =
                 new DataSetRegistrationAlgorithmState(incomingDataSetFile, openBisService,
-                        cleanAftrewardsAction, preRegistrationAction, postRegistrationAction,
+                        cleanAfterwardsAction, preRegistrationAction, postRegistrationAction,
                         dataSetInformation, dataStoreStrategy, typeExtractor, storageProcessor,
                         fileOperations, dataSetValidator, mailClient, shouldDeleteUnidentified,
                         registrationLock, dataStoreCode, shouldNotifySuccessfulRegistration);