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 0000000000000000000000000000000000000000..0deef8150bd7d108ea3d3fdd9aed21071cc796f8 --- /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 849cee06646aec971fa63f1d41922ed8f6ca7f9e..5f43a13b6698501657a64c716800d52c362390bd 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 a33aa4d4c06b1de7533f49ddd2749c78c133bcd2..e384ae0df02c4fb590fcaf913b5f337d3721dd1a 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 4919eb8580f53026bdcbe8484152e8181bd8c088..453e1fc896a71219f8f8d6cf5ed635dc5311c873 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 66eecad09ae115c9918b4a0199faaea2c010f321..7c6c9215d3f7bf09813da98c38e082fca92662f4 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 3897a1727be7e6eca66beb9e6ca9aeb8fcc1be67..305ce74230fef499b7d6a057e42bf5126b1985d3 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 a8d70ccf5927b1d1155673eb9c0f4d2722d2d386..7c75791bb13500e9f02aa6bf3df4d88ba5e74cc3 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/ +