diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IDataSetFileOperationsExecutor.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IDataSetFileOperationsExecutor.java index b206ffc123c5691a0c41c3cfcfb416c6b07bf2ab..1a8a89b206048745ad953ed265a042d11ac6d05d 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IDataSetFileOperationsExecutor.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IDataSetFileOperationsExecutor.java @@ -35,6 +35,9 @@ public interface IDataSetFileOperationsExecutor void copyDataSetToDestination(File dataSet, File destination); + // uses rsync --delete for performance both locally and remotely + void syncDataSetWithDestination(File dataSet, File destination); + void retrieveDataSetFromDestination(File dataSet, File destination); void renameTo(File newFile, File oldFile); diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/LocalDataSetFileOperationsExcecutor.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/LocalDataSetFileOperationsExcecutor.java index e8b7082926f2de832938fcae44cdc803f165f703..223a13b93cdf4831364ffa774833cb490109f38e 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/LocalDataSetFileOperationsExcecutor.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/LocalDataSetFileOperationsExcecutor.java @@ -32,6 +32,7 @@ import ch.systemsx.cisd.common.exceptions.Status; import ch.systemsx.cisd.common.filesystem.BooleanStatus; import ch.systemsx.cisd.common.filesystem.FileUtilities; import ch.systemsx.cisd.common.filesystem.IFileOperations; +import ch.systemsx.cisd.common.filesystem.IPathCopier; import ch.systemsx.cisd.common.logging.LogCategory; import ch.systemsx.cisd.common.logging.LogFactory; @@ -40,11 +41,21 @@ public final class LocalDataSetFileOperationsExcecutor implements IDataSetFileOp final static Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, LocalDataSetFileOperationsExcecutor.class); + private final IPathCopier copier; + + private final String rsyncModuleNameOrNull; + + private final String rsyncPasswordFileOrNull; + private final IFileOperations fileOperations; - public LocalDataSetFileOperationsExcecutor(IFileOperations fileOperations) + public LocalDataSetFileOperationsExcecutor(IFileOperations fileOperations, IPathCopier copier, + String rsyncModuleNameOrNull, String rsyncPasswordFileOrNull) { this.fileOperations = fileOperations; + this.copier = copier; + this.rsyncModuleNameOrNull = rsyncModuleNameOrNull; + this.rsyncPasswordFileOrNull = rsyncPasswordFileOrNull; } public BooleanStatus checkSame(File dataSet, File destination) @@ -63,7 +74,6 @@ public final class LocalDataSetFileOperationsExcecutor implements IDataSetFileOp if (destination.isDirectory()) { FileFilter nullFilter = null; - // TODO ignore symlinks? List<File> storeFiles = FileUtilities.listFiles(dataSet, nullFilter, true); List<File> destFiles = FileUtilities.listFiles(destination, nullFilter, true); @@ -154,6 +164,19 @@ public final class LocalDataSetFileOperationsExcecutor implements IDataSetFileOp } } + public void syncDataSetWithDestination(File dataSet, File destination) + { + // rsync --delete is more effective then deletion of destination directory & copy all + String host = null; // local + Status result = + copier.copyToRemote(dataSet, destination, host, rsyncModuleNameOrNull, + rsyncPasswordFileOrNull); + if (result.isError()) + { + throw new ExceptionWithStatus(result); + } + } + public void retrieveDataSetFromDestination(File dataSet, File destination) { try diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/RemoteDataSetFileOperationsExecutor.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/RemoteDataSetFileOperationsExecutor.java index c3932d0b4f9b5fc390f2a456218958aee63e13eb..72180f09bce5b04644a7968f0e019a383222e826 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/RemoteDataSetFileOperationsExecutor.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/RemoteDataSetFileOperationsExecutor.java @@ -128,6 +128,11 @@ public final class RemoteDataSetFileOperationsExecutor implements IDataSetFileOp } } + public void syncDataSetWithDestination(File dataSet, File destination) + { + copyDataSetToDestination(dataSet, destination); + } + public void retrieveDataSetFromDestination(File dataSet, File destination) { Status result = diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManager.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManager.java index 8745915c786a572a7ad72b9cde493d6b1fd1b53d..770dc5cedc4f7c57bb6d3997ad61c68a3d9c9034 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManager.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManager.java @@ -92,9 +92,16 @@ public class DataSetFileOperationsManager if (hostOrNull == null) { + File sshExecutable = null; // don't use ssh locally + File rsyncExecutable = Copier.getExecutable(properties, RSYNC_EXEC); + IPathCopier copier = pathCopierFactory.create(rsyncExecutable, sshExecutable); + copier.check(); + String rsyncModule = hostAwareFile.tryGetRsyncModule(); + String rsyncPasswordFile = properties.getProperty(RSYNC_PASSWORD_FILE_KEY); this.executor = new LocalDataSetFileOperationsExcecutor( - FileOperations.getMonitoredInstanceForCurrentThread()); + FileOperations.getMonitoredInstanceForCurrentThread(), copier, + rsyncModule, rsyncPasswordFile); } else { File sshExecutable = Copier.getExecutable(properties, SSH_EXEC); @@ -124,10 +131,18 @@ public class DataSetFileOperationsManager try { File destinationFolder = new File(destination, dataset.getDataSetLocation()); - createFolderIfNotExists(destinationFolder.getParentFile()); - operationLog.info("Copy dataset '" + dataset.getDatasetCode() + "' from '" - + originalData.getPath() + "' to '" + destinationFolder.getParentFile()); - executor.copyDataSetToDestination(originalData, destinationFolder.getParentFile()); + if (createFolderIfNotExists(destinationFolder.getParentFile()) + || destinationExists(destinationFolder).isSuccess() == false) + { + operationLog.info("Copy dataset '" + dataset.getDatasetCode() + "' from '" + + originalData.getPath() + "' to '" + destinationFolder.getParentFile()); + executor.copyDataSetToDestination(originalData, destinationFolder.getParentFile()); + } else + { + operationLog.info("Update dataset '" + dataset.getDatasetCode() + "' from '" + + originalData.getPath() + "' to '" + destinationFolder.getParentFile()); + executor.syncDataSetWithDestination(originalData, destinationFolder.getParentFile()); + } return Status.OK; } catch (ExceptionWithStatus ex) { diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiveCopierFactory.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiveCopierFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..a0898ecf996fa14fe4280187ea193de96c90debe --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiveCopierFactory.java @@ -0,0 +1,42 @@ +/* + * Copyright 2010 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.openbis.dss.generic.server.plugins.standard; + +import java.io.File; +import java.io.Serializable; + +import ch.systemsx.cisd.common.filesystem.IPathCopier; +import ch.systemsx.cisd.common.filesystem.rsync.RsyncCopier; + +/** + * {@link IPathCopierFactory} that is more reliable than {@link RsyncCopierFactory} when it comes to + * deciding which files to transfer. {@link IPathCopier} created by {@link RsyncCopierFactory} uses + * "--append" flag causing files that are bigger in destination than in source to be ignored. + * {@link IPathCopier} created by this factory is supposed to ignore only those files that have same + * sizes and modification times. + */ +public final class RsyncArchiveCopierFactory implements Serializable, IPathCopierFactory +{ + private static final long serialVersionUID = 1L; + + public IPathCopier create(File rsyncExecutable, File sshExecutableOrNull) + { + // TODO 2011-04-05, Piotr Buczek: should we use --no-whole-file? + return new RsyncCopier(rsyncExecutable, sshExecutableOrNull, "--archive", "--delete", + "--inplace"); + } +} \ No newline at end of file diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiver.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiver.java index 770df7c92be59595d11f87f6b64f3e4a9415068c..934f347010664fdfe182c7ccb775ccd724533cb6 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiver.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiver.java @@ -52,12 +52,12 @@ public class RsyncArchiver extends AbstractArchiverProcessingPlugin public RsyncArchiver(Properties properties, File storeRoot) { - this(properties, storeRoot, new RsyncCopierFactory(), new SshCommandExecutorFactory()); + this(properties, storeRoot, new RsyncArchiveCopierFactory(), + new SshCommandExecutorFactory()); } @Private - RsyncArchiver(Properties properties, File storeRoot, - IPathCopierFactory pathCopierFactory, + RsyncArchiver(Properties properties, File storeRoot, IPathCopierFactory pathCopierFactory, ISshCommandExecutorFactory sshCommandExecutorFactory) { super(properties, storeRoot, null, null); @@ -131,7 +131,8 @@ public class RsyncArchiver extends AbstractArchiverProcessingPlugin if (fileOperationsManager == null) { this.fileOperationsManager = - new DataSetFileOperationsManager(properties, pathCopierFactory, sshCommandExecutorFactory); + new DataSetFileOperationsManager(properties, pathCopierFactory, + sshCommandExecutorFactory); } } diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManagerTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManagerTest.java index b4cdae4e5d088594128eb73b5b4c96c5689a6bc5..112a24a96654f33e9ef797d20938a0b9ff291388 100644 --- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManagerTest.java +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManagerTest.java @@ -249,6 +249,18 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase /* * archive 2nd time (could happen on crash of DSS, but shouldn't hurt) */ + context.checking(new Expectations() + { + { + /* + * use rsync + */ + one(copier).copyToRemote(ds1Location, + ds1ArchivedLocationFile.getParentFile().getAbsoluteFile(), null, null, + null); + will(returnValue(Status.OK)); + } + }); status = dataSetCopier.copyToDestination(ds1Location, ds1); assertSuccessful(status); // check that data set is now in archive @@ -482,6 +494,9 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase { final Properties properties = new Properties(); properties.setProperty(DataSetFileOperationsManager.DESTINATION_KEY, destination.getPath()); + properties.setProperty(DataSetFileOperationsManager.RSYNC_EXEC + "-executable", + rsyncExec.getPath()); + prepareLocalCreateAndCheckCopier(); return properties; } @@ -521,6 +536,8 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase one(sshExecutor).exists(ds2ArchivedLocationFile.getParentFile().getPath(), SSH_TIMEOUT_MILLIS); will(returnValue(BooleanStatus.createTrue())); + one(sshExecutor).exists(ds2ArchivedLocationFile.getPath(), SSH_TIMEOUT_MILLIS); + will(returnValue(BooleanStatus.createTrue())); one(copier).copyToRemote(ds2Location, ds2ArchivedLocationFile.getParentFile(), HOST, null, null); @@ -851,6 +868,19 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase // context.assertIsSatisfied(); // } + private void prepareLocalCreateAndCheckCopier() + { + context.checking(new Expectations() + { + { + one(copierFactory).create(rsyncExec, null); + will(returnValue(copier)); + + one(copier).check(); + } + }); + } + private void prepareRemoteCreateAndCheckCopier(final String hostOrNull, final String rsyncModuleOrNull, final boolean checkingResult) { diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/dss/phosphonetx/server/plugins/LocalAndRemoteCopier.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/dss/phosphonetx/server/plugins/LocalAndRemoteCopier.java index bb1c99fecbcf20067606fd0bd589bb06b1eed7e2..4630408b2cf6fda8b5a13fdb7805eb1ac3b8eaae 100644 --- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/dss/phosphonetx/server/plugins/LocalAndRemoteCopier.java +++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/dss/phosphonetx/server/plugins/LocalAndRemoteCopier.java @@ -99,9 +99,17 @@ class LocalAndRemoteCopier implements Serializable, IPostRegistrationDatasetHand destination = hostAwareFile.getFile(); if (hostOrNull == null) { + File sshExecutable = null; // don't use ssh locally + File rsyncExecutable = Copier.getExecutable(properties, DataSetCopier.RSYNC_EXEC); + IPathCopier copier = pathCopierFactory.create(rsyncExecutable, sshExecutable); + copier.check(); + String rsyncModule = hostAwareFile.tryGetRsyncModule(); + String rsyncPasswordFile = + properties.getProperty(DataSetCopier.RSYNC_PASSWORD_FILE_KEY); executor = new LocalDataSetFileOperationsExcecutor( - FileOperations.getMonitoredInstanceForCurrentThread()); + FileOperations.getMonitoredInstanceForCurrentThread(), copier, + rsyncModule, rsyncPasswordFile); } else { File sshExecutable = Copier.getExecutable(properties, DataSetCopier.SSH_EXEC);