From 9f57793119c0e3a566bd5c07cd74cc4df8cccea1 Mon Sep 17 00:00:00 2001 From: izabel <izabel> Date: Tue, 1 Sep 2009 23:27:43 +0000 Subject: [PATCH] [SE-131] metabol db updating plugin SVN: 12354 --- .../ch/systemsx/cisd/etlserver/ETLDaemon.java | 17 ++ .../cisd/etlserver/IMaintenanceTask.java | 37 ++++ .../cisd/etlserver/MaintenancePlugin.java | 43 ++++ .../etlserver/MaintenanceTaskParameters.java | 61 ++++++ .../systemsx/cisd/etlserver/Parameters.java | 38 ++++ .../cisd/etlserver/plugins/.gitignore | 0 .../plugins/HierarchicalStorageUpdater.java | 201 ++++++++++++++++++ .../openbis/dss/generic/DataStoreServer.java | 1 - .../server/DataSetHierarchyHelper.java | 2 +- .../server/HierarchicalStorageDaemon.java | 1 + .../dss/generic/server/SoftLinkMaker.java | 2 +- .../openbis/generic/server/ETLService.java | 2 +- .../server/dataaccess/db/EventDAO.java | 4 +- .../server/dataaccess/db/EventDAOTest.java | 9 + .../yeastx/etl/MetabolDatabaseUpdater.java | 162 ++++++++++++++ .../source/sql/postgresql/002/schema-002.sql | 19 +- .../migration/migration-001-002.sql | 12 ++ 17 files changed, 591 insertions(+), 20 deletions(-) create mode 100644 datastore_server/source/java/ch/systemsx/cisd/etlserver/IMaintenanceTask.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/etlserver/MaintenancePlugin.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/etlserver/MaintenanceTaskParameters.java delete mode 100644 datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/.gitignore create mode 100644 datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/HierarchicalStorageUpdater.java create mode 100644 rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/MetabolDatabaseUpdater.java create mode 100644 rtd_yeastx/source/sql/postgresql/migration/migration-001-002.sql diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/ETLDaemon.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/ETLDaemon.java index 0fd75967808..dcafaf3d3b8 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/ETLDaemon.java +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/ETLDaemon.java @@ -546,7 +546,24 @@ public final class ETLDaemon QueueingPathRemoverService.start(shredderQueueFile); printInitialLogMessage(parameters); startupServer(parameters); + startupMaintenancePlugins(parameters.getMaintenancePlugins()); operationLog.info("Data Store Server ready and waiting for data."); } + private static void startupMaintenancePlugins(MaintenanceTaskParameters[] maintenancePlugins) + { + + List<MaintenancePlugin> plugins = new ArrayList<MaintenancePlugin>(); + for (MaintenanceTaskParameters parameters : maintenancePlugins) + { + MaintenancePlugin plugin = new MaintenancePlugin(parameters); + plugins.add(plugin); + } + for (MaintenancePlugin plugin : plugins) + { + plugin.start(); + } + + } + } diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/IMaintenanceTask.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/IMaintenanceTask.java new file mode 100644 index 00000000000..b2ca39f8079 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/IMaintenanceTask.java @@ -0,0 +1,37 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.etlserver; + +/** + * The interface that should be implemented by all maintenance tasks. + * + * @author Izabela Adamczyk + */ +public interface IMaintenanceTask +{ + + /** + * Performs the maintenance task. + */ + public void execute(); + + /** + * Prepares the task for execution and checks that it has been configured correctly. + */ + public void setUp(String pluginName); + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/MaintenancePlugin.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/MaintenancePlugin.java new file mode 100644 index 00000000000..f59524e5c4f --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/MaintenancePlugin.java @@ -0,0 +1,43 @@ +package ch.systemsx.cisd.etlserver; + +import java.util.Timer; +import java.util.TimerTask; + +import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel; +import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException; +import ch.systemsx.cisd.common.utilities.ClassUtils; + +public class MaintenancePlugin +{ + IMaintenanceTask task; + + MaintenanceTaskParameters parameters; + + public MaintenancePlugin(MaintenanceTaskParameters parameters) + { + this.parameters = parameters; + try + { + this.task = ClassUtils.create(IMaintenanceTask.class, parameters.getClassName()); + } catch (Exception ex) + { + throw new ConfigurationFailureException("Cannot find the plugin class '" + parameters + + "'", CheckedExceptionTunnel.unwrapIfNecessary(ex)); + } + task.setUp(parameters.getPluginName()); + } + + public void start() + { + final String timerThreadName = parameters.getPluginName() + " - Maintenance Plugin"; + final Timer workerTimer = new Timer(timerThreadName); + workerTimer.schedule(new TimerTask() + { + @Override + public void run() + { + task.execute(); + } + }, 0L, parameters.getInterval() * 1000); + } +} \ No newline at end of file diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/MaintenanceTaskParameters.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/MaintenanceTaskParameters.java new file mode 100644 index 00000000000..76262bbf9c7 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/MaintenanceTaskParameters.java @@ -0,0 +1,61 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.etlserver; + +import java.util.Properties; + +import ch.systemsx.cisd.common.utilities.PropertyUtils; + +/** + * @author Izabela Adamczyk + */ +public class MaintenanceTaskParameters +{ + private static final int ONE_DAY_IN_SEC = 60 * 60 * 24; + + private static final String CLASS_KEY = "class"; + + private static final String INTERVAL_KEY = "interval"; + + private final String pluginName; + + private final long interval; + + private final String className; + + public MaintenanceTaskParameters(Properties properties, String pluginName) + { + this.pluginName = pluginName; + interval = PropertyUtils.getLong(properties, INTERVAL_KEY, ONE_DAY_IN_SEC); + className = PropertyUtils.getProperty(properties, CLASS_KEY); + } + + public long getInterval() + { + return interval; + } + + public String getClassName() + { + return className; + } + + public String getPluginName() + { + return pluginName; + } +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/Parameters.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/Parameters.java index 76eae9367a0..6d94aa059e4 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/Parameters.java +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/Parameters.java @@ -63,6 +63,11 @@ public class Parameters /** property with thread names separated by delimiter */ private static final String INPUT_THREAD_NAMES = "inputs"; + /** + * property with maintenance plugin names separated by delimiter + */ + private static final String MAINTENANCE_PLUGINS = "maintenance-plugins"; + @Option(name = "s", longName = "server-url", metaVar = "URL", usage = "URL of the server") private String serverURL; @@ -129,6 +134,8 @@ public class Parameters private final Map<String, Properties> processorProperties; + private MaintenanceTaskParameters[] maintenancePlugins; + @Option(longName = "help", skipForExample = true, usage = "Prints out a description of the options.") void printHelp(final boolean exit) { @@ -177,6 +184,7 @@ public class Parameters this.threads = createThreadParameters(serviceProperties); this.mailProperties = createMailProperties(serviceProperties); this.timingParameters = TimingParameters.create(serviceProperties); + this.maintenancePlugins = createMaintenancePlugins(serviceProperties); initCommandLineParametersFromProperties(); @@ -191,6 +199,11 @@ public class Parameters } } + public MaintenanceTaskParameters[] getMaintenancePlugins() + { + return maintenancePlugins; + } + private void ensureParametersCorrect() { for (final ThreadParameters thread : threads) @@ -252,6 +265,31 @@ public class Parameters } } + private static MaintenanceTaskParameters[] createMaintenancePlugins( + final Properties serviceProperties) + { + SectionProperties[] sectionsProperties = + PropertyParametersUtil.extractSectionProperties(serviceProperties, + MAINTENANCE_PLUGINS, true); + return asMaintenanceParameters(sectionsProperties); + } + + private static MaintenanceTaskParameters[] asMaintenanceParameters( + SectionProperties[] sectionProperties) + { + final MaintenanceTaskParameters[] maintenanceParameters = + new MaintenanceTaskParameters[sectionProperties.length]; + for (int i = 0; i < maintenanceParameters.length; i++) + { + SectionProperties section = sectionProperties[i]; + operationLog.info("Create parameters for maintenance plugin '" + section.getKey() + + "'."); + maintenanceParameters[i] = + new MaintenanceTaskParameters(section.getProperties(), section.getKey()); + } + return maintenanceParameters; + } + private static ThreadParameters[] asThreadParameters(SectionProperties[] sectionProperties) { final ThreadParameters[] threadParameters = new ThreadParameters[sectionProperties.length]; diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/.gitignore b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/.gitignore deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/HierarchicalStorageUpdater.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/HierarchicalStorageUpdater.java new file mode 100644 index 00000000000..ad4479fd864 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/HierarchicalStorageUpdater.java @@ -0,0 +1,201 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.etlserver.plugins; + +import java.io.File; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import org.apache.log4j.Logger; + +import ch.systemsx.cisd.common.filesystem.FileUtilities; +import ch.systemsx.cisd.common.logging.LogCategory; +import ch.systemsx.cisd.common.logging.LogFactory; +import ch.systemsx.cisd.common.logging.LogInitializer; +import ch.systemsx.cisd.common.process.ProcessExecutionHelper; +import ch.systemsx.cisd.common.utilities.PropertyUtils; +import ch.systemsx.cisd.etlserver.IMaintenanceTask; +import ch.systemsx.cisd.openbis.dss.generic.server.DataSetHierarchyHelper; +import ch.systemsx.cisd.openbis.dss.generic.server.SoftLinkMaker; +import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService; +import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider; +import ch.systemsx.cisd.openbis.dss.generic.shared.utils.PropertyParametersUtil; +import ch.systemsx.cisd.openbis.generic.shared.dto.SimpleDataSetInformationDTO; + +/** + * Creates the hierarchical structure of data sets registered in openBIS for given data store. + * + * @author Izabela Adamczyk + */ +public class HierarchicalStorageUpdater implements IMaintenanceTask +{ + public static final String STOREROOT_DIR_KEY = "storeroot-dir"; + + public static final String HIERARCHY_ROOT_DIR_KEY = "hierarchy-root-dir"; + + private static final String REBUILDING_HIERARCHICAL_STORAGE = "Rebuilding hierarchical storage"; + + private static final Logger operationLog = + LogFactory.getLogger(LogCategory.OPERATION, HierarchicalStorageUpdater.class); + + private static final Logger machineLog = + LogFactory.getLogger(LogCategory.MACHINE, HierarchicalStorageUpdater.class); + + private IEncapsulatedOpenBISService openBISService; + + private String storeRoot; + + private String hierarchyRoot; + + public void setUp(String pluginName) + { + LogInitializer.init(); + Properties properties = PropertyParametersUtil.loadServiceProperties(); + storeRoot = PropertyUtils.getMandatoryProperty(properties, STOREROOT_DIR_KEY); + hierarchyRoot = + PropertyUtils.getMandatoryProperty(properties, pluginName + "." + + HIERARCHY_ROOT_DIR_KEY); + openBISService = ServiceProvider.getOpenBISService(); + operationLog.info("Plugin initialized with: store root = " + storeRoot + + ", hierarchy root = " + hierarchyRoot); + } + + public void execute() + { + rebuildHierarchy(new File(storeRoot), openBISService, new File(hierarchyRoot)); + } + + /** + * Refreshes the hierarchy of the data inside hierarchical storage accordingly to the database + * content. + */ + private static void rebuildHierarchy(File storeRoot, + IEncapsulatedOpenBISService openBISService, File hierarchyRoot) + { + logInfo(REBUILDING_HIERARCHICAL_STORAGE); + Collection<SimpleDataSetInformationDTO> dataSets = openBISService.listDataSets(); + Map<String, String> newLinkMappings = + convertDataToLinkMappings(storeRoot, hierarchyRoot, dataSets); + Set<String> toCreate = new HashSet<String>(newLinkMappings.keySet()); + Set<String> toDelete = DataSetHierarchyHelper.extractPaths(hierarchyRoot); + Set<String> dontTouch = intersection(toCreate, toDelete); + toCreate.removeAll(dontTouch); + toDelete.removeAll(dontTouch); + removeUnnecessaryMappings(newLinkMappings, toCreate); + deleteObsoleteLinks(hierarchyRoot, toDelete); + createLinksForChangedData(newLinkMappings); + } + + /** + * Extracts a {@link Map}: (target,source) from a collection of data sets. + */ + private static Map<String, String> convertDataToLinkMappings(File storeRoot, + File hierarchyRoot, Collection<SimpleDataSetInformationDTO> dataSets) + { + Map<String, String> linkMappings = new HashMap<String, String>(); + for (SimpleDataSetInformationDTO dataSet : dataSets) + { + File targetFile = + new File(hierarchyRoot, DataSetHierarchyHelper.createHierarchicalPath(dataSet)); + File sourceFile = new File(storeRoot, dataSet.getDataSetLocation()); + linkMappings.put(targetFile.getAbsolutePath(), sourceFile.getAbsolutePath()); + } + return linkMappings; + } + + /** + * Removes from the <code>linkMappings</code> map all the elements with keys not belonging to + * <code>keep</code> set. + */ + private static void removeUnnecessaryMappings(Map<String, String> linkMappings, Set<String> keep) + { + Set<String> keys = new HashSet<String>(linkMappings.keySet()); + for (String path : keys) + { + if (keep.contains(path) == false) + { + linkMappings.remove(path); + } + } + } + + /** + * Creates a new {@link Set} containing the elements that belong to both {@link Set}s. + */ + private static Set<String> intersection(Set<String> setA, Set<String> setB) + { + Set<String> toBeUntouched = new HashSet<String>(setA); + toBeUntouched.retainAll(setB); + return toBeUntouched; + } + + /** + * Recursively removes from the file system files with paths defined in <code>toBeDeleted</code> + * {@link Set}. + */ + private static void deleteObsoleteLinks(File hierarchyRoot, Set<String> toBeDeleted) + { + for (String pathToDelete : toBeDeleted) + { + File toDelete = new File(pathToDelete); + File parent = toDelete.getParentFile(); + FileUtilities.deleteRecursively(toDelete); + while (parent != null + && parent.getAbsolutePath().equals(hierarchyRoot.getAbsolutePath()) == false) + { + if (parent.list().length == 0) + { + toDelete = parent; + parent = toDelete.getParentFile(); + toDelete.delete(); + } else + { + break; + } + } + } + } + + /** + * Creates the soft links for files with paths defined in <code>linkMappings</code> {@link Map}. + */ + private static void createLinksForChangedData(Map<String, String> linkMappings) + { + for (String targetPath : linkMappings.keySet()) + { + File targetDir = new File(targetPath); + String sourcePath = linkMappings.get(targetPath); + File sourceFile = new File(sourcePath); + targetDir.mkdirs(); + ProcessExecutionHelper.runAndLog(new SoftLinkMaker().createCommand(sourceFile, + targetDir), operationLog, machineLog); + } + } + + private static void logInfo(String info) + { + if (operationLog.isInfoEnabled()) + { + operationLog.info(info); + } + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/DataStoreServer.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/DataStoreServer.java index f7b4da3d8af..63f75a8c73f 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/DataStoreServer.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/DataStoreServer.java @@ -31,6 +31,5 @@ public class DataStoreServer { ch.systemsx.cisd.openbis.dss.generic.server.DataStoreServer.main(args); ETLDaemon.main(args); - HierarchicalStorageDaemon.main(args); } } diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataSetHierarchyHelper.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataSetHierarchyHelper.java index b5380fb9372..56a1657c0cc 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataSetHierarchyHelper.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataSetHierarchyHelper.java @@ -28,7 +28,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SimpleDataSetInformationDTO; * * @author Izabela Adamczyk */ -class DataSetHierarchyHelper +public class DataSetHierarchyHelper { /** diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/HierarchicalStorageDaemon.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/HierarchicalStorageDaemon.java index 0f0e6c91eb9..cb9c215f52d 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/HierarchicalStorageDaemon.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/HierarchicalStorageDaemon.java @@ -222,4 +222,5 @@ public class HierarchicalStorageDaemon operationLog.info(info); } } + } diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/SoftLinkMaker.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/SoftLinkMaker.java index c10d7f5ccbf..497327e7105 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/SoftLinkMaker.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/SoftLinkMaker.java @@ -11,7 +11,7 @@ import ch.systemsx.cisd.base.utilities.OSUtilities; * * @author Izabela Adamczyk */ -class SoftLinkMaker +public class SoftLinkMaker { private File lnExec; diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java index 73621b9b0db..e9a9bcd9751 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java @@ -17,8 +17,8 @@ package ch.systemsx.cisd.openbis.generic.server; import java.io.UnsupportedEncodingException; -import java.util.ArrayList; import java.util.Date; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/EventDAO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/EventDAO.java index f6637f8263a..5de8b6a4755 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/EventDAO.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/EventDAO.java @@ -16,8 +16,8 @@ package ch.systemsx.cisd.openbis.generic.server.dataaccess.db; -import java.util.ArrayList; import java.util.Date; +import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; @@ -82,7 +82,7 @@ public class EventDAO extends AbstractGenericEntityDAO<EventPE> implements IEven final DetachedCriteria criteria = DetachedCriteria.forClass(EventPE.class); if (since != null) { - criteria.add(Restrictions.ge("registrationDate", since)); + criteria.add(Restrictions.gt("registrationDate", since)); } criteria.add(Restrictions.eq("eventType", EventType.DELETION)); criteria.add(Restrictions.eq("entityType", EntityType.DATASET)); diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/EventDAOTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/EventDAOTest.java index 9ce069171ad..d73a4923af8 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/EventDAOTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/EventDAOTest.java @@ -139,6 +139,15 @@ public class EventDAOTest extends AbstractDAOTest assertCorrectResult(numberOfDataSets, result); } + @Test + public void testListDeletedDataSetsWithoutSince() throws Exception + { + saveEvent(EventType.DELETION, EntityType.DATASET, KEEP_ME + 1, BEFORE); + saveEvent(EventType.DELETION, EntityType.DATASET, KEEP_ME + 2, SINCE); + List<DeletedDataSet> result = listDataDeletionEvents(SINCE); + assertCorrectResult(0, result); + } + private void saveEvent(EventType eventType, EntityType entityType, String identifier, Date date) { String description = eventType.name() + " " + entityType.name(); diff --git a/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/MetabolDatabaseUpdater.java b/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/MetabolDatabaseUpdater.java new file mode 100644 index 00000000000..ccc3a360150 --- /dev/null +++ b/rtd_yeastx/source/java/ch/systemsx/cisd/yeastx/etl/MetabolDatabaseUpdater.java @@ -0,0 +1,162 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ch.systemsx.cisd.yeastx.etl; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Date; +import java.util.List; + +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.log4j.Logger; + +import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException; +import ch.systemsx.cisd.common.logging.LogCategory; +import ch.systemsx.cisd.common.logging.LogFactory; +import ch.systemsx.cisd.common.logging.LogInitializer; +import ch.systemsx.cisd.dbmigration.DatabaseConfigurationContext; +import ch.systemsx.cisd.etlserver.IMaintenanceTask; +import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService; +import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DeletedDataSet; +import ch.systemsx.cisd.yeastx.db.DBUtils; + +/** + * Maintenance task deleting from metabol database data sets which have been deleted from openbis. + * + * @author Izabela Adamczyk + */ +public class MetabolDatabaseUpdater implements IMaintenanceTask +{ + + private static final String SYNCHRONIZATION_TABLE = "EVENTS"; + + private static final String SYNCHRONIZATION_TIMESTAMP = "EVENT_DATE"; + + private static final Logger operationLog = + LogFactory.getLogger(LogCategory.OPERATION, MetabolDatabaseUpdater.class); + + private Connection connection; + + private IEncapsulatedOpenBISService openBISService; + + private DatabaseConfigurationContext context; + + public void setUp(String pluginName) + { + LogInitializer.init(); + context = DBUtils.createDefaultDBContext(); + try + { + connection = context.getDataSource().getConnection(); + getPreviousSynchronizationDate(); + connection.close(); + } catch (SQLException ex) + { + throw new ConfigurationFailureException("Initialization failed", ex); + } + openBISService = ServiceProvider.getOpenBISService(); + operationLog.info("Plugin initialized"); + } + + public void execute() + { + operationLog.info("Synchronizing data set information"); + try + { + connection = context.getDataSource().getConnection(); + Date previousSyncDate = getPreviousSynchronizationDate(); + List<DeletedDataSet> deletedDataSets = + openBISService.listDeletedDataSets(previousSyncDate); + if (deletedDataSets.size() > 0) + { + boolean autoCommit = connection.getAutoCommit(); + connection.setAutoCommit(false); + deleteDatasets(deletedDataSets); + updateSynchronizationDate(previousSyncDate, deletedDataSets); + connection.commit(); + connection.setAutoCommit(autoCommit); + } + connection.close(); + } catch (SQLException ex) + { + operationLog.error(ex); + } + } + + private void deleteDatasets(List<DeletedDataSet> deletedDataSets) throws SQLException + { + connection.createStatement().execute( + String.format("DELETE FROM data_sets WHERE perm_id IN (%s)", + joinIds(deletedDataSets))); + } + + private void updateSynchronizationDate(Date previousSyncDate, List<DeletedDataSet> deleted) + throws SQLException + { + Date newSynchncDate = previousSyncDate; + for (DeletedDataSet dds : deleted) + { + Date date = dds.getDeletionDate(); + if (newSynchncDate == null || date.after(newSynchncDate)) + { + newSynchncDate = date; + } + } + if (previousSyncDate == null || newSynchncDate.after(previousSyncDate)) + { + PreparedStatement statement = + connection.prepareStatement("INSERT INTO " + SYNCHRONIZATION_TABLE + " (" + + SYNCHRONIZATION_TIMESTAMP + ") VALUES('" + newSynchncDate + "')"); + statement.executeUpdate(); + } + } + + private String joinIds(List<DeletedDataSet> deleted) + { + StringBuilder sb = new StringBuilder(); + for (DeletedDataSet dds : deleted) + { + if (sb.length() != 0) + { + sb.append(", "); + } + sb.append("'" + StringEscapeUtils.escapeSql(dds.getIdentifier()) + "'"); + } + String ids = sb.toString(); + return ids; + } + + private Date getPreviousSynchronizationDate() throws SQLException + { + Date lastDeleted = null; + ResultSet result = + connection.createStatement().executeQuery( + "SELECT MAX(" + SYNCHRONIZATION_TIMESTAMP + ") AS " + + SYNCHRONIZATION_TIMESTAMP + " FROM " + SYNCHRONIZATION_TABLE); + while (result.next()) + { + if (lastDeleted == null + || lastDeleted.before(result.getTimestamp(SYNCHRONIZATION_TIMESTAMP))) + { + lastDeleted = result.getTimestamp(SYNCHRONIZATION_TIMESTAMP); + } + } + return lastDeleted; + } +} diff --git a/rtd_yeastx/source/sql/postgresql/002/schema-002.sql b/rtd_yeastx/source/sql/postgresql/002/schema-002.sql index 0c3fb7d9591..8e261894a37 100644 --- a/rtd_yeastx/source/sql/postgresql/002/schema-002.sql +++ b/rtd_yeastx/source/sql/postgresql/002/schema-002.sql @@ -162,23 +162,12 @@ CREATE TABLE FIA_CENTROIDS ( CREATE INDEX FIA_CENTROID_I_ID on FIA_CENTROIDS(FIA_MS_RUN_ID); CREATE INDEX FIA_CENTROID_I_ID_MZ on FIA_CENTROIDS(FIA_MS_RUN_ID, MZ); --- Table CONCENTRATIONS - WORK IN PROGRESS +-- Table EVENTS -CREATE TABLE CONCENTRATIONS ( - ID BIGSERIAL NOT NULL, - EXPE_ID TECH_ID NOT NULL, - SAMP_ID TECH_ID NOT NULL, - DS_ID TECH_ID NOT NULL, - COMPOUND_ID CODE NOT NULL, - AMOUNT REAL NOT NULL - UNIT SHORT_LABEL NOT NULL - REGISTRATOR_ID SHORT_LABEL DEFAULT NULL, - CALIBRATION_NOTES TEXT, - COMMENTS TEXT, - ACQUISITION_DATE TIMESTAMP DEFAULT NULL, +CREATE TABLE EVENTS ( + EVENT_DATE TIMESTAMP WITH TIME ZONE NOT NULL ); - GRANT SELECT ON TABLE EXPERIMENTS TO GROUP metabol_readonly; GRANT SELECT ON TABLE SAMPLES TO GROUP metabol_readonly; GRANT SELECT ON TABLE DATA_SETS TO GROUP metabol_readonly; @@ -187,6 +176,7 @@ GRANT SELECT ON TABLE EIC_CHROMATOGRAMS TO GROUP metabol_readonly; GRANT SELECT ON TABLE FIA_MS_RUNS TO GROUP metabol_readonly; GRANT SELECT ON TABLE FIA_PROFILES TO GROUP metabol_readonly; GRANT SELECT ON TABLE FIA_CENTROIDS TO GROUP metabol_readonly; +GRANT SELECT ON TABLE EVENTS TO GROUP metabol_readonly; GRANT SELECT ON SEQUENCE EXPERIMENTS_ID_SEQ TO GROUP metabol_readonly; GRANT SELECT ON SEQUENCE SAMPLES_ID_SEQ TO GROUP metabol_readonly; GRANT SELECT ON SEQUENCE DATA_SETS_ID_SEQ TO GROUP metabol_readonly; @@ -204,6 +194,7 @@ GRANT ALL PRIVILEGES ON TABLE EIC_CHROMATOGRAMS TO GROUP metabol_readwrite; GRANT ALL PRIVILEGES ON TABLE FIA_MS_RUNS TO GROUP metabol_readwrite; GRANT ALL PRIVILEGES ON TABLE FIA_PROFILES TO GROUP metabol_readwrite; GRANT ALL PRIVILEGES ON TABLE FIA_CENTROIDS TO GROUP metabol_readwrite; +GRANT ALL PRIVILEGES ON TABLE EVENTS TO GROUP metabol_readwrite; GRANT ALL PRIVILEGES ON SEQUENCE EXPERIMENTS_ID_SEQ TO GROUP metabol_readwrite; GRANT ALL PRIVILEGES ON SEQUENCE SAMPLES_ID_SEQ TO GROUP metabol_readwrite; GRANT ALL PRIVILEGES ON SEQUENCE DATA_SETS_ID_SEQ TO GROUP metabol_readwrite; diff --git a/rtd_yeastx/source/sql/postgresql/migration/migration-001-002.sql b/rtd_yeastx/source/sql/postgresql/migration/migration-001-002.sql new file mode 100644 index 00000000000..fb6816854a2 --- /dev/null +++ b/rtd_yeastx/source/sql/postgresql/migration/migration-001-002.sql @@ -0,0 +1,12 @@ +----------------------------------- +-- Migration 001-002 +----------------------------------- + +-- Create Table EVENTS + +CREATE TABLE EVENTS ( + EVENT_DATE TIMESTAMP WITH TIME ZONE NOT NULL +); + +GRANT SELECT ON TABLE EVENTS TO GROUP metabol_readonly; +GRANT ALL PRIVILEGES ON TABLE EVENTS TO GROUP metabol_readwrite; -- GitLab