From aadd3c4b79b5fc9f043229e2a525f864c35e2ba9 Mon Sep 17 00:00:00 2001
From: cramakri <cramakri>
Date: Tue, 11 Jan 2011 15:38:50 +0000
Subject: [PATCH] LMS-1966 Refactoring DSS to make alternate top-level data set
 handlers possible.

SVN: 19365
---
 .../DataSetRegistrationAlgorithm.java         | 647 ++++++++++++++++++
 .../etlserver/TransferredDataSetHandler.java  |  39 +-
 ...etHandlerDataSetRegistrationAlgorithm.java | 365 +---------
 .../etlserver/api/v1/PutDataSetExecutor.java  |   8 +-
 4 files changed, 700 insertions(+), 359 deletions(-)
 create mode 100644 datastore_server/source/java/ch/systemsx/cisd/etlserver/DataSetRegistrationAlgorithm.java

diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataSetRegistrationAlgorithm.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataSetRegistrationAlgorithm.java
new file mode 100644
index 00000000000..2351fd7d447
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/DataSetRegistrationAlgorithm.java
@@ -0,0 +1,647 @@
+/*
+ * 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.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.time.StopWatch;
+import org.apache.log4j.Logger;
+
+import ch.systemsx.cisd.base.exceptions.IOExceptionUnchecked;
+import ch.systemsx.cisd.common.Constants;
+import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
+import ch.systemsx.cisd.common.exceptions.HighLevelException;
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.common.filesystem.FileUtilities;
+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.mail.IMailClient;
+import ch.systemsx.cisd.common.types.BooleanOrUnknown;
+import ch.systemsx.cisd.common.utilities.IDelegatedActionWithResult;
+import ch.systemsx.cisd.etlserver.IStorageProcessor.UnstoreDataAction;
+import ch.systemsx.cisd.etlserver.validation.IDataSetValidator;
+import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
+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.Experiment;
+import ch.systemsx.cisd.openbis.generic.shared.dto.NewExternalData;
+import ch.systemsx.cisd.openbis.generic.shared.dto.StorageFormat;
+import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
+
+/**
+ * An algorithm that implements the logic for registration of a data set.
+ * 
+ * @author Chandrasekhar Ramakrishnan
+ */
+public class DataSetRegistrationAlgorithm
+{
+
+    public static interface IRollbackDelegate
+    {
+        public void rollback(DataSetRegistrationAlgorithm algorithm, Throwable ex);
+    }
+
+    /**
+     * Object for holding the state necessary for registring a data set.
+     * 
+     * @author Chandrasekhar Ramakrishnan
+     */
+    public static class DataSetRegistrationAlgorithmState
+    {
+        private final File incomingDataSetFile;
+
+        private final IEncapsulatedOpenBISService openBisService;
+
+        private final IDelegatedActionWithResult<Boolean> cleanAfterwardsAction;
+
+        private final IPreRegistrationAction preRegistrationAction;
+
+        private final IPostRegistrationAction postRegistrationAction;
+
+        private final DataSetInformation dataSetInformation;
+
+        private final IDataStoreStrategy dataStoreStrategy;
+
+        private final ITypeExtractor typeExtractor;
+
+        private final IStorageProcessor storageProcessor;
+
+        private final IFileOperations fileOperations;
+
+        private final IDataSetValidator dataSetValidator;
+
+        private final IMailClient mailClient;
+
+        private final boolean shouldDeleteUnidentified;
+
+        private final String dataStoreCode;
+
+        private final Lock registrationLock;
+
+        private final boolean shouldNotifySuccessfulRegistration;
+
+        private final DataSetType dataSetType;
+
+        private final File storeRoot;
+
+        private final String defaultErrorMessageTemplate;
+
+        private final String emailSubjectTemplate;
+
+        public DataSetRegistrationAlgorithmState(File incomingDataSetFile,
+                IEncapsulatedOpenBISService openBisService,
+                IDelegatedActionWithResult<Boolean> cleanAftrewardsAction,
+                IPreRegistrationAction preRegistrationAction,
+                IPostRegistrationAction postRegistrationAction,
+                DataSetInformation dataSetInformation, IDataStoreStrategy dataStoreStrategy,
+                ITypeExtractor typeExtractor, IStorageProcessor storageProcessor,
+                IFileOperations fileOperations, IDataSetValidator dataSetValidator,
+                IMailClient mailClient, boolean shouldDeleteUnidentified, Lock registrationLock,
+                String dataStoreCode, boolean shouldNotifySuccessfulRegistration)
+        {
+            this.incomingDataSetFile = incomingDataSetFile;
+            this.openBisService = openBisService;
+            this.cleanAfterwardsAction = cleanAftrewardsAction;
+            this.preRegistrationAction = preRegistrationAction;
+            this.postRegistrationAction = postRegistrationAction;
+            this.dataSetInformation = dataSetInformation;
+            this.dataStoreStrategy = dataStoreStrategy;
+            this.typeExtractor = typeExtractor;
+            this.storageProcessor = storageProcessor;
+            this.fileOperations = fileOperations;
+            this.dataSetValidator = dataSetValidator;
+            this.mailClient = mailClient;
+            this.shouldDeleteUnidentified = shouldDeleteUnidentified;
+            this.registrationLock = registrationLock;
+            this.dataStoreCode = dataStoreCode;
+            this.shouldNotifySuccessfulRegistration = shouldNotifySuccessfulRegistration;
+
+            if (dataSetInformation.getDataSetCode() == null)
+            {
+                // Extractor didn't extract an externally generated data set code, so request one
+                // from the openBIS server.
+                dataSetInformation.setDataSetCode(openBisService.createDataSetCode());
+            }
+
+            this.dataSetType = typeExtractor.getDataSetType(incomingDataSetFile);
+            dataSetInformation.setDataSetType(dataSetType);
+            this.storeRoot = storageProcessor.getStoreRootDirectory();
+            this.defaultErrorMessageTemplate = DATA_SET_STORAGE_FAILURE_TEMPLATE;
+            this.emailSubjectTemplate = EMAIL_SUBJECT_TEMPLATE;
+        }
+
+        public String getErrorMessageTemplate()
+        {
+            return defaultErrorMessageTemplate;
+        }
+
+        public File getIncomingDataSetFile()
+        {
+            return incomingDataSetFile;
+        }
+
+        public IEncapsulatedOpenBISService getOpenBisService()
+        {
+            return openBisService;
+        }
+
+        public IDelegatedActionWithResult<Boolean> getCleanAftrewardsAction()
+        {
+            return cleanAfterwardsAction;
+        }
+
+        public IPreRegistrationAction getPreRegistrationAction()
+        {
+            return preRegistrationAction;
+        }
+
+        public IPostRegistrationAction getPostRegistrationAction()
+        {
+            return postRegistrationAction;
+        }
+
+        public DataSetInformation getDataSetInformation()
+        {
+            return dataSetInformation;
+        }
+
+        public IDataStoreStrategy getDataStoreStrategy()
+        {
+            return dataStoreStrategy;
+        }
+
+        public ITypeExtractor getTypeExtractor()
+        {
+            return typeExtractor;
+        }
+
+        public IStorageProcessor getStorageProcessor()
+        {
+            return storageProcessor;
+        }
+
+        public DataSetType getDataSetType()
+        {
+            return dataSetType;
+        }
+
+        public File getStoreRoot()
+        {
+            return storeRoot;
+        }
+
+        public IMailClient getMailClient()
+        {
+            return mailClient;
+        }
+
+        public Lock getRegistrationLock()
+        {
+            return registrationLock;
+        }
+    }
+
+    static private final Logger notificationLog = LogFactory.getLogger(LogCategory.NOTIFY,
+            DataSetRegistrationAlgorithm.class);
+
+    static private final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION,
+            DataSetRegistrationAlgorithm.class);
+
+    public static final String EMAIL_SUBJECT_TEMPLATE = "Success: data set for experiment '%s";
+
+    public static final String DATA_SET_REGISTRATION_FAILURE_TEMPLATE =
+            "Registration of data set '%s' failed.";
+
+    public static final String DATA_SET_STORAGE_FAILURE_TEMPLATE = "Storing data set '%s' failed.";
+
+    public static final String SUCCESSFULLY_REGISTERED = "Successfully registered data set: [";
+
+    // Immutable State
+    private final DataSetRegistrationAlgorithmState state;
+
+    private final IRollbackDelegate rollbackDelegate;
+
+    private final File incomingDataSetFile;
+
+    private final DataSetInformation dataSetInformation;
+
+    // State that changes during execution
+    private BaseDirectoryHolder baseDirectoryHolder;
+
+    private String errorMessageTemplate;
+
+    public DataSetRegistrationAlgorithm(DataSetRegistrationAlgorithmState state,
+            IRollbackDelegate rollbackDelegate)
+    {
+        this.state = state;
+        this.rollbackDelegate = rollbackDelegate;
+        incomingDataSetFile = state.incomingDataSetFile;
+        dataSetInformation = state.dataSetInformation;
+        errorMessageTemplate = state.getErrorMessageTemplate();
+    }
+
+    /**
+     * Prepare registration of a data set.
+     */
+    public final void prepare()
+    {
+        final File baseDirectory =
+                createBaseDirectory(getDataStoreStrategy(), state.storeRoot,
+                        state.dataSetInformation);
+        baseDirectoryHolder =
+                new BaseDirectoryHolder(getDataStoreStrategy(), baseDirectory,
+                        state.incomingDataSetFile);
+    }
+
+    public boolean hasDataSetBeenIdentified()
+    {
+        return state.dataStoreStrategy.getKey() == DataStoreStrategyKey.IDENTIFIED;
+    }
+
+    /**
+     * Register the data set. This method is only ever called for identified data sets.
+     */
+    public final List<DataSetInformation> registerDataSet()
+    {
+        String processorID = getTypeExtractor().getProcessorType(incomingDataSetFile);
+        try
+        {
+            getDataSetValidator().assertValidDataSet(state.dataSetType, incomingDataSetFile);
+            registerDataSetAndInitiateProcessing(processorID);
+            logAndNotifySuccessfulRegistration(getEmail());
+            if (state.fileOperations.exists(incomingDataSetFile)
+                    && removeAndLog("clean up failed") == false)
+            {
+                getOperationLog().error(
+                        "Cannot delete '" + incomingDataSetFile.getAbsolutePath() + "'.");
+            }
+
+            clean();
+            return Collections.singletonList(dataSetInformation);
+        } catch (final HighLevelException ex)
+        {
+            final String userEmailOrNull = dataSetInformation.tryGetUploadingUserEmail();
+            boolean deleted = false;
+            if (userEmailOrNull != null)
+            {
+                final String errorMessage =
+                        "Error when trying to register data set '" + incomingDataSetFile.getName()
+                                + "'.";
+                state.getMailClient()
+                        .sendMessage(
+                                String.format(errorMessage, dataSetInformation
+                                        .getExperimentIdentifier().getExperimentCode()),
+                                ex.getMessage(), null, null, userEmailOrNull);
+                if (state.shouldDeleteUnidentified)
+                {
+                    deleted = removeAndLog(errorMessage + " [" + ex.getMessage() + "]");
+                }
+            }
+            if (deleted == false)
+            {
+                rollback(ex);
+            }
+            return Collections.emptyList();
+        } catch (final Throwable throwable)
+        {
+            rollback(throwable);
+            return Collections.emptyList();
+        }
+    }
+
+    public final File createBaseDirectory(final IDataStoreStrategy strategy, final File baseDir,
+            final DataSetInformation dataSetInfo)
+    {
+        final File baseDirectory =
+                strategy.getBaseDirectory(baseDir, dataSetInfo, state.dataSetType);
+        baseDirectory.mkdirs();
+        if (state.fileOperations.isDirectory(baseDirectory) == false)
+        {
+            throw EnvironmentFailureException.fromTemplate(
+                    "Creating data set base directory '%s' for data set '%s' failed.",
+                    baseDirectory.getAbsolutePath(), state.incomingDataSetFile);
+        }
+        return baseDirectory;
+    }
+
+    public DataSetInformation getDataSetInformation()
+    {
+        return dataSetInformation;
+    }
+
+    public BaseDirectoryHolder getBaseDirectoryHolder()
+    {
+        return baseDirectoryHolder;
+    }
+
+    public void setBaseDirectoryHolder(BaseDirectoryHolder baseDirectoryHolder)
+    {
+        this.baseDirectoryHolder = baseDirectoryHolder;
+    }
+
+    public String getErrorMessageTemplate()
+    {
+        return errorMessageTemplate;
+    }
+
+    public File getStoreRoot()
+    {
+        return state.getStoreRoot();
+    }
+
+    /**
+     * Ask the storage processor to rollback. Used by clients of the algorithm.
+     */
+    public UnstoreDataAction rollbackStorageProcessor(final Throwable throwable)
+    {
+        UnstoreDataAction action =
+                getStorageProcessor().rollback(incomingDataSetFile,
+                        baseDirectoryHolder.getBaseDirectory(), throwable);
+        return action;
+    }
+
+    protected boolean clean()
+    {
+        return state.cleanAfterwardsAction.execute();
+    }
+
+    /**
+     * Contact openBis and register the data set there. Subclasses may override.
+     * 
+     * @throws Throwable
+     */
+    protected void registerDataSetInApplicationServer(NewExternalData data) throws Throwable
+    {
+        state.getOpenBisService().registerDataSet(dataSetInformation, data);
+    }
+
+    private void rollback(Throwable ex)
+    {
+        rollbackDelegate.rollback(this, ex);
+    }
+
+    /**
+     * Registers the data set.
+     */
+    private void registerDataSetAndInitiateProcessing(final String procedureTypeCode)
+            throws Throwable
+    {
+        final File markerFile = createProcessingMarkerFile();
+        try
+        {
+            String entityDescription = createEntityDescription();
+            if (getOperationLog().isInfoEnabled())
+            {
+                getOperationLog().info("Start storing data set for " + entityDescription + ".");
+            }
+            final StopWatch watch = new StopWatch();
+            watch.start();
+            NewExternalData data = createExternalData();
+            state.preRegistrationAction.execute(data.getCode(),
+                    incomingDataSetFile.getAbsolutePath());
+            File dataFile =
+                    getStorageProcessor().storeData(dataSetInformation, getTypeExtractor(),
+                            state.getMailClient(), incomingDataSetFile,
+                            baseDirectoryHolder.getBaseDirectory());
+            if (getOperationLog().isInfoEnabled())
+            {
+                getOperationLog().info(
+                        "Finished storing data set for " + entityDescription + ", took " + watch);
+            }
+            assert dataFile != null : "The folder that contains the stored data should not be null.";
+            final String relativePath = FileUtilities.getRelativeFile(state.storeRoot, dataFile);
+            String absolutePath = dataFile.getAbsolutePath();
+            assert relativePath != null : String.format(
+                    TransferredDataSetHandler.TARGET_NOT_RELATIVE_TO_STORE_ROOT, absolutePath,
+                    state.storeRoot.getAbsolutePath());
+            final StorageFormat availableFormat = getStorageProcessor().getStorageFormat();
+            final BooleanOrUnknown isCompleteFlag = dataSetInformation.getIsCompleteFlag();
+            // Ensure that we either register the data set and initiate the processing copy or
+            // do none of both.
+            state.getRegistrationLock().lock();
+            try
+            {
+                errorMessageTemplate = DATA_SET_REGISTRATION_FAILURE_TEMPLATE;
+                plainRegisterDataSet(data, relativePath, availableFormat, isCompleteFlag);
+                state.postRegistrationAction.execute(data.getCode(), absolutePath);
+                clean();
+            } finally
+            {
+                state.getRegistrationLock().unlock();
+            }
+            getStorageProcessor().commit(incomingDataSetFile,
+                    baseDirectoryHolder.getBaseDirectory());
+        } finally
+        {
+            getFileOperations().delete(markerFile);
+        }
+    }
+
+    private NewExternalData createExternalData()
+    {
+        final NewExternalData data = new NewExternalData();
+        data.setUserId(dataSetInformation.getUploadingUserIdOrNull());
+        data.setUserEMail(dataSetInformation.tryGetUploadingUserEmail());
+        data.setExtractableData(dataSetInformation.getExtractableData());
+        data.setLocatorType(getTypeExtractor().getLocatorType(incomingDataSetFile));
+        data.setDataSetType(getTypeExtractor().getDataSetType(incomingDataSetFile));
+        data.setFileFormatType(getTypeExtractor().getFileFormatType(incomingDataSetFile));
+        data.setMeasured(getTypeExtractor().isMeasuredData(incomingDataSetFile));
+        data.setDataStoreCode(state.dataStoreCode);
+        return data;
+    }
+
+    private void logAndNotifySuccessfulRegistration(final String email)
+    {
+        String msg = null;
+        if (getOperationLog().isInfoEnabled())
+        {
+            msg = getSuccessRegistrationMessage();
+            getOperationLog().info(msg);
+        }
+        if (state.shouldNotifySuccessfulRegistration)
+        {
+            if (msg == null)
+            {
+                msg = getSuccessRegistrationMessage();
+            }
+            if (getNotificationLog().isInfoEnabled())
+            {
+                getNotificationLog().info(msg);
+            }
+            if (StringUtils.isBlank(email) == false)
+            {
+                state.getMailClient().sendMessage(
+                        String.format(state.emailSubjectTemplate, dataSetInformation
+                                .getExperimentIdentifier().getExperimentCode()), msg, null, null,
+                        email);
+            }
+        }
+    }
+
+    private final String getSuccessRegistrationMessage()
+    {
+        final StringBuilder buffer = new StringBuilder();
+        buffer.append(SUCCESSFULLY_REGISTERED);
+        String userID = dataSetInformation.getUploadingUserIdOrNull();
+        String userEMail = dataSetInformation.tryGetUploadingUserEmail();
+        if (userID != null || userEMail != null)
+        {
+            appendNameAndObject(buffer, "User", userID == null ? userEMail : userID);
+        }
+        appendNameAndObject(buffer, "Data Set Code", dataSetInformation.getDataSetCode());
+        appendNameAndObject(buffer, "Data Set Type", state.dataSetType.getCode());
+        appendNameAndObject(buffer, "Experiment Identifier",
+                dataSetInformation.getExperimentIdentifier());
+        appendNameAndObject(buffer, "Sample Identifier", dataSetInformation.getSampleIdentifier());
+        appendNameAndObject(buffer, "Producer Code", dataSetInformation.getProducerCode());
+        appendNameAndObject(buffer, "Production Date",
+                formatDate(dataSetInformation.getProductionDate()));
+        final List<String> parentDataSetCodes = dataSetInformation.getParentDataSetCodes();
+        if (parentDataSetCodes.isEmpty() == false)
+        {
+            appendNameAndObject(buffer, "Parent Data Sets",
+                    StringUtils.join(parentDataSetCodes, ' '));
+        }
+        appendNameAndObject(buffer, "Is complete", dataSetInformation.getIsCompleteFlag());
+        buffer.setLength(buffer.length() - 1);
+        buffer.append(']');
+        return buffer.toString();
+    }
+
+    private String formatDate(Date productionDate)
+    {
+        return productionDate == null ? "" : Constants.DATE_FORMAT.get().format(productionDate);
+    }
+
+    private final void appendNameAndObject(final StringBuilder buffer, final String name,
+            final Object object)
+    {
+        if (object != null)
+        {
+            buffer.append(name).append("::").append(object).append(";");
+        }
+    }
+
+    private String getEmail()
+    {
+        Experiment experiment = dataSetInformation.tryToGetExperiment();
+        if (experiment == null)
+        {
+            throw new UserFailureException("Unknown experiment of data set " + dataSetInformation);
+        }
+        return experiment.getRegistrator().getEmail();
+    }
+
+    private boolean removeAndLog(String msg)
+    {
+        final boolean ok = getFileOperations().removeRecursivelyQueueing(incomingDataSetFile);
+        if (getOperationLog().isInfoEnabled())
+        {
+            getOperationLog().info("Dataset deleted in registration: " + msg);
+        }
+        return ok;
+    }
+
+    private final void plainRegisterDataSet(NewExternalData data, final String relativePath,
+            final StorageFormat storageFormat, final BooleanOrUnknown isCompleteFlag)
+            throws Throwable
+    {
+        updateExternalData(data, relativePath, storageFormat, isCompleteFlag);
+        // Finally: register the data set in the database.
+        registerDataSetInApplicationServer(data);
+    }
+
+    private final NewExternalData updateExternalData(NewExternalData data,
+            final String relativePath, final StorageFormat storageFormat,
+            final BooleanOrUnknown isCompleteFlag)
+    {
+        data.setComplete(isCompleteFlag);
+        data.setLocation(relativePath);
+        data.setStorageFormat(storageFormat);
+        return data;
+    }
+
+    private final File createProcessingMarkerFile()
+    {
+        final File baseDirectory = baseDirectoryHolder.getBaseDirectory();
+        final File baseParentDirectory = baseDirectory.getParentFile();
+        final String processingDirName = baseDirectory.getName();
+        final File markerFile =
+                new File(baseParentDirectory, Constants.PROCESSING_PREFIX + processingDirName);
+        try
+        {
+            getFileOperations().createNewFile(markerFile);
+        } catch (final IOExceptionUnchecked ex)
+        {
+            throw EnvironmentFailureException.fromTemplate(ex, "Cannot create marker file '%s'.",
+                    markerFile.getPath());
+        }
+        return markerFile;
+    }
+
+    private String createEntityDescription()
+    {
+        SampleIdentifier sampleIdentifier = dataSetInformation.getSampleIdentifier();
+        if (sampleIdentifier != null)
+        {
+            return "sample '" + sampleIdentifier + "'";
+        }
+        return "experiment '" + dataSetInformation.getExperimentIdentifier() + "'";
+    }
+
+    private IFileOperations getFileOperations()
+    {
+        return state.fileOperations;
+    }
+
+    private IStorageProcessor getStorageProcessor()
+    {
+        return state.storageProcessor;
+    }
+
+    private IDataStoreStrategy getDataStoreStrategy()
+    {
+        return state.dataStoreStrategy;
+    }
+
+    private ITypeExtractor getTypeExtractor()
+    {
+        return state.typeExtractor;
+    }
+
+    private IDataSetValidator getDataSetValidator()
+    {
+        return state.dataSetValidator;
+    }
+
+    private Logger getNotificationLog()
+    {
+        return notificationLog;
+    }
+
+    private Logger getOperationLog()
+    {
+        return operationLog;
+    }
+}
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 d6d1d30f3f1..7c6e2a6d4fa 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/TransferredDataSetHandler.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/TransferredDataSetHandler.java
@@ -221,7 +221,8 @@ public final class TransferredDataSetHandler extends AbstractTopLevelDataSetRegi
 
     public List<DataSetInformation> handleDataSet(final File dataSet)
     {
-        final TransferredDataSetHandlerDataSetRegistrationAlgorithm registrationHelper = createRegistrationHelper(dataSet);
+        final TransferredDataSetHandlerDataSetRegistrationAlgorithm registrationHelper =
+                createRegistrationHelper(dataSet);
         return new DataSetRegistrationAlgorithmRunner(registrationHelper).runAlgorithm();
     }
 
@@ -298,8 +299,8 @@ public final class TransferredDataSetHandler extends AbstractTopLevelDataSetRegi
         }
     }
 
-    private TransferredDataSetHandlerDataSetRegistrationAlgorithm createRegistrationHelper(File dataSet,
-            DataSetInformation dataSetInformation, IDataSetRegistrator registrator)
+    private TransferredDataSetHandlerDataSetRegistrationAlgorithm createRegistrationHelper(
+            File dataSet, DataSetInformation dataSetInformation, IDataSetRegistrator registrator)
     {
         if (useIsFinishedMarkerFile)
         {
@@ -542,12 +543,6 @@ public final class TransferredDataSetHandler extends AbstractTopLevelDataSetRegi
             return notifySuccessfulRegistration;
         }
 
-        @Override
-        protected String getEmailSubjectTemplate()
-        {
-            return TransferredDataSetHandlerDataSetRegistrationAlgorithm.EMAIL_SUBJECT_TEMPLATE;
-        }
-
         @Override
         protected void rollback(final Throwable throwable) throws Error
         {
@@ -557,11 +552,12 @@ public final class TransferredDataSetHandler extends AbstractTopLevelDataSetRegi
                 Thread.interrupted(); // Ensure the thread's interrupted state is cleared.
                 getOperationLog().warn(
                         String.format("Requested to stop registration of data set '%s'",
-                                dataSetInformation));
+                                algorithm.getDataSetInformation()));
             } else
             {
-                getNotificationLog().error(String.format(errorMessageTemplate, dataSetInformation),
-                        throwable);
+                getNotificationLog().error(
+                        String.format(algorithm.getErrorMessageTemplate(),
+                                algorithm.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
@@ -570,24 +566,25 @@ public final class TransferredDataSetHandler extends AbstractTopLevelDataSetRegi
             {
                 throw (Error) throwable;
             }
-            UnstoreDataAction action = rollbackStorageProcessor(throwable);
+            UnstoreDataAction action = algorithm.rollbackStorageProcessor(throwable);
             if (stopped == false)
             {
                 if (action == UnstoreDataAction.MOVE_TO_ERROR)
                 {
                     final File baseDirectory =
-                            createBaseDirectory(TransferredDataSetHandler.ERROR_DATA_STRATEGY,
-                                    storeRoot, dataSetInformation);
-                    baseDirectoryHolder =
-                            new BaseDirectoryHolder(TransferredDataSetHandler.ERROR_DATA_STRATEGY,
-                                    baseDirectory, incomingDataSetFile);
+                            algorithm.createBaseDirectory(
+                                    TransferredDataSetHandler.ERROR_DATA_STRATEGY,
+                                    algorithm.getStoreRoot(), algorithm.getDataSetInformation());
+                    algorithm.setBaseDirectoryHolder(new BaseDirectoryHolder(
+                            TransferredDataSetHandler.ERROR_DATA_STRATEGY, baseDirectory,
+                            incomingDataSetFile));
                     boolean moveInCaseOfErrorOk =
-                            FileRenamer.renameAndLog(incomingDataSetFile,
-                                    baseDirectoryHolder.getTargetFile());
+                            FileRenamer.renameAndLog(incomingDataSetFile, algorithm
+                                    .getBaseDirectoryHolder().getTargetFile());
                     writeThrowable(throwable);
                     if (moveInCaseOfErrorOk)
                     {
-                        clean();
+                        algorithm.clean();
                     }
                 } else if (action == UnstoreDataAction.DELETE)
                 {
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/TransferredDataSetHandlerDataSetRegistrationAlgorithm.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/TransferredDataSetHandlerDataSetRegistrationAlgorithm.java
index 9d7e1beab61..af804280d97 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/TransferredDataSetHandlerDataSetRegistrationAlgorithm.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/TransferredDataSetHandlerDataSetRegistrationAlgorithm.java
@@ -20,96 +20,67 @@ import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.util.Collections;
-import java.util.Date;
 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.commons.lang.time.StopWatch;
 import org.apache.log4j.Logger;
 
 import ch.rinn.restrictions.Private;
-import ch.systemsx.cisd.base.exceptions.IOExceptionUnchecked;
-import ch.systemsx.cisd.common.Constants;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 import ch.systemsx.cisd.common.exceptions.HighLevelException;
-import ch.systemsx.cisd.common.exceptions.UserFailureException;
-import ch.systemsx.cisd.common.filesystem.FileUtilities;
 import ch.systemsx.cisd.common.filesystem.IFileOperations;
 import ch.systemsx.cisd.common.mail.IMailClient;
-import ch.systemsx.cisd.common.types.BooleanOrUnknown;
 import ch.systemsx.cisd.common.utilities.IDelegatedActionWithResult;
-import ch.systemsx.cisd.etlserver.IStorageProcessor.UnstoreDataAction;
+import ch.systemsx.cisd.etlserver.DataSetRegistrationAlgorithm.DataSetRegistrationAlgorithmState;
 import ch.systemsx.cisd.etlserver.validation.IDataSetValidator;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
 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.DatabaseInstance;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
 import ch.systemsx.cisd.openbis.generic.shared.dto.NewExternalData;
-import ch.systemsx.cisd.openbis.generic.shared.dto.StorageFormat;
-import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
 
-public abstract class TransferredDataSetHandlerDataSetRegistrationAlgorithm
+public abstract class TransferredDataSetHandlerDataSetRegistrationAlgorithm implements
+        DataSetRegistrationAlgorithm.IRollbackDelegate
 {
     @Private
-    public static final String EMAIL_SUBJECT_TEMPLATE = "Success: data set for experiment '%s";
+    public static final String EMAIL_SUBJECT_TEMPLATE =
+            DataSetRegistrationAlgorithm.EMAIL_SUBJECT_TEMPLATE;
 
     @Private
     static final String DATA_SET_REGISTRATION_FAILURE_TEMPLATE =
-            "Registration of data set '%s' failed.";
+            DataSetRegistrationAlgorithm.DATA_SET_REGISTRATION_FAILURE_TEMPLATE;
 
     @Private
-    static final String DATA_SET_STORAGE_FAILURE_TEMPLATE = "Storing data set '%s' failed.";
+    static final String DATA_SET_STORAGE_FAILURE_TEMPLATE =
+            DataSetRegistrationAlgorithm.DATA_SET_STORAGE_FAILURE_TEMPLATE;
 
     @Private
-    static final String SUCCESSFULLY_REGISTERED = "Successfully registered data set: [";
-
-    protected final IDelegatedActionWithResult<Boolean> cleanAftrewardsAction;
+    static final String SUCCESSFULLY_REGISTERED =
+            DataSetRegistrationAlgorithm.SUCCESSFULLY_REGISTERED;
 
     protected final File incomingDataSetFile;
 
-    protected final DataSetInformation dataSetInformation;
-
-    protected final IDataStoreStrategy dataStoreStrategy;
-
-    protected final DataSetType dataSetType;
-
-    protected final File storeRoot;
-
-    private final IPreRegistrationAction preRegistrationAction;
-
-    private final IPostRegistrationAction postRegistrationAction;
-
-    protected BaseDirectoryHolder baseDirectoryHolder;
-
-    protected String errorMessageTemplate;
+    protected final DataSetRegistrationAlgorithm algorithm;
 
     public TransferredDataSetHandlerDataSetRegistrationAlgorithm(File incomingDataSetFile,
             IDelegatedActionWithResult<Boolean> cleanAftrewardsAction,
             IPreRegistrationAction preRegistrationAction,
             IPostRegistrationAction postRegistrationAction)
     {
-        this.preRegistrationAction = preRegistrationAction;
-        this.postRegistrationAction = postRegistrationAction;
-        this.errorMessageTemplate = TransferredDataSetHandlerDataSetRegistrationAlgorithm.DATA_SET_STORAGE_FAILURE_TEMPLATE;
-        this.incomingDataSetFile = incomingDataSetFile;
-        this.cleanAftrewardsAction = cleanAftrewardsAction;
-        this.dataSetInformation = extractDataSetInformation(incomingDataSetFile);
-        if (dataSetInformation.getDataSetCode() == null)
-        {
-            // Extractor didn't extract an externally generated data set code, so request one
-            // from the openBIS server.
-            dataSetInformation.setDataSetCode(getOpenBisService().createDataSetCode());
-        }
-        this.dataStoreStrategy =
+        DataSetInformation dataSetInformation = extractDataSetInformation(incomingDataSetFile);
+        IDataStoreStrategy dataStoreStrategy =
                 getDataStrategyStore()
                         .getDataStoreStrategy(dataSetInformation, incomingDataSetFile);
-        this.dataSetType = getTypeExtractor().getDataSetType(incomingDataSetFile);
-        dataSetInformation.setDataSetType(dataSetType);
-        this.storeRoot = getStorageProcessor().getStoreRootDirectory();
+        DataSetRegistrationAlgorithmState algorithmState =
+                new DataSetRegistrationAlgorithmState(incomingDataSetFile, getOpenBisService(),
+                        cleanAftrewardsAction, preRegistrationAction, postRegistrationAction,
+                        dataSetInformation, dataStoreStrategy, getTypeExtractor(),
+                        getStorageProcessor(), getFileOperations(), getDataSetValidator(),
+                        getMailClient(), shouldDeleteUnidentified(), getRegistrationLock(),
+                        getDataStoreCode(), shouldNotifySuccessfulRegistration());
+        algorithm = new DataSetRegistrationAlgorithm(algorithmState, this);
+        this.incomingDataSetFile = algorithmState.getIncomingDataSetFile();
     }
 
     /**
@@ -117,7 +88,7 @@ public abstract class TransferredDataSetHandlerDataSetRegistrationAlgorithm
      */
     public DataSetInformation getDataSetInformation()
     {
-        return dataSetInformation;
+        return algorithm.getDataSetInformation();
     }
 
     /**
@@ -125,15 +96,12 @@ public abstract class TransferredDataSetHandlerDataSetRegistrationAlgorithm
      */
     public final void prepare()
     {
-        final File baseDirectory =
-                createBaseDirectory(dataStoreStrategy, storeRoot, dataSetInformation);
-        baseDirectoryHolder =
-                new BaseDirectoryHolder(dataStoreStrategy, baseDirectory, incomingDataSetFile);
+        algorithm.prepare();
     }
 
     public final boolean hasDataSetBeenIdentified()
     {
-        return dataStoreStrategy.getKey() == DataStoreStrategyKey.IDENTIFIED;
+        return algorithm.hasDataSetBeenIdentified();
     }
 
     /**
@@ -141,154 +109,7 @@ public abstract class TransferredDataSetHandlerDataSetRegistrationAlgorithm
      */
     public final List<DataSetInformation> registerDataSet()
     {
-        String processorID = getTypeExtractor().getProcessorType(incomingDataSetFile);
-        try
-        {
-            getDataSetValidator().assertValidDataSet(dataSetType, incomingDataSetFile);
-            registerDataSetAndInitiateProcessing(processorID);
-            logAndNotifySuccessfulRegistration(getEmail());
-            if (getFileOperations().exists(incomingDataSetFile)
-                    && removeAndLog("clean up failed") == false)
-            {
-                getOperationLog().error(
-                        "Cannot delete '" + incomingDataSetFile.getAbsolutePath() + "'.");
-            }
-
-            clean();
-            return Collections.singletonList(dataSetInformation);
-        } catch (final HighLevelException ex)
-        {
-            final String userEmailOrNull = dataSetInformation.tryGetUploadingUserEmail();
-            boolean deleted = false;
-            if (userEmailOrNull != null)
-            {
-                final String errorMessage =
-                        "Error when trying to register data set '" + incomingDataSetFile.getName()
-                                + "'.";
-                getMailClient()
-                        .sendMessage(
-                                String.format(errorMessage, dataSetInformation
-                                        .getExperimentIdentifier().getExperimentCode()),
-                                ex.getMessage(), null, null, userEmailOrNull);
-                if (shouldDeleteUnidentified())
-                {
-                    deleted = removeAndLog(errorMessage + " [" + ex.getMessage() + "]");
-                }
-            }
-            if (deleted == false)
-            {
-                rollback(ex);
-            }
-            return Collections.emptyList();
-        } catch (final Throwable throwable)
-        {
-            rollback(throwable);
-            return Collections.emptyList();
-        }
-    }
-
-    private String getEmail()
-    {
-        Experiment experiment = dataSetInformation.tryToGetExperiment();
-        if (experiment == null)
-        {
-            throw new UserFailureException("Unknown experiment of data set " + dataSetInformation);
-        }
-        return experiment.getRegistrator().getEmail();
-    }
-
-    protected UnstoreDataAction rollbackStorageProcessor(final Throwable throwable)
-    {
-        UnstoreDataAction action =
-                getStorageProcessor().rollback(incomingDataSetFile,
-                        baseDirectoryHolder.getBaseDirectory(), throwable);
-        return action;
-    }
-
-    /**
-     * Registers the data set.
-     */
-    private void registerDataSetAndInitiateProcessing(final String procedureTypeCode)
-            throws Throwable
-    {
-        final File markerFile = createProcessingMarkerFile();
-        try
-        {
-            String entityDescription = createEntityDescription();
-            if (TransferredDataSetHandler.operationLog.isInfoEnabled())
-            {
-                TransferredDataSetHandler.operationLog.info("Start storing data set for "
-                        + entityDescription + ".");
-            }
-            final StopWatch watch = new StopWatch();
-            watch.start();
-            NewExternalData data = createExternalData();
-            preRegistrationAction.execute(data.getCode(), incomingDataSetFile.getAbsolutePath());
-            File dataFile =
-                    getStorageProcessor().storeData(dataSetInformation, getTypeExtractor(),
-                            getMailClient(), incomingDataSetFile,
-                            baseDirectoryHolder.getBaseDirectory());
-            if (getOperationLog().isInfoEnabled())
-            {
-                getOperationLog().info(
-                        "Finished storing data set for " + entityDescription + ", took " + watch);
-            }
-            assert dataFile != null : "The folder that contains the stored data should not be null.";
-            final String relativePath = FileUtilities.getRelativeFile(storeRoot, dataFile);
-            String absolutePath = dataFile.getAbsolutePath();
-            assert relativePath != null : String.format(
-                    TransferredDataSetHandler.TARGET_NOT_RELATIVE_TO_STORE_ROOT, absolutePath,
-                    storeRoot.getAbsolutePath());
-            final StorageFormat availableFormat = getStorageProcessor().getStorageFormat();
-            final BooleanOrUnknown isCompleteFlag = dataSetInformation.getIsCompleteFlag();
-            // Ensure that we either register the data set and initiate the processing copy or
-            // do none of both.
-            getRegistrationLock().lock();
-            try
-            {
-                errorMessageTemplate =
-                        TransferredDataSetHandlerDataSetRegistrationAlgorithm.DATA_SET_REGISTRATION_FAILURE_TEMPLATE;
-                plainRegisterDataSet(data, relativePath, availableFormat, isCompleteFlag);
-                postRegistrationAction.execute(data.getCode(), absolutePath);
-                clean();
-            } finally
-            {
-                getRegistrationLock().unlock();
-            }
-            getStorageProcessor().commit(incomingDataSetFile,
-                    baseDirectoryHolder.getBaseDirectory());
-        } finally
-        {
-            getFileOperations().delete(markerFile);
-        }
-    }
-
-    private String createEntityDescription()
-    {
-        SampleIdentifier sampleIdentifier = dataSetInformation.getSampleIdentifier();
-        if (sampleIdentifier != null)
-        {
-            return "sample '" + sampleIdentifier + "'";
-        }
-        return "experiment '" + dataSetInformation.getExperimentIdentifier() + "'";
-    }
-
-    private final File createProcessingMarkerFile()
-    {
-        final File baseDirectory = baseDirectoryHolder.getBaseDirectory();
-        final File baseParentDirectory = baseDirectory.getParentFile();
-        final String processingDirName = baseDirectory.getName();
-        final File markerFile =
-                new File(baseParentDirectory, Constants.PROCESSING_PREFIX + processingDirName);
-        try
-        {
-            getFileOperations().createNewFile(markerFile);
-        } catch (final IOExceptionUnchecked ex)
-        {
-            throw EnvironmentFailureException.fromTemplate(ex, "Cannot create marker file '%s'.",
-                    markerFile.getPath());
-        }
-        return markerFile;
+        return algorithm.registerDataSet();
     }
 
     /**
@@ -299,10 +120,10 @@ public abstract class TransferredDataSetHandlerDataSetRegistrationAlgorithm
         final boolean ok =
                 shouldDeleteUnidentified() ? (removeAndLog(incomingDataSetFile.getName()
                         + " could not be identified.")) : FileRenamer.renameAndLog(
-                        incomingDataSetFile, baseDirectoryHolder.getTargetFile());
+                        incomingDataSetFile, algorithm.getBaseDirectoryHolder().getTargetFile());
         if (ok)
         {
-            clean();
+            algorithm.clean();
         }
     }
 
@@ -316,15 +137,6 @@ public abstract class TransferredDataSetHandlerDataSetRegistrationAlgorithm
         return ok;
     }
 
-    private final void plainRegisterDataSet(NewExternalData data, final String relativePath,
-            final StorageFormat storageFormat, final BooleanOrUnknown isCompleteFlag)
-            throws Throwable
-    {
-        updateExternalData(data, relativePath, storageFormat, isCompleteFlag);
-        // Finally: register the data set in the database.
-        registerDataSetInApplicationServer(data);
-    }
-
     /**
      * Contact openBis and register the data set there. Subclasses may override.
      * 
@@ -332,79 +144,7 @@ public abstract class TransferredDataSetHandlerDataSetRegistrationAlgorithm
      */
     protected void registerDataSetInApplicationServer(NewExternalData data) throws Throwable
     {
-        getOpenBisService().registerDataSet(dataSetInformation, data);
-    }
-
-    private void logAndNotifySuccessfulRegistration(final String email)
-    {
-        String msg = null;
-        if (TransferredDataSetHandler.operationLog.isInfoEnabled())
-        {
-            msg = getSuccessRegistrationMessage();
-            TransferredDataSetHandler.operationLog.info(msg);
-        }
-        if (shouldNotifySuccessfulRegistration())
-        {
-            if (msg == null)
-            {
-                msg = getSuccessRegistrationMessage();
-            }
-            if (TransferredDataSetHandler.notificationLog.isInfoEnabled())
-            {
-                TransferredDataSetHandler.notificationLog.info(msg);
-            }
-            if (StringUtils.isBlank(email) == false)
-            {
-                getMailClient().sendMessage(
-                        String.format(getEmailSubjectTemplate(), dataSetInformation
-                                .getExperimentIdentifier().getExperimentCode()), msg, null, null,
-                        email);
-            }
-        }
-    }
-
-    private final String getSuccessRegistrationMessage()
-    {
-        final StringBuilder buffer = new StringBuilder();
-        buffer.append(TransferredDataSetHandlerDataSetRegistrationAlgorithm.SUCCESSFULLY_REGISTERED);
-        String userID = dataSetInformation.getUploadingUserIdOrNull();
-        String userEMail = dataSetInformation.tryGetUploadingUserEmail();
-        if (userID != null || userEMail != null)
-        {
-            appendNameAndObject(buffer, "User", userID == null ? userEMail : userID);
-        }
-        appendNameAndObject(buffer, "Data Set Code", dataSetInformation.getDataSetCode());
-        appendNameAndObject(buffer, "Data Set Type", dataSetType.getCode());
-        appendNameAndObject(buffer, "Experiment Identifier",
-                dataSetInformation.getExperimentIdentifier());
-        appendNameAndObject(buffer, "Sample Identifier", dataSetInformation.getSampleIdentifier());
-        appendNameAndObject(buffer, "Producer Code", dataSetInformation.getProducerCode());
-        appendNameAndObject(buffer, "Production Date",
-                formatDate(dataSetInformation.getProductionDate()));
-        final List<String> parentDataSetCodes = dataSetInformation.getParentDataSetCodes();
-        if (parentDataSetCodes.isEmpty() == false)
-        {
-            appendNameAndObject(buffer, "Parent Data Sets",
-                    StringUtils.join(parentDataSetCodes, ' '));
-        }
-        appendNameAndObject(buffer, "Is complete", dataSetInformation.getIsCompleteFlag());
-        buffer.setLength(buffer.length() - 1);
-        buffer.append(']');
-        return buffer.toString();
-    }
-
-    private String formatDate(Date productionDate)
-    {
-        return productionDate == null ? "" : Constants.DATE_FORMAT.get().format(productionDate);
-    }
-
-    private final void appendNameAndObject(final StringBuilder buffer, final String name,
-            final Object object)
-    {
-        if (object != null)
-        {
-            buffer.append(name).append("::").append(object).append(";");
-        }
+        getOpenBisService().registerDataSet(algorithm.getDataSetInformation(), data);
     }
 
     /**
@@ -449,48 +189,12 @@ public abstract class TransferredDataSetHandlerDataSetRegistrationAlgorithm
         }
     }
 
-    protected final File createBaseDirectory(final IDataStoreStrategy strategy, final File baseDir,
-            final DataSetInformation dataSetInfo)
-    {
-        final File baseDirectory = strategy.getBaseDirectory(baseDir, dataSetInfo, dataSetType);
-        baseDirectory.mkdirs();
-        if (getFileOperations().isDirectory(baseDirectory) == false)
-        {
-            throw EnvironmentFailureException.fromTemplate(
-                    "Creating data set base directory '%s' for data set '%s' failed.",
-                    baseDirectory.getAbsolutePath(), incomingDataSetFile);
-        }
-        return baseDirectory;
-    }
-
-    private final NewExternalData updateExternalData(NewExternalData data,
-            final String relativePath, final StorageFormat storageFormat,
-            final BooleanOrUnknown isCompleteFlag)
-    {
-        data.setComplete(isCompleteFlag);
-        data.setLocation(relativePath);
-        data.setStorageFormat(storageFormat);
-        return data;
-    }
-
-    private NewExternalData createExternalData()
-    {
-        final NewExternalData data = new NewExternalData();
-        data.setUserId(dataSetInformation.getUploadingUserIdOrNull());
-        data.setUserEMail(dataSetInformation.tryGetUploadingUserEmail());
-        data.setExtractableData(dataSetInformation.getExtractableData());
-        data.setLocatorType(getTypeExtractor().getLocatorType(incomingDataSetFile));
-        data.setDataSetType(getTypeExtractor().getDataSetType(incomingDataSetFile));
-        data.setFileFormatType(getTypeExtractor().getFileFormatType(incomingDataSetFile));
-        data.setMeasured(getTypeExtractor().isMeasuredData(incomingDataSetFile));
-        data.setDataStoreCode(getDataStoreCode());
-        return data;
-    }
-
     protected final void writeThrowable(final Throwable throwable)
     {
         final String fileName = incomingDataSetFile.getName() + ".exception";
-        final File file = new File(baseDirectoryHolder.getTargetFile().getParentFile(), fileName);
+        final File file =
+                new File(algorithm.getBaseDirectoryHolder().getTargetFile().getParentFile(),
+                        fileName);
         FileWriter writer = null;
         try
         {
@@ -507,9 +211,9 @@ public abstract class TransferredDataSetHandlerDataSetRegistrationAlgorithm
         }
     }
 
-    protected boolean clean()
+    public void rollback(DataSetRegistrationAlgorithm algo, Throwable ex)
     {
-        return cleanAftrewardsAction.execute();
+        rollback(ex);
     }
 
     // subclass responsibility
@@ -546,5 +250,4 @@ public abstract class TransferredDataSetHandlerDataSetRegistrationAlgorithm
 
     protected abstract boolean shouldNotifySuccessfulRegistration();
 
-    protected abstract String getEmailSubjectTemplate();
 }
\ No newline at end of file
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetExecutor.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetExecutor.java
index c74b2ea29bc..ef1b0c8e2a9 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetExecutor.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetExecutor.java
@@ -524,12 +524,6 @@ class PutDataSetExecutor implements IDataSetHandlerRpc
             return service.getDataStrategyStore();
         }
 
-        @Override
-        protected String getEmailSubjectTemplate()
-        {
-            return TransferredDataSetHandlerDataSetRegistrationAlgorithm.EMAIL_SUBJECT_TEMPLATE;
-        }
-
         @Override
         protected IFileOperations getFileOperations()
         {
@@ -587,7 +581,7 @@ class PutDataSetExecutor implements IDataSetHandlerRpc
         @Override
         protected void rollback(Throwable ex)
         {
-            rollbackStorageProcessor(ex);
+            algorithm.rollbackStorageProcessor(ex);
             if (ex instanceof UserFailureException)
             {
                 throw (UserFailureException) ex;
-- 
GitLab