diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/PropertiesBasedETLServerPlugin.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/PropertiesBasedETLServerPlugin.java index ad2a7dc1266c77de74b3fede7a8e590e199d6ab9..63a67da96f306722e1329a178eb51464bddd667d 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/PropertiesBasedETLServerPlugin.java +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/PropertiesBasedETLServerPlugin.java @@ -155,7 +155,7 @@ public class PropertiesBasedETLServerPlugin extends ETLServerPlugin } else { return create(IDataSetHandler.class, properties, IDataSetHandler.DATASET_HANDLER_KEY, - true, primaryDataSetHandler, openbisService); + false, primaryDataSetHandler, openbisService); } } diff --git a/rtd_yeastx/.classpath b/rtd_yeastx/.classpath index 86c092fe801fe1fecb81938a071c9698e921776a..5ac63c6d1c8eb111b16a2d054ef83e3b323411cd 100644 --- a/rtd_yeastx/.classpath +++ b/rtd_yeastx/.classpath @@ -17,5 +17,6 @@ <classpathentry kind="lib" path="/libraries/log4j/log4j.jar" sourcepath="/libraries/log4j/src.zip"/> <classpathentry kind="lib" path="/libraries/spring/spring-beans.jar"/> <classpathentry kind="lib" path="/libraries/cisd-base/cisd-base-test.jar" sourcepath="/libraries/cisd-base/cisd-base-src.zip"/> + <classpathentry kind="lib" path="/libraries/mail/mail.jar"/> <classpathentry kind="output" path="targets/classes"/> </classpath> diff --git a/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/BatchDataSetHandler.java b/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/BatchDataSetHandler.java index 597ab729006d190fb9e435595b35b0e7e4ef6e0e..905330f4885612c24ddcfbd334ee9f79d6301aff 100644 --- a/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/BatchDataSetHandler.java +++ b/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/BatchDataSetHandler.java @@ -28,9 +28,13 @@ import java.util.Set; import ch.systemsx.cisd.common.collections.TableMap; import ch.systemsx.cisd.common.filesystem.FileUtilities; +import ch.systemsx.cisd.common.mail.IMailClient; +import ch.systemsx.cisd.common.mail.MailClient; +import ch.systemsx.cisd.common.utilities.ExtendedProperties; import ch.systemsx.cisd.etlserver.IDataSetHandler; import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService; import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation; +import ch.systemsx.cisd.yeastx.etl.DatasetMappingUtil.DataSetMappingInformationFile; /** * {@link IDataSetHandler} implementation which for each dataset directory reads all the files @@ -42,46 +46,81 @@ public class BatchDataSetHandler implements IDataSetHandler { private final IDataSetHandler delegator; + private final IMailClient mailClient; + private final DatasetMappingResolver datasetMappingResolver; - public BatchDataSetHandler(Properties properties, IDataSetHandler delegator, + public BatchDataSetHandler(Properties parentProperties, IDataSetHandler delegator, IEncapsulatedOpenBISService openbisService) { this.delegator = delegator; - this.datasetMappingResolver = new DatasetMappingResolver(properties, openbisService); + this.mailClient = new MailClient(parentProperties); + this.datasetMappingResolver = + new DatasetMappingResolver(getSpecificProperties(parentProperties), openbisService); + } + + private static Properties getSpecificProperties(Properties properties) + { + return ExtendedProperties.getSubset(properties, IDataSetHandler.DATASET_HANDLER_KEY + '.', + true); } public List<DataSetInformation> handleDataSet(File datasetsParentDir) { - List<DataSetInformation> processedDatasetFiles = new ArrayList<DataSetInformation>(); if (canBatchBeProcessed(datasetsParentDir) == false) { - return processedDatasetFiles; + return createEmptyResult(); } LogUtils log = new LogUtils(datasetsParentDir); - TableMap<String, DataSetMappingInformation> datasetsMapping = - DatasetMappingUtil.tryGetDatasetsMapping(datasetsParentDir); - if (datasetsMapping == null) + DataSetMappingInformationFile datasetMappingFile = + DatasetMappingUtil.tryGetDatasetsMapping(datasetsParentDir, log); + if (datasetMappingFile == null || datasetMappingFile.tryGetMappings() == null) { touchErrorMarkerFile(datasetsParentDir, log); - return processedDatasetFiles; + sendNotificationsIfNecessary(log, tryGetEmail(datasetMappingFile)); + return createEmptyResult(); } + return processDatasets(datasetsParentDir, log, datasetMappingFile.tryGetMappings(), + datasetMappingFile.getNotificationEmail()); + } + + private List<DataSetInformation> processDatasets(File datasetsParentDir, LogUtils log, + TableMap<String, DataSetMappingInformation> mappings, String notificationEmail) + { + List<DataSetInformation> processedDatasetFiles = createEmptyResult(); + Set<String> processedFiles = new HashSet<String>(); List<File> files = listAll(datasetsParentDir); for (File file : files) { - if (canDatasetBeProcessed(file, datasetsMapping, log)) + if (canDatasetBeProcessed(file, mappings, log)) { processedDatasetFiles.addAll(delegator.handleDataSet(file)); processedFiles.add(file.getName().toLowerCase()); } } - clean(datasetsParentDir, processedFiles, log, datasetsMapping.values().size()); - log.sendNotificationsIfNecessary(); + clean(datasetsParentDir, processedFiles, log, mappings.values().size()); + sendNotificationsIfNecessary(log, notificationEmail); return processedDatasetFiles; } - private boolean canBatchBeProcessed(File parentDir) + private void sendNotificationsIfNecessary(LogUtils log, String email) + { + log.sendNotificationsIfNecessary(mailClient, email); + } + + private static String tryGetEmail(DataSetMappingInformationFile datasetMappingFileOrNull) + { + return datasetMappingFileOrNull == null ? null : datasetMappingFileOrNull + .getNotificationEmail(); + } + + private static ArrayList<DataSetInformation> createEmptyResult() + { + return new ArrayList<DataSetInformation>(); + } + + private static boolean canBatchBeProcessed(File parentDir) { if (parentDir.isDirectory() == false) { @@ -109,18 +148,13 @@ public class BatchDataSetHandler implements IDataSetHandler return new File(datasetsParentDir, ERROR_MARKER_FILE).isFile(); } - private void cleanMappingFile(File datasetsParentDir, Set<String> processedFiles, LogUtils log) + private static void cleanMappingFile(File datasetsParentDir, Set<String> processedFiles, + LogUtils log) { - try - { - DatasetMappingUtil.cleanMappingFile(datasetsParentDir, processedFiles); - } catch (IOException ex) - { - log.userError("Cannot clean dataset mappings file: " + ex.getMessage()); - } + DatasetMappingUtil.cleanMappingFile(datasetsParentDir, processedFiles, log); } - private void clean(File datasetsParentDir, Set<String> processedFiles, LogUtils log, + private static void clean(File datasetsParentDir, Set<String> processedFiles, LogUtils log, int datasetMappingsNumber) { cleanMappingFile(datasetsParentDir, processedFiles, log); @@ -155,13 +189,13 @@ public class BatchDataSetHandler implements IDataSetHandler .getPath()); } else { - log.userWarning( + log.warning( "Correct the errors and delete the '%s' file to start processing again.", ERROR_MARKER_FILE); } } - private void cleanDatasetsDir(File datasetsParentDir) + private static void cleanDatasetsDir(File datasetsParentDir) { LogUtils.deleteUserLog(datasetsParentDir); DatasetMappingUtil.deleteMappingFile(datasetsParentDir); @@ -186,7 +220,7 @@ public class BatchDataSetHandler implements IDataSetHandler return datasetMappingResolver.isMappingCorrect(mapping, log); } - private void deleteEmptyDir(File dir) + private static void deleteEmptyDir(File dir) { boolean ok = dir.delete(); if (ok == false) @@ -197,7 +231,7 @@ public class BatchDataSetHandler implements IDataSetHandler } } - private boolean hasNoPotentialDatasetFiles(File dir) + private static boolean hasNoPotentialDatasetFiles(File dir) { List<File> files = listAll(dir); int datasetsCounter = files.size(); @@ -211,7 +245,7 @@ public class BatchDataSetHandler implements IDataSetHandler return datasetsCounter == 0; } - private List<File> listAll(File dataSet) + private static List<File> listAll(File dataSet) { return FileUtilities.listFilesAndDirectories(dataSet, false, null); } diff --git a/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/BatchDataSetInfoExtractor.java b/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/BatchDataSetInfoExtractor.java index 8a1a974929dc3599e2625dbd9d03d1a7765d9948..62779d1c2ad7d608435a509e7f4ecb7d72f097fa 100644 --- a/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/BatchDataSetInfoExtractor.java +++ b/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/BatchDataSetInfoExtractor.java @@ -42,15 +42,15 @@ public class BatchDataSetInfoExtractor implements IDataSetInfoExtractor IEncapsulatedOpenBISService openbisService) throws UserFailureException, EnvironmentFailureException { + LogUtils log = new LogUtils(incomingDataSetPath.getParentFile()); DataSetMappingInformation plainInfo = - DatasetMappingUtil.tryGetDatasetMapping(incomingDataSetPath); + DatasetMappingUtil.tryGetDatasetMapping(incomingDataSetPath, log); if (plainInfo != null) { DataSetInformationYeastX info = new DataSetInformationYeastX(); info.setComplete(true); info.setDataSetProperties(plainInfo.getProperties()); - String sampleCode = - getSampleCode(plainInfo, openbisService, incomingDataSetPath.getParentFile()); + String sampleCode = getSampleCode(plainInfo, openbisService, log); info.setSampleCode(sampleCode); info.setGroupCode(plainInfo.getGroupCode()); MLConversionType conversion = getConversion(plainInfo.getConversion()); @@ -76,11 +76,11 @@ public class BatchDataSetInfoExtractor implements IDataSetInfoExtractor } private String getSampleCode(DataSetMappingInformation mapping, - IEncapsulatedOpenBISService openbisService, File logDir) + IEncapsulatedOpenBISService openbisService, LogUtils log) { String sampleCode = new DatasetMappingResolver(properties, openbisService).tryFigureSampleCode(mapping, - new LogUtils(logDir)); + log); if (sampleCode == null) { // should not happen, the dataset handler should skip datasets with incorrect mapping diff --git a/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/DataSetMappingInformationParser.java b/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/DataSetMappingInformationParser.java index f87a74cb737aae6b8a7f993c69b3044cc3a553de..03003ec3d747ef3da9d0c10424dd934f66b328df 100644 --- a/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/DataSetMappingInformationParser.java +++ b/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/DataSetMappingInformationParser.java @@ -36,7 +36,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.NewProperty; */ class DataSetMappingInformationParser { - public static List<DataSetMappingInformation> tryParse(File mappingFile) + public static List<DataSetMappingInformation> tryParse(File mappingFile, LogUtils log) { TabFileLoader<DataSetMappingInformation> tabFileLoader = new TabFileLoader<DataSetMappingInformation>( @@ -54,16 +54,16 @@ class DataSetMappingInformationParser return tabFileLoader.load(mappingFile); } catch (final IllegalArgumentException e) { - logParsingError(e, mappingFile); + logParsingError(log, e, mappingFile); return null; } catch (final Exception e) { - logParsingError(e, mappingFile); + logParsingError(log, e, mappingFile); return null; } } - private static void logParsingError(Exception e, File mappingFile) + private static void logParsingError(LogUtils log, Exception e, File mappingFile) { Throwable cause = e.getCause(); String causeMsg = ""; @@ -71,10 +71,8 @@ class DataSetMappingInformationParser { causeMsg = "\nThe exception was caused by: " + cause.getMessage(); } - LogUtils.basicError(mappingFile.getParentFile(), - "The datasets cannot be processed because the mapping file '%s' has incorrect format." - + " The following exception occured:\n%s%s", mappingFile.getPath(), e - .getMessage(), causeMsg); + log.mappingFileError(mappingFile, "the mapping file '%s' has incorrect format." + + " The problem is:\n%s%s", mappingFile.getPath(), e.getMessage(), causeMsg); } private static final class NewPropertyParserObjectFactory extends diff --git a/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/DatasetMappingResolver.java b/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/DatasetMappingResolver.java index be8d4350427a7edf0fcd98227472f7de74724e48..7002768232e42737587a71c5513cd0525a4c3215 100644 --- a/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/DatasetMappingResolver.java +++ b/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/DatasetMappingResolver.java @@ -101,7 +101,7 @@ class DatasetMappingResolver samples = openbisService.listSamplesByCriteria(criteria); } catch (UserFailureException e) { - logMappingError(mapping, log, e.getMessage()); + log.datasetMappingError(mapping, e.getMessage()); return null; } if (samples.size() == 1) @@ -109,7 +109,7 @@ class DatasetMappingResolver return samples.get(0); } else if (samples.size() == 0) { - logMappingError(mapping, log, "there is no sample which matches the criteria <" + log.datasetMappingError(mapping, "there is no sample which matches the criteria <" + criteria + ">"); return null; } else @@ -119,7 +119,7 @@ class DatasetMappingResolver "there should be exacty one sample which matches the criteria '%s', but %d of them were found." + " Consider using the unique sample code.", criteria, samples .size()); - logMappingError(mapping, log, errMsg); + log.datasetMappingError(mapping, errMsg); return null; } } @@ -160,25 +160,26 @@ class DatasetMappingResolver MLConversionType conversion = MLConversionType.tryCreate(conversionText); if (conversion == null) { - log.userError(String.format( - "Error for file '%s'. Unexpected value '%s' in 'conversion' column. " - + "Leave the column empty or use one of the allowed values: %s.", - mapping.getFileName(), conversionText, CollectionUtils.abbreviate( - MLConversionType.values(), MLConversionType.values().length))); + String availableConvTypes = + CollectionUtils.abbreviate(MLConversionType.values(), + MLConversionType.values().length); + log.datasetMappingError(mapping, "unexpected value '%s' in 'conversion' column. " + + "Leave the column empty or use one of the allowed values: %s.", + conversionText, availableConvTypes); return false; } boolean conversionRequired = isConversionRequired(mapping); if (conversion == MLConversionType.NONE && conversionRequired) { - log.userError("Error for file '%s'. Conversion column cannot be empty " - + "for this type of file.", mapping.getFileName()); + log.datasetMappingError(mapping, "conversion column cannot be empty " + + "for this type of file."); return false; } if (conversion != MLConversionType.NONE && conversionRequired == false) { - log.userError("Error for file '%s'. Conversion column must be empty " - + "for this type of file.", mapping.getFileName()); + log.datasetMappingError(mapping, "conversion column must be empty " + + "for this type of file."); return false; } return true; @@ -197,8 +198,8 @@ class DatasetMappingResolver ExperimentPE experiment = tryFigureExperiment(sampleCode, mapping, log); if (experiment == null) { - logMappingError(mapping, log, String.format("sample with the code '%s' does not exist" - + " or is not connected to any experiment", sampleCode)); + log.datasetMappingError(mapping, "sample with the code '%s' does not exist" + + " or is not connected to any experiment", sampleCode); return false; } return true; @@ -213,7 +214,8 @@ class DatasetMappingResolver return openbisService.getBaseExperiment(sampleIdentifier); } catch (UserFailureException e) { - log.userError("Error when checking if sample '%s' belongs to an experiment: %s", + log.datasetMappingError(mapping, + "error when checking if sample '%s' belongs to an experiment: %s", sampleIdentifier, e.getMessage()); return null; } @@ -230,29 +232,24 @@ class DatasetMappingResolver { if ((mapping.getExperimentCode() == null) != (mapping.getProjectCode() == null)) { - logMappingError(mapping, log, - "experiment and project columns should be both empty or should be both filled."); + log + .datasetMappingError(mapping, + "experiment and project columns should be both empty or should be both filled."); return false; } if (propertyCodeOrNull == null && mapping.getExperimentCode() != null) { - logMappingError( - mapping, - log, - "openBis is not configured to use the sample label to identify the sample." - + " You can still identify the sample by the code (clear the experiment column in this case)." - + " You can also contact your administrator to change the server configuration and set the property type code which should be used."); + log + .datasetMappingError( + mapping, + "openBis is not configured to use the sample label to identify the sample." + + " You can still identify the sample by the code (clear the experiment column in this case)." + + " You can also contact your administrator to change the server configuration and set the property type code which should be used."); return false; } return true; } - private void logMappingError(DataSetMappingInformation mapping, LogUtils log, - String errorMessage) - { - log.userError("Mapping for file " + mapping.getFileName() + " is incorrect: " + errorMessage); - } - public static void adaptPropertyCodes(List<DataSetMappingInformation> list) { for (DataSetMappingInformation mapping : list) diff --git a/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/DatasetMappingUtil.java b/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/DatasetMappingUtil.java index 895b1eeda37e56de7b8e04c84bd934c43f135d7b..ad0e69e13321315030c368d3578b7bdf6aead966 100644 --- a/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/DatasetMappingUtil.java +++ b/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/DatasetMappingUtil.java @@ -48,7 +48,7 @@ class DatasetMappingUtil { "tsv" }; private static TableMap<String, DataSetMappingInformation> tryAsFileMap( - List<DataSetMappingInformation> list, File logDir) + List<DataSetMappingInformation> list, LogUtils log, File mappingFile) { IKeyExtractor<String, DataSetMappingInformation> extractor = new IKeyExtractor<String, DataSetMappingInformation>() @@ -64,23 +64,21 @@ class DatasetMappingUtil UniqueKeyViolationStrategy.ERROR); } catch (UniqueKeyViolationException e) { - // TODO 2009-06-16, Tomasz Pylak: use email to send notifications - LogUtils.basicError(logDir, - "The file '%s' appears more than once. No datasets will be processed.", e - .getInvalidKey()); + log.mappingFileError(mappingFile, "the file '%s' appears more than once.", e + .getInvalidKey()); return null; } } - public static DataSetMappingInformation tryGetDatasetMapping(File datasetFile) + public static DataSetMappingInformation tryGetDatasetMapping(File datasetFile, LogUtils log) { - TableMap<String, DataSetMappingInformation> datasetsMapping = - tryGetDatasetsMapping(datasetFile.getParentFile()); - if (datasetsMapping == null) + DataSetMappingInformationFile datasetsMappings = + tryGetDatasetsMapping(datasetFile.getParentFile(), log); + if (datasetsMappings == null || datasetsMappings.tryGetMappings() == null) { return null; } - return tryGetDatasetMapping(datasetFile, datasetsMapping); + return tryGetDatasetMapping(datasetFile, datasetsMappings.tryGetMappings()); } public static DataSetMappingInformation tryGetDatasetMapping(File datasetFile, @@ -91,7 +89,7 @@ class DatasetMappingUtil } // returns the content of the first line comment or null if there is no comment or it is empty - private static String tryGetFirstLineCommentContent(File mappingFile) + private static String tryGetFirstLineCommentContent(File mappingFile, LogUtils log) { List<String> lines; try @@ -99,7 +97,8 @@ class DatasetMappingUtil lines = readLines(mappingFile); } catch (IOException e) { - errorInFile(mappingFile, e.getMessage()); + Object[] arguments = {}; + log.mappingFileError(mappingFile, e.getMessage(), arguments); return null; } if (lines.size() == 0) @@ -120,60 +119,80 @@ class DatasetMappingUtil return firstLine; } - private static void errorInFile(File file, String message) - { - LogUtils.basicError(file.getParentFile(), "Error while reading the file '%s': %s", file - .getPath(), message); - } - - // returns email address from the first line of the mapping file or null if there is no emai or - // it is invalid - private static String tryGetEmail(File mappingFile) + /** + * @return email address from the first line of the mapping file or null if there is no emai or + * it is invalid. + */ + private static String tryGetEmail(File mappingFile, LogUtils log) { - String email = tryGetFirstLineCommentContent(mappingFile); + String email = tryGetFirstLineCommentContent(mappingFile, log); if (email == null) { - errorInFile(mappingFile, String.format( + log.mappingFileError(mappingFile, "There should be a '%s' character followed by an email address " + "in the first line of the file. " + "The email is needed to send messages about errors.", - TabFileLoader.COMMENT_PREFIX)); + TabFileLoader.COMMENT_PREFIX); return null; } if (email.contains("@") == false || email.contains(".") == false) { - errorInFile(mappingFile, String.format( - "The text '%s' does not seem to be an email address.", email)); + log.mappingFileError(mappingFile, + "The text '%s' does not seem to be an email address.", email); return null; } return email; } - public static TableMap<String/* file name in lowercase */, DataSetMappingInformation> tryGetDatasetsMapping( - File parentDir) + static class DataSetMappingInformationFile + { + private final TableMap<String/* file name in lowercase */, DataSetMappingInformation> mappingsOrNull; + + private final String notificationEmail; + + public DataSetMappingInformationFile( + TableMap<String, DataSetMappingInformation> mappingsOrNull, String notificationEmail) + { + this.mappingsOrNull = mappingsOrNull; + this.notificationEmail = notificationEmail; + } + + public TableMap<String, DataSetMappingInformation> tryGetMappings() + { + return mappingsOrNull; + } + + public String getNotificationEmail() + { + return notificationEmail; + } + } + + public static DataSetMappingInformationFile tryGetDatasetsMapping(File parentDir, LogUtils log) { File mappingFile = tryGetMappingFile(parentDir); if (mappingFile == null) { - LogUtils.basicWarn(parentDir, "Cannot process the directory '%s' " + log.error("No datasets from the directory '%s' can be processed " + "because a file with extension '%s' which contains dataset descriptions " - + "does not exist or there is more than one.", parentDir.getPath(), - CollectionUtils.abbreviate(MAPPING_FILE_EXTENSIONS, -1)); + + "does not exist or there is more than one file with taht extension.", + parentDir.getName(), CollectionUtils.abbreviate(MAPPING_FILE_EXTENSIONS, -1)); return null; } - String notificationEmail = tryGetEmail(mappingFile); + String notificationEmail = tryGetEmail(mappingFile, log); if (notificationEmail == null) { - return null; + return null; // email has to be provided always } + TableMap<String, DataSetMappingInformation> mappingsOrNull = null; List<DataSetMappingInformation> list = - DataSetMappingInformationParser.tryParse(mappingFile); - if (list == null) + DataSetMappingInformationParser.tryParse(mappingFile, log); + if (list != null) { - return null; + DatasetMappingResolver.adaptPropertyCodes(list); + mappingsOrNull = tryAsFileMap(list, log, mappingFile); } - DatasetMappingResolver.adaptPropertyCodes(list); - return tryAsFileMap(list, parentDir); + return new DataSetMappingInformationFile(mappingsOrNull, notificationEmail); } public static boolean isMappingFile(File file) @@ -228,25 +247,31 @@ class DatasetMappingUtil * * @param processedFiles files which should be removed from the mapping file */ - public static void cleanMappingFile(File parentDir, Set<String> processedFiles) - throws IOException + public static void cleanMappingFile(File parentDir, Set<String> processedFiles, LogUtils log) { File mappingFile = tryGetMappingFile(parentDir); if (mappingFile == null) { return; } - List<String> lines = readLines(mappingFile); - List<String> unprocessedLines = new ArrayList<String>(); - for (String line : lines) + try { - String tokens[] = line.trim().split("\t"); - if (tokens.length == 0 || processedFiles.contains(tokens[0].toLowerCase()) == false) + List<String> lines = readLines(mappingFile); + List<String> unprocessedLines = new ArrayList<String>(); + for (String line : lines) { - unprocessedLines.add(line); + String tokens[] = line.trim().split("\t"); + if (tokens.length == 0 || processedFiles.contains(tokens[0].toLowerCase()) == false) + { + unprocessedLines.add(line); + } } + IOUtils.writeLines(unprocessedLines, "\n", new FileOutputStream(mappingFile)); + } catch (IOException ex) + { + log.mappingFileError(mappingFile, "cannot clean dataset mappings file: " + + ex.getMessage()); } - IOUtils.writeLines(unprocessedLines, "\n", new FileOutputStream(mappingFile)); } @SuppressWarnings("unchecked") diff --git a/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/LogUtils.java b/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/LogUtils.java index c7664344e461f08433366ebf9079b4583e69fa8e..5dfe772b0853ce1f3337017ef94b6ec97226ef9e 100644 --- a/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/LogUtils.java +++ b/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/LogUtils.java @@ -28,12 +28,16 @@ import org.apache.log4j.Logger; import ch.systemsx.cisd.common.logging.LogCategory; import ch.systemsx.cisd.common.logging.LogFactory; +import ch.systemsx.cisd.common.mail.IMailClient; /** * @author Tomasz Pylak */ class LogUtils { + private static final String ERROR_NOTIFICATION_EMAIL_SUBJECT = + "[openBIS] problems with datasets upload in directory: %s"; + private static final Logger notificationLog = LogFactory.getLogger(LogCategory.NOTIFY, LogUtils.class); @@ -42,50 +46,97 @@ class LogUtils private final File loggingDir; - private final StringBuffer messageToSend; + private final StringBuffer errorMessages; public LogUtils(File loggingDir) { this.loggingDir = loggingDir; - this.messageToSend = new StringBuffer(); + this.errorMessages = new StringBuffer(); + } + + /** Logs errors about one dataset mapping. Uses user log file and email notification. */ + public void datasetMappingError(DataSetMappingInformation mapping, String errorMessageFormat, + Object... arguments) + { + String errorMessage = String.format(errorMessageFormat, arguments); + error("Cannot upload the file " + mapping.getFileName() + ": " + errorMessage); } - public void userError(String messageFormat, Object... arguments) + /** + * Logs an error about the syntax of the mapping file. Uses user log file and email + * notification. + */ + public void mappingFileError(File mappingFile, String messageFormat, Object... arguments) { - String message = basicError(loggingDir, messageFormat, arguments); - messageToSend.append(message); + String errorMessage = String.format(messageFormat, arguments); + error("No datasets could be processed, because there is an error in the mapping file " + + mappingFile.getName() + ": " + errorMessage); } - public void userWarning(String messageFormat, Object... arguments) + /** Uses user log file and email notification to log an error. */ + public void error(String messageFormat, Object... arguments) { - String message = basicWarn(loggingDir, messageFormat, arguments); - messageToSend.append(message); + logError(loggingDir, messageFormat, arguments); + appendNotification(messageFormat, arguments); } - public void sendNotificationsIfNecessary() + /** Uses user log file and email notification to log a warning. */ + public void warning(String messageFormat, Object... arguments) { - // TODO 2009-06-16, Tomasz Pylak: add email notification - if (messageToSend.length() > 0) + logWarning(loggingDir, messageFormat, arguments); + appendNotification(messageFormat, arguments); + } + + private void appendNotification(String messageFormat, Object... arguments) + { + errorMessages.append(String.format(messageFormat, arguments)); + errorMessages.append("\n"); + } + + /** has to be called at the end to send all notifications in one email */ + public void sendNotificationsIfNecessary(IMailClient mailClient, String notificationEmailOrNull) + { + if (notificationEmailOrNull != null && errorMessages.length() > 0) { - System.out.println("Email content: "); - System.out.println(messageToSend); + sendErrorMessage(mailClient, notificationEmailOrNull); } } + private void sendErrorMessage(IMailClient mailClient, String notificationEmail) + { + String subject = String.format(ERROR_NOTIFICATION_EMAIL_SUBJECT, loggingDir.getName()); + mailClient.sendMessage(subject, createErrorNotificationContent(), null, notificationEmail); + } + + private String createErrorNotificationContent() + { + StringBuffer sb = new StringBuffer(); + sb.append("Hello,\n"); + sb.append("This email has been generated automatically by openBIS.\n"); + sb.append("The upload of some datasets from '"); + sb.append(loggingDir.getName()); + sb.append("' directory has failed. There are following errors:\n"); + sb.append(errorMessages); + sb.append("\n"); + sb.append("If you are not sure how to correct the errors and you cannot find the answer" + + " in the documentation, ask for help your openBIS administrator.\n"); + sb.append("Kind regards,\n"); + sb.append(" openBIS Team"); + return sb.toString(); + } + /** Adds an entry about an error to the user log file. Does not send an email. */ - public static String basicError(File loggingDir, String messageFormat, Object... arguments) + private static void logError(File loggingDir, String messageFormat, Object... arguments) { String message = createUserMessage("ERROR", messageFormat, arguments); notifyUserByLogFile(loggingDir, message); - return message; } /** Adds an entry about a warning to the user log file. Does not send an email. */ - public static String basicWarn(File loggingDir, String messageFormat, Object... arguments) + private static void logWarning(File loggingDir, String messageFormat, Object... arguments) { String message = createUserMessage("WARNING", messageFormat, arguments); notifyUserByLogFile(loggingDir, message); - return message; } private static void notifyUserByLogFile(File loggingDir, String message) diff --git a/rtd_yeastx/sourceTest/java/ch/systemsx/cisd/yeastx/etl/DataSetInformationParserTest.java b/rtd_yeastx/sourceTest/java/ch/systemsx/cisd/yeastx/etl/DataSetInformationParserTest.java index da2419b10bbe3cd55bab3ae43da64feb7502c9c2..443f1ad36a2abbbe7bb0d2e795308ba363458b00 100644 --- a/rtd_yeastx/sourceTest/java/ch/systemsx/cisd/yeastx/etl/DataSetInformationParserTest.java +++ b/rtd_yeastx/sourceTest/java/ch/systemsx/cisd/yeastx/etl/DataSetInformationParserTest.java @@ -47,7 +47,7 @@ public class DataSetInformationParserTest extends AbstractFileSystemTestCase { File indexFile = writeMappingFile(HEADER + "data.txt sample1 group1 experiment1 fiaML v1 v2"); - List<DataSetMappingInformation> list = DataSetMappingInformationParser.tryParse(indexFile); + List<DataSetMappingInformation> list = tryParse(indexFile); AssertJUnit.assertEquals(1, list.size()); DataSetMappingInformation elem = list.get(0); AssertJUnit.assertEquals("group1", elem.getGroupCode()); @@ -63,14 +63,12 @@ public class DataSetInformationParserTest extends AbstractFileSystemTestCase IOException { File indexFile = writeMappingFile(HEADER + TAB + TAB + TAB + TAB + TAB + TAB); - List<DataSetMappingInformation> result = - DataSetMappingInformationParser.tryParse(indexFile); + List<DataSetMappingInformation> result = tryParse(indexFile); AssertJUnit.assertNull("error during parsing expected", result); List<String> logLines = readLogFile(); System.out.println(logLines); AssertJUnit.assertEquals(3, logLines.size()); - AssertionUtil.assertContains("Missing value for the mandatory column", logLines - .get(2)); + AssertionUtil.assertContains("Missing value for the mandatory column", logLines.get(2)); } @Test @@ -78,8 +76,7 @@ public class DataSetInformationParserTest extends AbstractFileSystemTestCase IOException { File indexFile = writeMappingFile("xxx"); - List<DataSetMappingInformation> result = - DataSetMappingInformationParser.tryParse(indexFile); + List<DataSetMappingInformation> result = tryParse(indexFile); AssertJUnit.assertNull("error during parsing expected", result); List<String> logLines = readLogFile(); AssertJUnit.assertEquals(2, logLines.size()); @@ -87,6 +84,12 @@ public class DataSetInformationParserTest extends AbstractFileSystemTestCase "Mandatory column(s) 'group', 'sample', 'file_name' are missing", logLines.get(1)); } + private List<DataSetMappingInformation> tryParse(File indexFile) + { + LogUtils log = new LogUtils(indexFile.getParentFile()); + return DataSetMappingInformationParser.tryParse(indexFile, log); + } + private List<String> readLogFile() throws IOException, FileNotFoundException { File log = new File(workingDirectory, ConstantsYeastX.USER_LOG_FILE);