From 41f0d020bba694f1fccd586f2ba3a70ab3bdd634 Mon Sep 17 00:00:00 2001
From: cramakri <cramakri>
Date: Wed, 22 Dec 2010 16:59:59 +0000
Subject: [PATCH] LMS-1947 Better error messages.

SVN: 19214
---
 .../RegistrationStatus.java                   |  76 +++++++
 .../SampleAndDataSetControlFileProcessor.java | 211 ++++++++++++++++--
 ...mpleAndDataSetRegistrationGlobalState.java |  19 +-
 .../SampleAndDataSetRegistrationHandler.java  |  26 ++-
 .../SampleAndDataSetRegistrator.java          |  77 +++----
 ...mpleAndDatasetRegistrationHandlerTest.java | 124 ++++++++--
 .../not-all-subfolders-mentioned/control.tsv  |   3 +-
 7 files changed, 437 insertions(+), 99 deletions(-)
 create mode 100644 datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/RegistrationStatus.java

diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/RegistrationStatus.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/RegistrationStatus.java
new file mode 100644
index 00000000000..0deef8150bd
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/RegistrationStatus.java
@@ -0,0 +1,76 @@
+package ch.systemsx.cisd.etlserver.entityregistration;
+
+/**
+ * An interface to describe success and errors encountered during registration.
+ * 
+ * @author Chandrasekhar Ramakrishnan
+ */
+interface IRegistrationStatus
+{
+    boolean isError();
+
+    Throwable getError();
+
+    String getMessage();
+}
+
+/**
+ * Abstract superclass for success and errors.
+ * 
+ * @author Chandrasekhar Ramakrishnan
+ */
+abstract class AbstractRegistrationStatus implements IRegistrationStatus
+{
+    protected AbstractRegistrationStatus()
+    {
+    }
+}
+
+class RegistrationError extends AbstractRegistrationStatus
+{
+    private final Throwable error;
+
+    RegistrationError(Throwable error)
+    {
+        this.error = error;
+    }
+
+    public boolean isError()
+    {
+        return true;
+    }
+
+    public Throwable getError()
+    {
+        return error;
+    }
+
+    public String getMessage()
+    {
+        return error.getMessage();
+    }
+}
+
+class RegistrationSuccess extends AbstractRegistrationStatus
+{
+    RegistrationSuccess(String[] registeredMetadata)
+    {
+    }
+
+    public boolean isError()
+    {
+        return false;
+    }
+
+    public Throwable getError()
+    {
+        assert false : "getError() should not be called on a success object";
+
+        return null;
+    }
+
+    public String getMessage()
+    {
+        return "Success";
+    }
+}
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDataSetControlFileProcessor.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDataSetControlFileProcessor.java
index 849cee06646..5f43a13b669 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDataSetControlFileProcessor.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDataSetControlFileProcessor.java
@@ -21,7 +21,9 @@ import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.Reader;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 
 import javax.activation.DataHandler;
@@ -34,7 +36,6 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.common.io.DelegatedReader;
 import ch.systemsx.cisd.common.mail.EMailAddress;
 import ch.systemsx.cisd.common.utilities.UnicodeUtils;
-import ch.systemsx.cisd.etlserver.entityregistration.SampleAndDataSetRegistrator.RegistrationErrorWrapper;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Person;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleType;
@@ -47,10 +48,26 @@ import ch.systemsx.cisd.openbis.generic.shared.parser.GlobalPropertiesLoader;
  */
 class SampleAndDataSetControlFileProcessor extends AbstractSampleAndDataSetProcessor
 {
+    private static final String SUCCESS_FILENAME = "registered.txt";
+
     private final File controlFile;
 
-    private final HashMap<SampleDataSetPair, RegistrationErrorWrapper> errorMap =
-            new HashMap<SampleDataSetPair, RegistrationErrorWrapper>();
+    private final HashMap<SampleDataSetPair, IRegistrationStatus> errorMap =
+            new HashMap<SampleDataSetPair, IRegistrationStatus>();
+
+    private final HashMap<SampleDataSetPair, IRegistrationStatus> successMap =
+            new HashMap<SampleDataSetPair, IRegistrationStatus>();
+
+    private final HashSet<File> processedDataSetFiles = new HashSet<File>();
+
+    // State that is filled out as a result of processing
+    private ControlFileRegistrationProperties properties;
+
+    private String failureLinesResultSectionOrNull;
+
+    private String unmentionedSubfoldersResultSectionOrNull;
+
+    private String successLinesResultSectionOrNull;
 
     /**
      * Utility class for accessing the properties defined in a control file.
@@ -226,8 +243,7 @@ class SampleAndDataSetControlFileProcessor extends AbstractSampleAndDataSetProce
 
         logControlFileOverridePropertiesExtracted(overrideProperties);
 
-        ControlFileRegistrationProperties properties =
-                new ControlFileRegistrationProperties(overrideProperties, globalState);
+        properties = new ControlFileRegistrationProperties(overrideProperties, globalState);
 
         BisTabFileLoader<SampleDataSetPair> controlFileLoader =
                 new BisTabFileLoader<SampleDataSetPair>(
@@ -252,7 +268,7 @@ class SampleAndDataSetControlFileProcessor extends AbstractSampleAndDataSetProce
                 throw e;
             }
 
-            sendEmailWithErrorMessage(properties, e.getMessage());
+            sendEmailWithErrorMessage(e.getMessage());
             return;
         } finally
         {
@@ -272,26 +288,69 @@ class SampleAndDataSetControlFileProcessor extends AbstractSampleAndDataSetProce
             sampleDataSet.getDataSetInformation().setUploadingUserId(userId);
             SampleAndDataSetRegistrator registrator =
                     new SampleAndDataSetRegistrator(folder, properties, sampleDataSet);
-            RegistrationErrorWrapper resultOrNull = registrator.register();
-            if (null != resultOrNull)
+            IRegistrationStatus result = registrator.register();
+            processedDataSetFiles.add(registrator.getDataSetFile());
+            if (result.isError())
             {
-                errorMap.put(sampleDataSet, resultOrNull);
+                errorMap.put(sampleDataSet, result);
+            } else
+            {
+                successMap.put(sampleDataSet, result);
             }
         }
-        if (false == errorMap.isEmpty())
+
+        sendResultsEmail();
+    }
+
+    private void sendResultsEmail()
+    {
+
+        createFailureLinesSection();
+        createUnmentionedFoldersSection();
+        createSuccessfulLinesSection();
+
+        boolean wasSuccessful = true;
+
+        StringBuilder resultEmail = new StringBuilder();
+        if (null != failureLinesResultSectionOrNull)
+        {
+            resultEmail.append(failureLinesResultSectionOrNull);
+            resultEmail.append("\n");
+            wasSuccessful = false;
+        }
+        if (null != unmentionedSubfoldersResultSectionOrNull)
+        {
+            resultEmail.append(unmentionedSubfoldersResultSectionOrNull);
+            resultEmail.append("\n");
+            wasSuccessful = false;
+        }
+        if (null != successLinesResultSectionOrNull)
         {
-            sendEmailWithErrorMessage(properties, createErrorMessageFromErrorMap());
+            resultEmail.append(successLinesResultSectionOrNull);
         }
 
+        if (wasSuccessful)
+        {
+            sendEmailWithSuccessMessage(resultEmail.toString());
+        } else
+        {
+            sendEmailWithErrorMessage(resultEmail.toString());
+        }
     }
 
-    private String createErrorMessageFromErrorMap()
+    private void createFailureLinesSection()
     {
+        if (errorMap.isEmpty())
+        {
+            failureLinesResultSectionOrNull = null;
+            return;
+        }
+
         StringBuilder sb = new StringBuilder();
         sb.append("Encountered errors in the following lines:\n");
         for (SampleDataSetPair pair : errorMap.keySet())
         {
-            RegistrationErrorWrapper error = errorMap.get(pair);
+            IRegistrationStatus error = errorMap.get(pair);
             sb.append("# ");
             sb.append(error.getMessage());
             sb.append("\n");
@@ -308,26 +367,107 @@ class SampleAndDataSetControlFileProcessor extends AbstractSampleAndDataSetProce
             }
             sb.append("\n");
         }
-        return sb.toString();
+        failureLinesResultSectionOrNull = sb.toString();
+    }
+
+    private void createUnmentionedFoldersSection()
+    {
+        if (false == globalState.areUnmentionedFoldersAnError())
+        {
+            unmentionedSubfoldersResultSectionOrNull = null;
+            return;
+        }
+        // Make sure all folders were processed
+        ArrayList<File> unprocessedFiles = getUnprocessedDataSetList();
+
+        if (unprocessedFiles.isEmpty())
+        {
+            unmentionedSubfoldersResultSectionOrNull = null;
+            return;
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("The following subfolders were in the uploaded folder, but were not mentioned in the control file:\n");
+        for (File file : unprocessedFiles)
+        {
+            sb.append(file.getName());
+            sb.append(",");
+        }
+
+        // remove the final comma
+        sb.deleteCharAt(sb.length() - 1);
+
+        unmentionedSubfoldersResultSectionOrNull = sb.toString();
+    }
+
+    private void createSuccessfulLinesSection()
+    {
+        if (errorMap.isEmpty())
+        {
+            successLinesResultSectionOrNull = null;
+            return;
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("The following lines were successfully registered:\n");
+        for (SampleDataSetPair pair : successMap.keySet())
+        {
+            sb.append("# ");
+            String[] tokens = pair.getTokens();
+            int i = 0;
+            for (String token : tokens)
+            {
+                sb.append(token);
+                if (++i < tokens.length)
+                {
+                    sb.append("\t");
+                }
+            }
+            sb.append("\n");
+        }
+        successLinesResultSectionOrNull = sb.toString();
     }
 
-    private void logControlFileOverridePropertiesExtracted(ControlFileOverrideProperties properties)
+    private void logControlFileOverridePropertiesExtracted(
+            ControlFileOverrideProperties overrideProperties)
     {
         String message =
                 String.format(
                         "Global properties extracted from file '%s': SAMPLE_TYPE(%s) DATA_SET_TYPE(%s) USER(%s)",
-                        controlFile.getName(), properties.trySampleType(),
-                        properties.tryDataSetType(), properties.tryUserString());
+                        controlFile.getName(), overrideProperties.trySampleType(),
+                        overrideProperties.tryDataSetType(), overrideProperties.tryUserString());
         logInfo(message);
     }
 
+    /**
+     * Send an email message to the person who uploaded the file, telling them that everything went
+     * ok.
+     */
+    private void sendEmailWithSuccessMessage(String message)
+    {
+        // Create an email and send it.
+        try
+        {
+            String subject = createSuccessEmailSubject();
+            String content = createSuccessEmailContent();
+            String filename = SUCCESS_FILENAME;
+            EMailAddress recipient = new EMailAddress(properties.getUser().getEmail());
+            DataSource dataSource = new ByteArrayDataSource(message, "text/plain");
+
+            globalState.getMailClient().sendEmailMessageWithAttachment(subject, content, filename,
+                    new DataHandler(dataSource), null, null, recipient);
+        } catch (IOException ex)
+        {
+            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
+        }
+    }
+
     /**
      * Send an email message to the person who uploaded the file. This method is only called if we
      * have a valid email address to contact. Otherwise, errors are forwarded to a higher level for
      * handling.
      */
-    private void sendEmailWithErrorMessage(ControlFileRegistrationProperties properties,
-            String message)
+    private void sendEmailWithErrorMessage(String message)
     {
         // Log it
         logError(message);
@@ -360,4 +500,37 @@ class SampleAndDataSetControlFileProcessor extends AbstractSampleAndDataSetProce
                 .format("Not all samples and data sets specified in the control file, %s, could be registered / updated. The errors are detailed in the attachment. Each faulty line is reproduced, preceded by a comment explaining the cause of the error.",
                         controlFile);
     }
+
+    private String createSuccessEmailSubject()
+    {
+        return String.format("Sample / Data Set Registration Succeeded -- %s", controlFile);
+    }
+
+    private String createSuccessEmailContent()
+    {
+        return String
+                .format("The registration/update of samples and the registration of data sets was successful specified in the control file, %s, was successful.",
+                        controlFile);
+    }
+
+    private ArrayList<File> getUnprocessedDataSetList()
+    {
+        File[] files = folder.listFiles();
+        ArrayList<File> unprcessedDataSets = new ArrayList<File>();
+        for (File file : files)
+        {
+            if (controlFile.equals(file))
+            {
+                continue;
+            }
+            // See if it was handled already
+            if (processedDataSetFiles.contains(file))
+            {
+                continue;
+            }
+            unprcessedDataSets.add(file);
+        }
+
+        return unprcessedDataSets;
+    }
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDataSetRegistrationGlobalState.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDataSetRegistrationGlobalState.java
index a33aa4d4c06..e384ae0df02 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDataSetRegistrationGlobalState.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDataSetRegistrationGlobalState.java
@@ -58,6 +58,10 @@ class SampleAndDataSetRegistrationGlobalState
 
     private final String controlFilePattern;
 
+    private final boolean alwaysCleanupAfterProcessing;
+
+    private final boolean unmentionedSubfolderIsFailure;
+
     private final Logger operationLog;
 
     private IMailClient mailClient;
@@ -66,7 +70,8 @@ class SampleAndDataSetRegistrationGlobalState
             IEncapsulatedOpenBISService openbisService, SpaceIdentifier spaceIdentifierOrNull,
             SampleType sampleTypeOrNull, DataSetType dataSetTypeOrNull,
             SampleRegistrationMode sampleRegistrationMode, List<String> errorEmailRecipientsOrNull,
-            String controlFilePattern, Logger operationLog)
+            String controlFilePattern, boolean alwaysCleanupAfterProcessing,
+            boolean unmentionedSubfolderIsFailure, Logger operationLog)
     {
         this.delegator = delegator;
         this.openbisService = openbisService;
@@ -76,6 +81,8 @@ class SampleAndDataSetRegistrationGlobalState
         this.sampleRegistrationMode = sampleRegistrationMode;
         this.errorEmailRecipientsOrNull = errorEmailRecipientsOrNull;
         this.controlFilePattern = controlFilePattern;
+        this.alwaysCleanupAfterProcessing = alwaysCleanupAfterProcessing;
+        this.unmentionedSubfolderIsFailure = unmentionedSubfolderIsFailure;
         this.operationLog = operationLog;
     }
 
@@ -174,4 +181,14 @@ class SampleAndDataSetRegistrationGlobalState
     {
         return controlFilePattern;
     }
+
+    public boolean alwaysCleanUpAfterProcessing()
+    {
+        return alwaysCleanupAfterProcessing;
+    }
+
+    public boolean areUnmentionedFoldersAnError()
+    {
+        return unmentionedSubfolderIsFailure;
+    }
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDataSetRegistrationHandler.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDataSetRegistrationHandler.java
index 4919eb8580f..453e1fc896a 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDataSetRegistrationHandler.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDataSetRegistrationHandler.java
@@ -67,7 +67,13 @@ public class SampleAndDataSetRegistrationHandler implements IDataSetHandlerWithM
 
     static final String CONTROL_FILE_REGEX_PATTERN = "control-file-regex-pattern";
 
-    private static final String DEFAULT_CONTROL_FILE_REGEX_PATTERN = ".*.[Tt][Ss][Vv]";
+    private static final String DEFAULT_CONTROL_FILE_REGEX_PATTERN = ".*\\.[Tt][Ss][Vv]";
+
+    static final String CONTROL_FILE_ALWAYS_CLEANUP_AFTER_PROCESSING =
+            "always-cleanup-after-processing";
+
+    static final String CONTROL_FILE_UNMENTIONED_SUBFOLDER_IS_FAILURE =
+            "unmentioned-subfolder-is-failure";
 
     private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION,
             SampleAndDataSetRegistrationHandler.class);
@@ -134,9 +140,18 @@ public class SampleAndDataSetRegistrationHandler implements IDataSetHandlerWithM
                 PropertyUtils.getProperty(specificProperties, CONTROL_FILE_REGEX_PATTERN,
                         DEFAULT_CONTROL_FILE_REGEX_PATTERN);
 
+        boolean deleteFilesOnFailure =
+                PropertyUtils.getBoolean(specificProperties,
+                        CONTROL_FILE_ALWAYS_CLEANUP_AFTER_PROCESSING, true);
+
+        boolean unmentionedSubfolderIsFailure =
+                PropertyUtils.getBoolean(specificProperties,
+                        CONTROL_FILE_UNMENTIONED_SUBFOLDER_IS_FAILURE, true);
+
         return new SampleAndDataSetRegistrationGlobalState(delegator, service, spaceIdentifier,
                 sampleTypeOrNull, dataSetTypeOrNull, registrationMode, errorEmailRecipients,
-                controlFilePattern, operationLog);
+                controlFilePattern, deleteFilesOnFailure, unmentionedSubfolderIsFailure,
+                operationLog);
     }
 
     public void initializeMailClient(IMailClient mailClient)
@@ -158,8 +173,11 @@ public class SampleAndDataSetRegistrationHandler implements IDataSetHandlerWithM
             throw new CheckedExceptionTunnel(ex);
         } finally
         {
-            FileOperations.getMonitoredInstanceForCurrentThread().deleteRecursively(file);
-            logFileDeletion(file);
+            if (globalState.alwaysCleanUpAfterProcessing())
+            {
+                FileOperations.getMonitoredInstanceForCurrentThread().deleteRecursively(file);
+                logFileDeletion(file);
+            }
         }
         return createReturnValue();
     }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDataSetRegistrator.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDataSetRegistrator.java
index 66eecad09ae..7c6c9215d3f 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDataSetRegistrator.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDataSetRegistrator.java
@@ -40,57 +40,21 @@ class SampleAndDataSetRegistrator extends AbstractSampleAndDataSetProcessor impl
         TransferredDataSetHandler.IDataSetRegistrator
 {
 
-    /**
-     * A wrapper around errors encountered during registration.
-     * 
-     * @author Chandrasekhar Ramakrishnan
-     */
-    static class RegistrationErrorWrapper
-    {
-        private final boolean isError;
-
-        private final Throwable errorOrNull;
-
-        RegistrationErrorWrapper(Throwable errorOrNull)
-        {
-            this.errorOrNull = errorOrNull;
-            isError = errorOrNull != null;
-        }
-
-        public boolean isError()
-        {
-            return isError;
-        }
-
-        public Throwable getErrorOrNull()
-        {
-            return errorOrNull;
-        }
-
-        public String getMessage()
-        {
-            if (isError)
-            {
-                return errorOrNull.getMessage();
-            } else
-            {
-                return "Success";
-            }
-        }
-
-    }
-
     private final ControlFileRegistrationProperties properties;
 
     private final SampleDataSetPair sampleDataSetPair;
 
     // State that is updated during the registration
+
+    // The file/folder to register
+    private File dataSetFile;
+
     // The sample, if the sample exists, null otherwise.
     private Sample sampleOrNull;
 
     private boolean didSucceed = false;
 
-    private RegistrationErrorWrapper failureException = null;
+    private IRegistrationStatus failureException = null;
 
     SampleAndDataSetRegistrator(File folder, ControlFileRegistrationProperties properties,
             SampleDataSetPair sampleDataSetPair)
@@ -105,18 +69,18 @@ class SampleAndDataSetRegistrator extends AbstractSampleAndDataSetProcessor impl
      * 
      * @return An exception if one was encountered, otherwise null.
      */
-    public RegistrationErrorWrapper register()
+    public IRegistrationStatus register()
     {
-        File dataSetFile = new File(folder, sampleDataSetPair.getFolderName());
+        dataSetFile = new File(folder, sampleDataSetPair.getFolderName());
         try
         {
-            checkDataSetFileNotEmpty(dataSetFile);
+            checkDataSetFileNotEmpty();
             checkExperimentExists();
             retrieveSampleOrNull();
             checkConformanceToMode();
         } catch (UserFailureException ex)
         {
-            return new RegistrationErrorWrapper(ex);
+            return new RegistrationError(ex);
         }
 
         if (globalState.getDelegator() instanceof IExtensibleDataSetHandler)
@@ -129,7 +93,22 @@ class SampleAndDataSetRegistrator extends AbstractSampleAndDataSetProcessor impl
                 logDataRegistered();
             }
         }
-        return failureException;
+        if (null == failureException)
+        {
+            return new RegistrationSuccess(sampleDataSetPair.getTokens());
+        } else
+        {
+            return failureException;
+        }
+    }
+
+    /**
+     * Return the file that we treat as a data set. This is only valid *after* register has been
+     * called.
+     */
+    public File getDataSetFile()
+    {
+        return dataSetFile;
     }
 
     /**
@@ -159,7 +138,7 @@ class SampleAndDataSetRegistrator extends AbstractSampleAndDataSetProcessor impl
         }
     }
 
-    private void checkDataSetFileNotEmpty(File dataSetFile)
+    private void checkDataSetFileNotEmpty()
     {
         if (0 == dataSetFile.list().length)
         {
@@ -239,13 +218,13 @@ class SampleAndDataSetRegistrator extends AbstractSampleAndDataSetProcessor impl
         } catch (UserFailureException e)
         {
             didSucceed = false;
-            failureException = new RegistrationErrorWrapper(e);
+            failureException = new RegistrationError(e);
             throw e;
         } catch (Throwable e)
         {
             logError("Could not register " + sampleDataSetPair + " in openBIS", e);
             didSucceed = false;
-            failureException = new RegistrationErrorWrapper(e);
+            failureException = new RegistrationError(e);
             throw e;
         }
     }
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDatasetRegistrationHandlerTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDatasetRegistrationHandlerTest.java
index 3897a1727be..305ce74230f 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDatasetRegistrationHandlerTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/entityregistration/SampleAndDatasetRegistrationHandlerTest.java
@@ -111,10 +111,10 @@ public class SampleAndDatasetRegistrationHandlerTest extends AbstractFileSystemT
         final RecordingMatcher<DataHandler> attachmentMatcher = new RecordingMatcher<DataHandler>();
         final RecordingMatcher<EMailAddress[]> addressesMatcher =
                 new RecordingMatcher<EMailAddress[]>();
-        setupPartialSuccessErrorEmailExpectations(attachmentMatcher, addressesMatcher,
-                "missing-sample-identifier");
+        final String folderName = "missing-sample-identifier";
+        setupPartialSuccessErrorEmailExpectations(attachmentMatcher, addressesMatcher, folderName);
 
-        File workingCopy = createWorkingCopyOfTestFolder("missing-sample-identifier");
+        File workingCopy = createWorkingCopyOfTestFolder(folderName);
 
         initializeDefaultDataSetHandler();
         handler.handleDataSet(workingCopy);
@@ -125,8 +125,44 @@ public class SampleAndDatasetRegistrationHandlerTest extends AbstractFileSystemT
                         + "Registered sample/data set pair SampleDataSetPair[sampleIdentifier=/MYSPACE/S3,sampleProperties={prop1: VAL12,prop2: VAL22,prop3: VAL32},dataSetInformation=DataSetInformation{sampleCode=<null>,properties={},dataSetType=MY_DATA_SET_TYPE,instanceUUID=<null>,instanceCode=<null>,spaceCode=<null>,experimentIdentifier=/MYSPACE/MYPROJ/EXP3,isCompleteFlag=U,extractableData=ExtractableData{productionDate=<null>,dataProducerCode=<null>,parentDataSetCodes=[],dataSetProperties=[NewProperty{property=prop1,value=VAL42}, NewProperty{property=prop2,value=VAL52}],code=<null>},uploadingUserEmailOrNull=<null>,uploadingUserIdOrNull=test}]\n"
                         + "Encountered errors in the following lines:\n"
                         + "# Illegal empty identifier\n"
-                        + "\t/MYSPACE/MYPROJ/EXP2\tVAL11\tVAL21\tVAL31\tFILE_TYPE\tVAL41\tVAL51\tds2/\n";
-        checkAppenderContent(logText, "missing-sample-identifier");
+                        + "\t/MYSPACE/MYPROJ/EXP2\tVAL11\tVAL21\tVAL31\tFILE_TYPE\tVAL41\tVAL51\tds2/\n\n"
+                        + "The following lines were successfully registered:\n"
+                        + "# /MYSPACE/S3\t/MYSPACE/MYPROJ/EXP3\tVAL12\tVAL22\tVAL32\tFILE_TYPE\tVAL42\tVAL52\tds3/\n"
+                        + "# /MYSPACE/S1\t/MYSPACE/MYPROJ/EXP1\tVAL10\tVAL20\tVAL30\tFILE_TYPE\tVAL40\tVAL50\tds1/\n";
+        checkAppenderContent(logText, folderName);
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testNotAllFoldersMentioned()
+    {
+        final RecordingMatcher<DataSetInformation> dataSetInfoMatcher =
+                new RecordingMatcher<DataSetInformation>();
+        final RecordingMatcher<NewSample> newSampleMatcher = new RecordingMatcher<NewSample>();
+
+        setupOpenBisExpectations();
+        NewExternalData externalData = setupDataSetHandlerExpectations(dataSetInfoMatcher, 1);
+        setupUpdateSampleExistsExpectations("S1", false);
+        setupRegisterSampleAndDataSetExpectations(externalData, newSampleMatcher, 1);
+
+        final String folderName = "not-all-subfolders-mentioned";
+        final RecordingMatcher<DataHandler> attachmentMatcher = new RecordingMatcher<DataHandler>();
+        final RecordingMatcher<EMailAddress[]> addressesMatcher =
+                new RecordingMatcher<EMailAddress[]>();
+        setupPartialSuccessErrorEmailExpectations(attachmentMatcher, addressesMatcher, folderName);
+
+        File workingCopy = createWorkingCopyOfTestFolder(folderName);
+
+        initializeDefaultDataSetHandler();
+        handler.handleDataSet(workingCopy);
+
+        String logText =
+                "Global properties extracted from file 'control.tsv': SAMPLE_TYPE(MY_SAMPLE_TYPE) DATA_SET_TYPE(MY_DATA_SET_TYPE) USER(test@test.test)\n"
+                        + "Registered sample/data set pair SampleDataSetPair[sampleIdentifier=/MYSPACE/S1,sampleProperties={prop1: VAL10,prop2: VAL20,prop3: VAL30},dataSetInformation=DataSetInformation{sampleCode=<null>,properties={},dataSetType=MY_DATA_SET_TYPE,instanceUUID=<null>,instanceCode=<null>,spaceCode=<null>,experimentIdentifier=/MYSPACE/MYPROJ/EXP1,isCompleteFlag=U,extractableData=ExtractableData{productionDate=<null>,dataProducerCode=<null>,parentDataSetCodes=[],dataSetProperties=[NewProperty{property=prop1,value=VAL40}, NewProperty{property=prop2,value=VAL50}],code=<null>},uploadingUserEmailOrNull=<null>,uploadingUserIdOrNull=test}]\n"
+                        + "The following subfolders were in the uploaded folder, but were not mentioned in the control file:\n"
+                        + "ds2,ds3\n";
+        checkAppenderContent(logText, folderName);
 
         context.assertIsSatisfied();
     }
@@ -145,7 +181,13 @@ public class SampleAndDatasetRegistrationHandlerTest extends AbstractFileSystemT
         setupUpdateSampleExistsExpectations("S3", false);
         setupRegisterSampleAndDataSetExpectations(externalData, newSampleMatcher, 3);
 
-        File workingCopy = createWorkingCopyOfTestFolder("basic-example");
+        final String folderName = "basic-example";
+        final RecordingMatcher<DataHandler> attachmentMatcher = new RecordingMatcher<DataHandler>();
+        final RecordingMatcher<EMailAddress[]> addressesMatcher =
+                new RecordingMatcher<EMailAddress[]>();
+        setupSuccessEmailExpectations(attachmentMatcher, addressesMatcher, folderName);
+
+        File workingCopy = createWorkingCopyOfTestFolder(folderName);
 
         initializeDefaultDataSetHandler();
         handler.handleDataSet(workingCopy);
@@ -155,7 +197,7 @@ public class SampleAndDatasetRegistrationHandlerTest extends AbstractFileSystemT
                         + "Registered sample/data set pair SampleDataSetPair[sampleIdentifier=/MYSPACE/S1,sampleProperties={prop1: VAL10,prop2: VAL20,prop3: VAL30},dataSetInformation=DataSetInformation{sampleCode=<null>,properties={},dataSetType=MY_DATA_SET_TYPE,instanceUUID=<null>,instanceCode=<null>,spaceCode=<null>,experimentIdentifier=/MYSPACE/MYPROJ/EXP1,isCompleteFlag=U,extractableData=ExtractableData{productionDate=<null>,dataProducerCode=<null>,parentDataSetCodes=[],dataSetProperties=[NewProperty{property=prop1,value=VAL40}, NewProperty{property=prop2,value=VAL50}],code=<null>},uploadingUserEmailOrNull=<null>,uploadingUserIdOrNull=test}]\n"
                         + "Registered sample/data set pair SampleDataSetPair[sampleIdentifier=/MYSPACE/S2,sampleProperties={prop1: VAL11,prop2: VAL21,prop3: VAL31},dataSetInformation=DataSetInformation{sampleCode=<null>,properties={},dataSetType=MY_DATA_SET_TYPE,instanceUUID=<null>,instanceCode=<null>,spaceCode=<null>,experimentIdentifier=/MYSPACE/MYPROJ/EXP2,isCompleteFlag=U,extractableData=ExtractableData{productionDate=<null>,dataProducerCode=<null>,parentDataSetCodes=[],dataSetProperties=[NewProperty{property=prop1,value=VAL41}, NewProperty{property=prop2,value=VAL51}],code=<null>},uploadingUserEmailOrNull=<null>,uploadingUserIdOrNull=test}]\n"
                         + "Registered sample/data set pair SampleDataSetPair[sampleIdentifier=/MYSPACE/S3,sampleProperties={prop1: VAL12,prop2: VAL22,prop3: VAL32},dataSetInformation=DataSetInformation{sampleCode=<null>,properties={},dataSetType=MY_DATA_SET_TYPE,instanceUUID=<null>,instanceCode=<null>,spaceCode=<null>,experimentIdentifier=/MYSPACE/MYPROJ/EXP3,isCompleteFlag=U,extractableData=ExtractableData{productionDate=<null>,dataProducerCode=<null>,parentDataSetCodes=[],dataSetProperties=[NewProperty{property=prop1,value=VAL42}, NewProperty{property=prop2,value=VAL52}],code=<null>},uploadingUserEmailOrNull=<null>,uploadingUserIdOrNull=test}]";
-        checkAppenderContent(logText, "basic-example");
+        checkAppenderContent(logText, folderName);
 
         context.assertIsSatisfied();
     }
@@ -174,7 +216,13 @@ public class SampleAndDatasetRegistrationHandlerTest extends AbstractFileSystemT
         setupUpdateSampleExistsExpectations("S3", false);
         setupRegisterSampleAndDataSetExpectations(externalData, newSampleMatcher, 3);
 
-        File workingCopy = createWorkingCopyOfTestFolder("empty-lines");
+        final String folderName = "empty-lines";
+        final RecordingMatcher<DataHandler> attachmentMatcher = new RecordingMatcher<DataHandler>();
+        final RecordingMatcher<EMailAddress[]> addressesMatcher =
+                new RecordingMatcher<EMailAddress[]>();
+        setupSuccessEmailExpectations(attachmentMatcher, addressesMatcher, folderName);
+
+        File workingCopy = createWorkingCopyOfTestFolder(folderName);
 
         initializeDefaultDataSetHandler();
         handler.handleDataSet(workingCopy);
@@ -184,7 +232,7 @@ public class SampleAndDatasetRegistrationHandlerTest extends AbstractFileSystemT
                         + "Registered sample/data set pair SampleDataSetPair[sampleIdentifier=/MYSPACE/S1,sampleProperties={prop1: VAL10,prop2: VAL20,prop3: VAL30},dataSetInformation=DataSetInformation{sampleCode=<null>,properties={},dataSetType=MY_DATA_SET_TYPE,instanceUUID=<null>,instanceCode=<null>,spaceCode=<null>,experimentIdentifier=/MYSPACE/MYPROJ/EXP1,isCompleteFlag=U,extractableData=ExtractableData{productionDate=<null>,dataProducerCode=<null>,parentDataSetCodes=[],dataSetProperties=[NewProperty{property=prop1,value=VAL40}, NewProperty{property=prop2,value=VAL50}],code=<null>},uploadingUserEmailOrNull=<null>,uploadingUserIdOrNull=test}]\n"
                         + "Registered sample/data set pair SampleDataSetPair[sampleIdentifier=/MYSPACE/S2,sampleProperties={prop1: VAL11,prop2: VAL21,prop3: VAL31},dataSetInformation=DataSetInformation{sampleCode=<null>,properties={},dataSetType=MY_DATA_SET_TYPE,instanceUUID=<null>,instanceCode=<null>,spaceCode=<null>,experimentIdentifier=/MYSPACE/MYPROJ/EXP2,isCompleteFlag=U,extractableData=ExtractableData{productionDate=<null>,dataProducerCode=<null>,parentDataSetCodes=[],dataSetProperties=[NewProperty{property=prop1,value=VAL41}, NewProperty{property=prop2,value=VAL51}],code=<null>},uploadingUserEmailOrNull=<null>,uploadingUserIdOrNull=test}]\n"
                         + "Registered sample/data set pair SampleDataSetPair[sampleIdentifier=/MYSPACE/S3,sampleProperties={prop1: VAL12,prop2: VAL22,prop3: VAL32},dataSetInformation=DataSetInformation{sampleCode=<null>,properties={},dataSetType=MY_DATA_SET_TYPE,instanceUUID=<null>,instanceCode=<null>,spaceCode=<null>,experimentIdentifier=/MYSPACE/MYPROJ/EXP3,isCompleteFlag=U,extractableData=ExtractableData{productionDate=<null>,dataProducerCode=<null>,parentDataSetCodes=[],dataSetProperties=[NewProperty{property=prop1,value=VAL42}, NewProperty{property=prop2,value=VAL52}],code=<null>},uploadingUserEmailOrNull=<null>,uploadingUserIdOrNull=test}]";
-        checkAppenderContent(logText, "empty-lines");
+        checkAppenderContent(logText, folderName);
 
         context.assertIsSatisfied();
     }
@@ -204,7 +252,13 @@ public class SampleAndDatasetRegistrationHandlerTest extends AbstractFileSystemT
         setupUpdateSampleExistsExpectations("S3", true);
         setupUpdateSampleAndRegisterDataSetExpectations(externalData, sampleUpdatesMatcher, 3);
 
-        File workingCopy = createWorkingCopyOfTestFolder("basic-example");
+        final String folderName = "basic-example";
+        final RecordingMatcher<DataHandler> attachmentMatcher = new RecordingMatcher<DataHandler>();
+        final RecordingMatcher<EMailAddress[]> addressesMatcher =
+                new RecordingMatcher<EMailAddress[]>();
+        setupSuccessEmailExpectations(attachmentMatcher, addressesMatcher, folderName);
+
+        File workingCopy = createWorkingCopyOfTestFolder(folderName);
 
         initializeDefaultDataSetHandler();
         handler.handleDataSet(workingCopy);
@@ -214,7 +268,7 @@ public class SampleAndDatasetRegistrationHandlerTest extends AbstractFileSystemT
                         + "Updated sample, registered data set SampleDataSetPair[sampleIdentifier=/MYSPACE/S1,sampleProperties={prop1: VAL10,prop2: VAL20,prop3: VAL30},dataSetInformation=DataSetInformation{sampleCode=<null>,properties={},dataSetType=MY_DATA_SET_TYPE,instanceUUID=<null>,instanceCode=<null>,spaceCode=<null>,experimentIdentifier=/MYSPACE/MYPROJ/EXP1,isCompleteFlag=U,extractableData=ExtractableData{productionDate=<null>,dataProducerCode=<null>,parentDataSetCodes=[],dataSetProperties=[NewProperty{property=prop1,value=VAL40}, NewProperty{property=prop2,value=VAL50}],code=<null>},uploadingUserEmailOrNull=<null>,uploadingUserIdOrNull=test}]\n"
                         + "Updated sample, registered data set SampleDataSetPair[sampleIdentifier=/MYSPACE/S2,sampleProperties={prop1: VAL11,prop2: VAL21,prop3: VAL31},dataSetInformation=DataSetInformation{sampleCode=<null>,properties={},dataSetType=MY_DATA_SET_TYPE,instanceUUID=<null>,instanceCode=<null>,spaceCode=<null>,experimentIdentifier=/MYSPACE/MYPROJ/EXP2,isCompleteFlag=U,extractableData=ExtractableData{productionDate=<null>,dataProducerCode=<null>,parentDataSetCodes=[],dataSetProperties=[NewProperty{property=prop1,value=VAL41}, NewProperty{property=prop2,value=VAL51}],code=<null>},uploadingUserEmailOrNull=<null>,uploadingUserIdOrNull=test}]\n"
                         + "Updated sample, registered data set SampleDataSetPair[sampleIdentifier=/MYSPACE/S3,sampleProperties={prop1: VAL12,prop2: VAL22,prop3: VAL32},dataSetInformation=DataSetInformation{sampleCode=<null>,properties={},dataSetType=MY_DATA_SET_TYPE,instanceUUID=<null>,instanceCode=<null>,spaceCode=<null>,experimentIdentifier=/MYSPACE/MYPROJ/EXP3,isCompleteFlag=U,extractableData=ExtractableData{productionDate=<null>,dataProducerCode=<null>,parentDataSetCodes=[],dataSetProperties=[NewProperty{property=prop1,value=VAL42}, NewProperty{property=prop2,value=VAL52}],code=<null>},uploadingUserEmailOrNull=<null>,uploadingUserIdOrNull=test}]";
-        checkAppenderContent(logText, "basic-example");
+        checkAppenderContent(logText, folderName);
 
         context.assertIsSatisfied();
     }
@@ -236,7 +290,14 @@ public class SampleAndDatasetRegistrationHandlerTest extends AbstractFileSystemT
         setupRegisterSampleAndDataSetExpectations(externalData, newSampleMatcher, 2);
         setupUpdateSampleAndRegisterDataSetExpectations(externalData, sampleUpdatesMatcher, 1);
 
-        File workingCopy = createWorkingCopyOfTestFolder("basic-example");
+        final String folderName = "basic-example";
+
+        final RecordingMatcher<DataHandler> attachmentMatcher = new RecordingMatcher<DataHandler>();
+        final RecordingMatcher<EMailAddress[]> addressesMatcher =
+                new RecordingMatcher<EMailAddress[]>();
+        setupSuccessEmailExpectations(attachmentMatcher, addressesMatcher, folderName);
+
+        File workingCopy = createWorkingCopyOfTestFolder(folderName);
 
         initializeDefaultDataSetHandler();
         handler.handleDataSet(workingCopy);
@@ -246,7 +307,7 @@ public class SampleAndDatasetRegistrationHandlerTest extends AbstractFileSystemT
                         + "Registered sample/data set pair SampleDataSetPair[sampleIdentifier=/MYSPACE/S1,sampleProperties={prop1: VAL10,prop2: VAL20,prop3: VAL30},dataSetInformation=DataSetInformation{sampleCode=<null>,properties={},dataSetType=MY_DATA_SET_TYPE,instanceUUID=<null>,instanceCode=<null>,spaceCode=<null>,experimentIdentifier=/MYSPACE/MYPROJ/EXP1,isCompleteFlag=U,extractableData=ExtractableData{productionDate=<null>,dataProducerCode=<null>,parentDataSetCodes=[],dataSetProperties=[NewProperty{property=prop1,value=VAL40}, NewProperty{property=prop2,value=VAL50}],code=<null>},uploadingUserEmailOrNull=<null>,uploadingUserIdOrNull=test}]\n"
                         + "Updated sample, registered data set SampleDataSetPair[sampleIdentifier=/MYSPACE/S2,sampleProperties={prop1: VAL11,prop2: VAL21,prop3: VAL31},dataSetInformation=DataSetInformation{sampleCode=<null>,properties={},dataSetType=MY_DATA_SET_TYPE,instanceUUID=<null>,instanceCode=<null>,spaceCode=<null>,experimentIdentifier=/MYSPACE/MYPROJ/EXP2,isCompleteFlag=U,extractableData=ExtractableData{productionDate=<null>,dataProducerCode=<null>,parentDataSetCodes=[],dataSetProperties=[NewProperty{property=prop1,value=VAL41}, NewProperty{property=prop2,value=VAL51}],code=<null>},uploadingUserEmailOrNull=<null>,uploadingUserIdOrNull=test}]\n"
                         + "Registered sample/data set pair SampleDataSetPair[sampleIdentifier=/MYSPACE/S3,sampleProperties={prop1: VAL12,prop2: VAL22,prop3: VAL32},dataSetInformation=DataSetInformation{sampleCode=<null>,properties={},dataSetType=MY_DATA_SET_TYPE,instanceUUID=<null>,instanceCode=<null>,spaceCode=<null>,experimentIdentifier=/MYSPACE/MYPROJ/EXP3,isCompleteFlag=U,extractableData=ExtractableData{productionDate=<null>,dataProducerCode=<null>,parentDataSetCodes=[],dataSetProperties=[NewProperty{property=prop1,value=VAL42}, NewProperty{property=prop2,value=VAL52}],code=<null>},uploadingUserEmailOrNull=<null>,uploadingUserIdOrNull=test}]";
-        checkAppenderContent(logText, "basic-example");
+        checkAppenderContent(logText, folderName);
 
         context.assertIsSatisfied();
     }
@@ -266,8 +327,8 @@ public class SampleAndDatasetRegistrationHandlerTest extends AbstractFileSystemT
         handler.handleDataSet(workingCopy);
 
         String errorText =
-                "Folder (no-control) for sample/dataset registration contains no control files matching the configured pattern: .*.[Tt][Ss][Vv].\n"
-                        + "Folder contents:\n" + "\t.svn\n" + "\tnot-a-tsv.txt\n";
+                "Folder (no-control) for sample/dataset registration contains no control files matching the configured pattern: .*\\.[Tt][Ss][Vv].\n"
+                        + "Folder contents:\n" + "\tnot-a-tsv.txt\n";
 
         // Check the log
         checkAppenderContent(errorText, "no-control");
@@ -292,8 +353,8 @@ public class SampleAndDatasetRegistrationHandlerTest extends AbstractFileSystemT
         handler.handleDataSet(workingCopy);
 
         String errorText =
-                "Folder (empty-folder) for sample/dataset registration contains no control files matching the configured pattern: .*.[Tt][Ss][Vv].\n"
-                        + "Folder contents:\n" + "\t.svn\n";
+                "Folder (empty-folder) for sample/dataset registration contains no control files matching the configured pattern: .*\\.[Tt][Ss][Vv].\n"
+                        + "Folder contents:\n";
         // Check the log
         checkAppenderContent(errorText, "empty-folder");
 
@@ -339,6 +400,7 @@ public class SampleAndDatasetRegistrationHandlerTest extends AbstractFileSystemT
 
         File workingCopy = new File(workingDirectory, folderName);
         FileOperations.getInstance().copy(dataSetFile, workingCopy);
+        FileOperations.getInstance().deleteRecursively(new File(workingCopy, ".svn"));
         return workingCopy;
     }
 
@@ -356,7 +418,7 @@ public class SampleAndDatasetRegistrationHandlerTest extends AbstractFileSystemT
                     will(returnValue(Collections.singletonList(admin)));
                 }
             });
-        setupErrorEmailExpectations(
+        setupEmailExpectations(
                 attachmentMatcher,
                 addressesMatcher,
                 "Sample / Data Set Registration Error -- targets/unit-test-wd/ch.systemsx.cisd.etlserver.entityregistration.SampleAndDatasetRegistrationHandlerTest/"
@@ -364,14 +426,14 @@ public class SampleAndDatasetRegistrationHandlerTest extends AbstractFileSystemT
                 "When trying to process the files in the folder, targets/unit-test-wd/ch.systemsx.cisd.etlserver.entityregistration.SampleAndDatasetRegistrationHandlerTest/"
                         + folderName
                         + ", errors were encountered. These errors are detailed in the attachment.",
-                folderName);
+                "errors.txt", folderName);
     }
 
     private void setupPartialSuccessErrorEmailExpectations(
             final RecordingMatcher<DataHandler> attachmentMatcher,
             final RecordingMatcher<EMailAddress[]> addressesMatcher, final String folderName)
     {
-        setupErrorEmailExpectations(
+        setupEmailExpectations(
                 attachmentMatcher,
                 addressesMatcher,
                 "Sample / Data Set Registration Error -- targets/unit-test-wd/ch.systemsx.cisd.etlserver.entityregistration.SampleAndDatasetRegistrationHandlerTest/"
@@ -379,18 +441,32 @@ public class SampleAndDatasetRegistrationHandlerTest extends AbstractFileSystemT
                 "Not all samples and data sets specified in the control file, targets/unit-test-wd/ch.systemsx.cisd.etlserver.entityregistration.SampleAndDatasetRegistrationHandlerTest/"
                         + folderName
                         + "/control.tsv, could be registered / updated. The errors are detailed in the attachment. Each faulty line is reproduced, preceded by a comment explaining the cause of the error.",
+                "errors.txt", folderName);
+    }
+
+    private void setupSuccessEmailExpectations(
+            final RecordingMatcher<DataHandler> attachmentMatcher,
+            final RecordingMatcher<EMailAddress[]> addressesMatcher, final String folderName)
+    {
+        setupEmailExpectations(
+                attachmentMatcher,
+                addressesMatcher,
+                "Sample / Data Set Registration Succeeded -- targets/unit-test-wd/ch.systemsx.cisd.etlserver.entityregistration.SampleAndDatasetRegistrationHandlerTest/"
+                        + folderName + "/control.tsv",
+                "The registration/update of samples and the registration of data sets was successful specified in the control file, targets/unit-test-wd/ch.systemsx.cisd.etlserver.entityregistration.SampleAndDatasetRegistrationHandlerTest/"
+                        + folderName + "/control.tsv, was successful.", "registered.txt",
                 folderName);
     }
 
-    private void setupErrorEmailExpectations(final RecordingMatcher<DataHandler> attachmentMatcher,
+    private void setupEmailExpectations(final RecordingMatcher<DataHandler> attachmentMatcher,
             final RecordingMatcher<EMailAddress[]> addressesMatcher, final String emailSubject,
-            final String emailBody, final String folderName)
+            final String emailBody, final String attachmentName, final String folderName)
     {
         context.checking(new Expectations()
             {
                 {
                     one(mailClient).sendEmailMessageWithAttachment(with(emailSubject),
-                            with(emailBody), with("errors.txt"), with(attachmentMatcher),
+                            with(emailBody), with(attachmentName), with(attachmentMatcher),
                             with(new IsNull<EMailAddress>()), with(new IsNull<EMailAddress>()),
                             with(addressesMatcher));
                 }
@@ -427,7 +503,7 @@ public class SampleAndDatasetRegistrationHandlerTest extends AbstractFileSystemT
                         ExperimentIdentifier experimentId =
                                 new ExperimentIdentifier(DatabaseInstanceIdentifier.HOME,
                                         SPACE_CODE, "MYPROJ", "EXP" + i);
-                        oneOf(openbisService).tryToGetExperiment(experimentId);
+                        allowing(openbisService).tryToGetExperiment(experimentId);
                         Experiment exp = new Experiment();
                         exp.setIdentifier(experimentId.toString());
                         will(returnValue(exp));
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/entityregistration/test-data/not-all-subfolders-mentioned/control.tsv b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/entityregistration/test-data/not-all-subfolders-mentioned/control.tsv
index a8d70ccf592..7c75791bb13 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/entityregistration/test-data/not-all-subfolders-mentioned/control.tsv
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/entityregistration/test-data/not-all-subfolders-mentioned/control.tsv
@@ -7,5 +7,4 @@
 # Data
 S_identifier	S_experiment	S_PROP1	S_PROP2	S_PROP3	D_file_type	D_PROP1	D_PROP2	FOLDER
 /MYSPACE/S1	/MYSPACE/MYPROJ/EXP1	VAL10	VAL20	VAL30	FILE_TYPE	VAL40	VAL50	ds1/
-/MYSPACE/S2	/MYSPACE/MYPROJ/EXP2	VAL11	VAL21	VAL31	FILE_TYPE	VAL41	VAL51	ds2/
-/MYSPACE/S3	/MYSPACE/MYPROJ/EXP3	VAL12	VAL22	VAL32	FILE_TYPE	VAL42	VAL52	ds3/
+
-- 
GitLab