From 3fbf856f3d4fff25a70cd351c7e5cbc20e4e58d2 Mon Sep 17 00:00:00 2001 From: pkupczyk <pkupczyk> Date: Mon, 30 Jul 2012 08:53:15 +0000 Subject: [PATCH] SP-154 / BIS-38 - Improve checksum check for share moving SVN: 26274 --- .../cisd/etlserver/plugins/DataSetMover.java | 12 +- .../cisd/etlserver/plugins/IDataSetMover.java | 21 ++-- .../postregistration/EagerShufflingTask.java | 80 +++++++++---- .../HierarchicalContentChecksumProvider.java | 60 ++++++++++ .../dss/generic/shared/IChecksumProvider.java | 31 +++++ .../shared/utils/SegmentedStoreUtils.java | 53 +++++--- .../plugins/SimpleShufflingTest.java | 6 +- .../EagerShufflingTaskTest.java | 113 +++++++++++------- .../shared/utils/SegmentedStoreUtilsTest.java | 61 ++++++++-- 9 files changed, 334 insertions(+), 103 deletions(-) create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/HierarchicalContentChecksumProvider.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/IChecksumProvider.java diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/DataSetMover.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/DataSetMover.java index 3a0b5f284dc..31be0c2a4c0 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/DataSetMover.java +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/DataSetMover.java @@ -3,20 +3,22 @@ package ch.systemsx.cisd.etlserver.plugins; import java.io.File; import ch.systemsx.cisd.common.logging.ISimpleLogger; +import ch.systemsx.cisd.openbis.dss.generic.shared.IChecksumProvider; import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService; import ch.systemsx.cisd.openbis.dss.generic.shared.IShareIdManager; import ch.systemsx.cisd.openbis.dss.generic.shared.utils.SegmentedStoreUtils; /** * Implementation of {@link IDataSetMover}. - * + * * @author Franz-Josef Elmer */ public class DataSetMover implements IDataSetMover { private final IEncapsulatedOpenBISService service; + private final IShareIdManager manager; - + public DataSetMover(IEncapsulatedOpenBISService service, IShareIdManager shareIdManager) { this.service = service; @@ -25,9 +27,9 @@ public class DataSetMover implements IDataSetMover @Override public void moveDataSetToAnotherShare(File dataSetDirInStore, File share, - ISimpleLogger logger) + IChecksumProvider checksumProvider, ISimpleLogger logger) { - SegmentedStoreUtils.moveDataSetToAnotherShare(dataSetDirInStore, share, - service, manager, logger); + SegmentedStoreUtils.moveDataSetToAnotherShare(dataSetDirInStore, share, service, manager, + checksumProvider, logger); } } \ No newline at end of file diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/IDataSetMover.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/IDataSetMover.java index 240dff83f48..ff24f2a3915 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/IDataSetMover.java +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/IDataSetMover.java @@ -20,20 +20,25 @@ import java.io.File; import java.util.Properties; import ch.systemsx.cisd.common.logging.ISimpleLogger; - +import ch.systemsx.cisd.openbis.dss.generic.shared.IChecksumProvider; /** - * Strategy of moving a data set to another share. Implementations of this interface should - * have a public constructor with an argument of type {@link Properties}. - * + * Strategy of moving a data set to another share. Implementations of this interface should have a + * public constructor with an argument of type {@link Properties}. + * * @author Franz-Josef Elmer */ public interface IDataSetMover { /** - * Moves the specified data set to the specified share. The data set is folder in the store - * its name is the data set code. The destination folder is <code>share</code>. Its name is - * the share id. Share id and size will be updated on openBIS. + * Moves the specified data set to the specified share. The data set is folder in the store its + * name is the data set code. The destination folder is <code>share</code>. Its name is the + * share id. Share id and size will be updated on openBIS. + * + * @param checksumProvider provides checksums of the source data set files that will be compared + * with calculated checksums of the destination data set files. If checksumProvider + * is null, then checksum verification is not performed. */ - public void moveDataSetToAnotherShare(File dataSetDirInStore, File share, ISimpleLogger logger); + public void moveDataSetToAnotherShare(File dataSetDirInStore, File share, + IChecksumProvider checksumProvider, ISimpleLogger logger); } diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/postregistration/EagerShufflingTask.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/postregistration/EagerShufflingTask.java index 8c51edf17e9..44d2b767f7f 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/postregistration/EagerShufflingTask.java +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/postregistration/EagerShufflingTask.java @@ -40,6 +40,8 @@ import ch.systemsx.cisd.common.utilities.PropertyParametersUtil; import ch.systemsx.cisd.common.utilities.PropertyUtils; import ch.systemsx.cisd.etlserver.plugins.DataSetMover; import ch.systemsx.cisd.etlserver.plugins.IDataSetMover; +import ch.systemsx.cisd.openbis.dss.generic.shared.HierarchicalContentChecksumProvider; +import ch.systemsx.cisd.openbis.dss.generic.shared.IChecksumProvider; import ch.systemsx.cisd.openbis.dss.generic.shared.IConfigProvider; import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService; import ch.systemsx.cisd.openbis.dss.generic.shared.IShareFinder; @@ -52,21 +54,30 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SimpleDataSetInformationDTO; /** * Post registration task which move the data set to share which has enough space. - * + * * @author Franz-Josef Elmer */ public class EagerShufflingTask extends AbstractPostRegistrationTask { - @Private public static final String SHARE_FINDER_KEY = "share-finder"; - @Private public static final String FREE_SPACE_LIMIT_KEY = "free-space-limit-in-MB-triggering-notification"; - @Private public static final String STOP_ON_NO_SHARE_FOUND_KEY = "stop-on-no-share-found"; + @Private + public static final String SHARE_FINDER_KEY = "share-finder"; + + @Private + public static final String FREE_SPACE_LIMIT_KEY = + "free-space-limit-in-MB-triggering-notification"; + + @Private + public static final String STOP_ON_NO_SHARE_FOUND_KEY = "stop-on-no-share-found"; + + @Private + public static final String VERIFY_CHECKSUM_KEY = "verify-checksum"; private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, EagerShufflingTask.class); private static final Logger notificationLog = LogFactory.getLogger(LogCategory.NOTIFY, EagerShufflingTask.class); - + private static SimpleDataSetInformationDTO findDataSet(List<Share> shares, String dataSetCode) { for (Share share : shares) @@ -89,10 +100,12 @@ public class EagerShufflingTask extends AbstractPostRegistrationTask private final IDataSetMover dataSetMover; + private final IChecksumProvider checksumProvider; + private final ISimpleLogger logger; private final ISimpleLogger notifyer; - + private final File storeRoot; private final String dataStoreCode; @@ -100,29 +113,36 @@ public class EagerShufflingTask extends AbstractPostRegistrationTask private final Set<String> incomingShares; private IShareFinder finder; - + private long freeSpaceLimitTriggeringNotification; - + private boolean stopOnNoShareFound; - + + private boolean verifyChecksum; + public EagerShufflingTask(Properties properties, IEncapsulatedOpenBISService service) { this(properties, IncomingShareIdProvider.getIdsOfIncomingShares(), service, ServiceProvider .getShareIdManager(), new SimpleFreeSpaceProvider(), new DataSetMover(service, ServiceProvider.getShareIdManager()), ServiceProvider.getConfigProvider(), - new Log4jSimpleLogger(operationLog), new Log4jSimpleLogger(notificationLog)); + new HierarchicalContentChecksumProvider( + ServiceProvider.getHierarchicalContentProvider()), new Log4jSimpleLogger( + operationLog), new Log4jSimpleLogger(notificationLog)); } - @Private public EagerShufflingTask(Properties properties, Set<String> incomingShares, + @Private + public EagerShufflingTask(Properties properties, Set<String> incomingShares, IEncapsulatedOpenBISService service, IShareIdManager shareIdManager, IFreeSpaceProvider freeSpaceProvider, IDataSetMover dataSetMover, - IConfigProvider configProvider, ISimpleLogger logger, ISimpleLogger notifyer) + IConfigProvider configProvider, IChecksumProvider checksumProvider, + ISimpleLogger logger, ISimpleLogger notifyer) { super(properties, service); this.incomingShares = incomingShares; this.shareIdManager = shareIdManager; this.freeSpaceProvider = freeSpaceProvider; this.dataSetMover = dataSetMover; + this.checksumProvider = checksumProvider; this.logger = logger; this.notifyer = notifyer; @@ -140,7 +160,9 @@ public class EagerShufflingTask extends AbstractPostRegistrationTask finder = ClassUtils.create(IShareFinder.class, props.getProperty("class"), props); freeSpaceLimitTriggeringNotification = FileUtils.ONE_MB * PropertyUtils.getInt(properties, FREE_SPACE_LIMIT_KEY, 0); - stopOnNoShareFound = PropertyUtils.getBoolean(properties, STOP_ON_NO_SHARE_FOUND_KEY, false); + stopOnNoShareFound = + PropertyUtils.getBoolean(properties, STOP_ON_NO_SHARE_FOUND_KEY, false); + verifyChecksum = PropertyUtils.getBoolean(properties, VERIFY_CHECKSUM_KEY, true); } @Override @@ -149,6 +171,17 @@ public class EagerShufflingTask extends AbstractPostRegistrationTask return true; } + private IChecksumProvider getChecksumProvider() + { + if (verifyChecksum) + { + return checksumProvider; + } else + { + return null; + } + } + @Override public IPostRegistrationTaskExecutor createExecutor(String dataSetCode, boolean container) { @@ -158,14 +191,15 @@ public class EagerShufflingTask extends AbstractPostRegistrationTask } return new Executor(dataSetCode); } - + private final class Executor implements IPostRegistrationTaskExecutor { private final String dataSetCode; private SimpleDataSetInformationDTO dataSet; + private Share shareWithMostFreeOrNull; - + Executor(String dataSetCode) { this.dataSetCode = dataSetCode; @@ -175,8 +209,8 @@ public class EagerShufflingTask extends AbstractPostRegistrationTask public ICleanupTask createCleanupTask() { List<Share> shares = - SegmentedStoreUtils.getDataSetsPerShare(storeRoot, dataStoreCode, incomingShares, - freeSpaceProvider, service, logger); + SegmentedStoreUtils.getDataSetsPerShare(storeRoot, dataStoreCode, + incomingShares, freeSpaceProvider, service, logger); dataSet = findDataSet(shares, dataSetCode); shareWithMostFreeOrNull = finder.tryToFindShare(dataSet, shares); if (shareWithMostFreeOrNull == null) @@ -192,7 +226,7 @@ public class EagerShufflingTask extends AbstractPostRegistrationTask } return new CleanupTask(dataSet, storeRoot, shareWithMostFreeOrNull.getShareId()); } - + @Override public void execute() { @@ -202,7 +236,8 @@ public class EagerShufflingTask extends AbstractPostRegistrationTask File share = new File(storeRoot, shareIdManager.getShareId(dataSetCode)); dataSetMover.moveDataSetToAnotherShare( new File(share, dataSet.getDataSetLocation()), - shareWithMostFreeOrNull.getShare(), logger); + shareWithMostFreeOrNull.getShare(), getChecksumProvider(), logger); + String shareId = shareWithMostFreeOrNull.getShareId(); logger.log(LogLevel.INFO, "Data set " + dataSetCode + " successfully moved from share " + dataSet.getDataSetShareId() + " to " @@ -221,16 +256,17 @@ public class EagerShufflingTask extends AbstractPostRegistrationTask } } } - - + private static final class CleanupTask implements ICleanupTask { private static final long serialVersionUID = 1L; private final SimpleDataSetInformationDTO dataSet; + private final File storeRoot; + private final String newShareId; - + CleanupTask(SimpleDataSetInformationDTO dataSet, File storeRoot, String newShareId) { this.dataSet = dataSet; diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/HierarchicalContentChecksumProvider.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/HierarchicalContentChecksumProvider.java new file mode 100644 index 00000000000..3b75976744e --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/HierarchicalContentChecksumProvider.java @@ -0,0 +1,60 @@ +/* + * Copyright 2012 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.shared; + +import java.io.IOException; + +import ch.systemsx.cisd.common.io.hierarchical_content.api.IHierarchicalContent; +import ch.systemsx.cisd.common.io.hierarchical_content.api.IHierarchicalContentNode; +import ch.systemsx.cisd.openbis.dss.generic.shared.IChecksumProvider; +import ch.systemsx.cisd.openbis.dss.generic.shared.IHierarchicalContentProvider; + +/** + * Checksum provider that uses a hierarchical content provider to get checksums. + * + * @author pkupczyk + */ +public class HierarchicalContentChecksumProvider implements IChecksumProvider +{ + + private IHierarchicalContentProvider hierarchicalContentProvider; + + public HierarchicalContentChecksumProvider( + IHierarchicalContentProvider hierarchicalContentProvider) + { + this.hierarchicalContentProvider = hierarchicalContentProvider; + } + + @Override + public long getChecksum(String dataSetCode, String relativePath) throws IOException + { + IHierarchicalContent content = null; + + try + { + content = hierarchicalContentProvider.asContent(dataSetCode); + IHierarchicalContentNode node = content.getNode(relativePath); + return node.getChecksumCRC32(); + } finally + { + if (content != null) + { + content.close(); + } + } + } +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/IChecksumProvider.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/IChecksumProvider.java new file mode 100644 index 00000000000..c26b460a590 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/IChecksumProvider.java @@ -0,0 +1,31 @@ +/* + * Copyright 2012 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.shared; + +import java.io.IOException; + +/** + * Provides checksums of data set files. + * + * @author pkupczyk + */ +public interface IChecksumProvider +{ + + public long getChecksum(String dataSetCode, String relativePath) throws IOException; + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/SegmentedStoreUtils.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/SegmentedStoreUtils.java index e040a2c66ae..0f6e3d15031 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/SegmentedStoreUtils.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/SegmentedStoreUtils.java @@ -45,6 +45,7 @@ import ch.systemsx.cisd.common.logging.LogLevel; import ch.systemsx.cisd.common.utilities.ITimeProvider; import ch.systemsx.cisd.common.utilities.SystemTimeProvider; import ch.systemsx.cisd.openbis.dss.generic.shared.IDataSetDirectoryProvider; +import ch.systemsx.cisd.openbis.dss.generic.shared.IChecksumProvider; import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService; import ch.systemsx.cisd.openbis.dss.generic.shared.IShareIdManager; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData; @@ -270,10 +271,11 @@ public class SegmentedStoreUtils * </ol> * * @param service to access openBIS AS. + * @param checksumProvider */ public static void moveDataSetToAnotherShare(final File dataSetDirInStore, File share, IEncapsulatedOpenBISService service, final IShareIdManager shareIdManager, - final ISimpleLogger logger) + IChecksumProvider checksumProvider, final ISimpleLogger logger) { final String dataSetCode = dataSetDirInStore.getName(); ExternalData dataSet = service.tryGetDataSet(dataSetCode); @@ -295,7 +297,9 @@ public class SegmentedStoreUtils File dataSetDirInNewShare = new File(share, relativePath); dataSetDirInNewShare.mkdirs(); copyToShare(dataSetDirInStore, dataSetDirInNewShare, logger); - long size = assertEqualSizeAndChildren(dataSetDirInStore, dataSetDirInNewShare); + long size = + assertEqualSizeAndChildren(dataSetCode, dataSetDirInStore, dataSetDirInStore, + dataSetDirInNewShare, dataSetDirInNewShare, checksumProvider); String shareId = share.getName(); service.updateShareIdAndSize(dataSetCode, shareId, size); shareIdManager.setShareId(dataSetCode, shareId); @@ -398,13 +402,15 @@ public class SegmentedStoreUtils share.getPath(), (System.currentTimeMillis() - start) / 1000.0)); } - private static long assertEqualSizeAndChildren(File source, File destination) + private static long assertEqualSizeAndChildren(String dataSetCode, File sourceRoot, + File source, File destinationRoot, File destination, IChecksumProvider checksumProvider) { assertSameName(source, destination); if (source.isFile()) { assertFile(destination); - return assertSameSizeAndCheckSum(source, destination); + return assertSameSizeAndCheckSum(dataSetCode, sourceRoot, source, destinationRoot, + destination, checksumProvider); } else { assertDirectory(destination); @@ -414,7 +420,9 @@ public class SegmentedStoreUtils long sum = 0; for (int i = 0; i < sourceFiles.length; i++) { - sum += assertEqualSizeAndChildren(sourceFiles[i], destinationFiles[i]); + sum += + assertEqualSizeAndChildren(dataSetCode, sourceRoot, sourceFiles[i], + destinationRoot, destinationFiles[i], checksumProvider); } return sum; } @@ -432,7 +440,8 @@ public class SegmentedStoreUtils } } - private static long assertSameSizeAndCheckSum(File source, File destination) + private static long assertSameSizeAndCheckSum(String dataSetCode, File sourceRoot, File source, + File destinationRoot, File destination, IChecksumProvider checksumProvider) { long sourceSize = source.length(); long destinationSize = destination.length(); @@ -443,23 +452,37 @@ public class SegmentedStoreUtils + " but source file '" + source.getAbsolutePath() + "' has size " + sourceSize + "."); } - long sourceChecksum = calculateCRC(source); - long destinationChecksum = calculateCRC(destination); - if (sourceChecksum != destinationChecksum) + + if (checksumProvider != null) { - throw new EnvironmentFailureException("Destination file '" - + destination.getAbsolutePath() + "' has checksum " + destinationChecksum - + " but source file '" + source.getAbsolutePath() + "' has checksum " - + sourceChecksum + "."); + try + { + long sourceChecksum = + checksumProvider.getChecksum(dataSetCode, + FileUtilities.getRelativeFilePath(sourceRoot, source)); + long destinationChecksum = calculateCRC(destination); + + if (sourceChecksum != destinationChecksum) + { + throw new EnvironmentFailureException("Destination file '" + + destination.getAbsolutePath() + "' has checksum " + + destinationChecksum + " but source file '" + source.getAbsolutePath() + + "' has checksum " + sourceChecksum + "."); + } + } catch (IOException ex) + { + throw CheckedExceptionTunnel.wrapIfNecessary(ex); + } } + return sourceSize; } - private static long calculateCRC(File file) + private static int calculateCRC(File file) { try { - return FileUtils.checksumCRC32(file); + return (int) FileUtils.checksumCRC32(file); } catch (IOException ex) { throw CheckedExceptionTunnel.wrapIfNecessary(ex); diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/plugins/SimpleShufflingTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/plugins/SimpleShufflingTest.java index 55ef886ac3e..376eaf9bf3d 100644 --- a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/plugins/SimpleShufflingTest.java +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/plugins/SimpleShufflingTest.java @@ -162,7 +162,7 @@ public class SimpleShufflingTest extends AbstractFileSystemTestCase }); eagerShufflingTask = new EagerShufflingTask(properties, new HashSet<String>(Arrays.asList("1", "2")), - service, shareIdManager, spaceProvider, dataSetMover, configProvider, + service, shareIdManager, spaceProvider, dataSetMover, configProvider, null, logger, notifyer); balancer = new SimpleShuffling(properties, eagerShufflingTask); } @@ -238,7 +238,7 @@ public class SimpleShufflingTest extends AbstractFileSystemTestCase one(dataSetMover).moveDataSetToAnotherShare( new File(share1.getShare(), STORE_PATH + "ds3"), share3.getShare(), - logger); + null, logger); one(logger).log(LogLevel.INFO, "Data set ds3 successfully moved from share 1 to 3."); @@ -248,7 +248,7 @@ public class SimpleShufflingTest extends AbstractFileSystemTestCase one(dataSetMover).moveDataSetToAnotherShare( new File(share1.getShare(), STORE_PATH + "ds2"), share4.getShare(), - logger); + null, logger); one(logger).log(LogLevel.INFO, "Data set ds2 successfully moved from share 1 to 4."); diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/postregistration/EagerShufflingTaskTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/postregistration/EagerShufflingTaskTest.java index bfca0ac9e37..d945feaaf3d 100644 --- a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/postregistration/EagerShufflingTaskTest.java +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/postregistration/EagerShufflingTaskTest.java @@ -44,6 +44,7 @@ import ch.systemsx.cisd.common.logging.ISimpleLogger; import ch.systemsx.cisd.common.logging.LogLevel; import ch.systemsx.cisd.common.test.RecordingMatcher; import ch.systemsx.cisd.etlserver.plugins.IDataSetMover; +import ch.systemsx.cisd.openbis.dss.generic.shared.IChecksumProvider; import ch.systemsx.cisd.openbis.dss.generic.shared.IConfigProvider; import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService; import ch.systemsx.cisd.openbis.dss.generic.shared.IShareIdManager; @@ -51,19 +52,19 @@ import ch.systemsx.cisd.openbis.generic.shared.Constants; import ch.systemsx.cisd.openbis.generic.shared.dto.SimpleDataSetInformationDTO; /** - * - * * @author Franz-Josef Elmer */ -@Friend(toClasses=EagerShufflingTask.class) +@Friend(toClasses = EagerShufflingTask.class) public class EagerShufflingTaskTest extends AbstractFileSystemTestCase { private static final class MockFreeSpaceProvider implements IFreeSpaceProvider { private final List<String> shares = new ArrayList<String>(); + private Integer[] freeSpaceValues; + private int index; - + void setFreeSpaceValues(Integer... freeSpaceValues) { this.freeSpaceValues = freeSpaceValues; @@ -76,26 +77,43 @@ public class EagerShufflingTaskTest extends AbstractFileSystemTestCase return freeSpaceValues[index++ % freeSpaceValues.length]; } } - + private static final String SHARDING = "sharding/"; + private static final String DATA_STORE_SERVER_CODE = "DSS"; + private static final String DATA_SET_CODE1 = "ds-1"; + private BufferedAppender logRecorder; + private Mockery context; private IEncapsulatedOpenBISService service; + private IShareIdManager shareIdManager; + private MockFreeSpaceProvider freeSpaceProvider; + private IDataSetMover dataSetMover; + private IChecksumProvider checksumProvider; + private IConfigProvider configProvider; + private ISimpleLogger logger; + private ISimpleLogger notifyer; + private File store; + private File share1; + private File share2; + private File share3; + private File share4; + private File ds1File; @BeforeMethod @@ -108,6 +126,7 @@ public class EagerShufflingTaskTest extends AbstractFileSystemTestCase freeSpaceProvider = new MockFreeSpaceProvider(); configProvider = context.mock(IConfigProvider.class); dataSetMover = context.mock(IDataSetMover.class); + checksumProvider = context.mock(IChecksumProvider.class); logger = context.mock(ISimpleLogger.class, "logger"); notifyer = context.mock(ISimpleLogger.class, "notifyer"); store = new File(workingDirectory.getAbsolutePath(), "store"); @@ -124,7 +143,7 @@ public class EagerShufflingTaskTest extends AbstractFileSystemTestCase share4 = new File(store, "4"); share4.mkdir(); } - + @AfterMethod(alwaysRun = true) public void afterMethod() { @@ -138,7 +157,9 @@ public class EagerShufflingTaskTest extends AbstractFileSystemTestCase public void testShufflingIntoAnExtensionShare() { prepareConfigProvider(); - EagerShufflingTask task = createTask(); + Properties properties = createDefaultProperties(); + properties.setProperty(EagerShufflingTask.VERIFY_CHECKSUM_KEY, "true"); + EagerShufflingTask task = createTask(properties); freeSpaceProvider.setFreeSpaceValues(200, 100, 300, 400, 400, 300); prepareListDataSets(); prepareGetShareId(); @@ -146,14 +167,15 @@ public class EagerShufflingTaskTest extends AbstractFileSystemTestCase context.checking(new Expectations() { { - one(dataSetMover).moveDataSetToAnotherShare(ds1File, share4, logger); + one(dataSetMover).moveDataSetToAnotherShare(ds1File, share4, checksumProvider, + logger); } }); - + IPostRegistrationTaskExecutor executor = task.createExecutor(DATA_SET_CODE1, false); executor.createCleanupTask(); executor.execute(); - + assertEquals("Data set ds-1 successfully moved from share 1 to 4.", infoMessageMatcher.recordedObject()); assertEquals("[1, 2, 3, 4, 4, 4]", freeSpaceProvider.shares.toString()); @@ -172,7 +194,8 @@ public class EagerShufflingTaskTest extends AbstractFileSystemTestCase context.checking(new Expectations() { { - one(dataSetMover).moveDataSetToAnotherShare(ds1File, share2, logger); + one(dataSetMover).moveDataSetToAnotherShare(ds1File, share2, checksumProvider, + logger); } }); @@ -185,7 +208,7 @@ public class EagerShufflingTaskTest extends AbstractFileSystemTestCase assertEquals("[1, 2, 3, 4, 2, 2]", freeSpaceProvider.shares.toString()); context.assertIsSatisfied(); } - + @Test public void testShufflingButNoShareFoundExceptTheOwnOne() { @@ -195,23 +218,24 @@ public class EagerShufflingTaskTest extends AbstractFileSystemTestCase prepareListDataSets(); RecordingMatcher<String> logMessageMatcher = prepareLogging(LogLevel.WARN); - + IPostRegistrationTaskExecutor executor = task.createExecutor(DATA_SET_CODE1, false); executor.createCleanupTask(); executor.execute(); - + assertEquals("No share found for shuffling data set ds-1.", logMessageMatcher.recordedObject()); assertEquals("[1, 2, 3, 4, 1, 2, 3, 4]", freeSpaceProvider.shares.toString()); context.assertIsSatisfied(); } - + @Test public void testShufflingWithNotification() { prepareConfigProvider(); Properties properties = createDefaultProperties(); properties.setProperty(EagerShufflingTask.FREE_SPACE_LIMIT_KEY, "1"); + properties.setProperty(EagerShufflingTask.VERIFY_CHECKSUM_KEY, "false"); EagerShufflingTask task = createTask(properties); freeSpaceProvider.setFreeSpaceValues(200, 1234, 10, 0, 1234, 900); prepareListDataSets(); @@ -221,7 +245,7 @@ public class EagerShufflingTaskTest extends AbstractFileSystemTestCase context.checking(new Expectations() { { - one(dataSetMover).moveDataSetToAnotherShare(ds1File, share2, logger); + one(dataSetMover).moveDataSetToAnotherShare(ds1File, share2, null, logger); one(notifyer).log(with(LogLevel.WARN), with(notificationRecorder)); } }); @@ -232,8 +256,10 @@ public class EagerShufflingTaskTest extends AbstractFileSystemTestCase assertEquals("Data set ds-1 successfully moved from share 1 to 2.", infoMessageMatcher.recordedObject()); - assertEquals("After moving data set ds-1 to share 2 that share has only 900.00 KB free space. " + - "It might be necessary to add a new share.", notificationRecorder.recordedObject()); + assertEquals( + "After moving data set ds-1 to share 2 that share has only 900.00 KB free space. " + + "It might be necessary to add a new share.", + notificationRecorder.recordedObject()); assertEquals("[1, 2, 3, 4, 2, 2]", freeSpaceProvider.shares.toString()); context.assertIsSatisfied(); } @@ -264,37 +290,38 @@ public class EagerShufflingTaskTest extends AbstractFileSystemTestCase { assertEquals("No share found for shuffling data set ds-1.", ex.getMessage()); } - - assertEquals("No share found for shuffling data set ds-1.", notificationRecorder.recordedObject()); + + assertEquals("No share found for shuffling data set ds-1.", + notificationRecorder.recordedObject()); assertEquals("[1, 2, 3, 4, 1, 2, 3, 4]", freeSpaceProvider.shares.toString()); context.assertIsSatisfied(); } - + private RecordingMatcher<String> prepareLogging(final LogLevel level) { final RecordingMatcher<String> logMessageMatcher = new RecordingMatcher<String>(); context.checking(new Expectations() - { { - one(logger).log(with(level), with(logMessageMatcher)); - } - }); + { + one(logger).log(with(level), with(logMessageMatcher)); + } + }); return logMessageMatcher; } - + private void prepareListDataSets() { context.checking(new Expectations() - { { - one(service).listDataSets(); - will(returnValue(Arrays.asList(dataSet("1", DATA_SET_CODE1)))); - one(logger).log( - with(LogLevel.INFO), - with(Matchers.startsWith("Obtained the list of all " - + "datasets in all shares"))); - } - }); + { + one(service).listDataSets(); + will(returnValue(Arrays.asList(dataSet("1", DATA_SET_CODE1)))); + one(logger).log( + with(LogLevel.INFO), + with(Matchers.startsWith("Obtained the list of all " + + "datasets in all shares"))); + } + }); } private void prepareConfigProvider() @@ -313,14 +340,14 @@ public class EagerShufflingTaskTest extends AbstractFileSystemTestCase private void prepareGetShareId() { context.checking(new Expectations() - { { - one(shareIdManager).getShareId(DATA_SET_CODE1); - will(returnValue("1")); - } - }); + { + one(shareIdManager).getShareId(DATA_SET_CODE1); + will(returnValue("1")); + } + }); } - + private SimpleDataSetInformationDTO dataSet(String shareId, String dataSetCode) { SimpleDataSetInformationDTO dataSet = new SimpleDataSetInformationDTO(); @@ -350,7 +377,7 @@ public class EagerShufflingTaskTest extends AbstractFileSystemTestCase { return new EagerShufflingTask(properties, new LinkedHashSet<String>(Arrays.asList("1", "2")), service, shareIdManager, - freeSpaceProvider, dataSetMover, configProvider, logger, notifyer); + freeSpaceProvider, dataSetMover, configProvider, checksumProvider, logger, notifyer); } - + } diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/SegmentedStoreUtilsTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/SegmentedStoreUtilsTest.java index eeca7ee9207..a2cb81661b7 100644 --- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/SegmentedStoreUtilsTest.java +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/SegmentedStoreUtilsTest.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.apache.commons.io.FileUtils; import org.jmock.Expectations; import org.jmock.Mockery; import org.testng.annotations.AfterMethod; @@ -32,12 +33,14 @@ import org.testng.annotations.Test; import ch.rinn.restrictions.Friend; import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase; import ch.systemsx.cisd.common.concurrent.MessageChannel; +import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; import ch.systemsx.cisd.common.filesystem.FileUtilities; import ch.systemsx.cisd.common.filesystem.HostAwareFile; import ch.systemsx.cisd.common.filesystem.IFreeSpaceProvider; import ch.systemsx.cisd.common.logging.MockLogger; import ch.systemsx.cisd.common.test.RecordingMatcher; import ch.systemsx.cisd.common.utilities.ITimeProvider; +import ch.systemsx.cisd.openbis.dss.generic.shared.IChecksumProvider; import ch.systemsx.cisd.openbis.dss.generic.shared.IDataSetDirectoryProvider; import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService; import ch.systemsx.cisd.openbis.dss.generic.shared.IShareIdManager; @@ -54,11 +57,13 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SimpleDataSetInformationDTO; public class SegmentedStoreUtilsTest extends AbstractFileSystemTestCase { private static final String MOVED = "Moved"; + private static final String LOCK = "lock"; + private static final String START_DELETION = "Start deletion"; private static final String DATA_STORE_CODE = "ds-code"; - + private Mockery context; private IEncapsulatedOpenBISService service; @@ -75,6 +80,8 @@ public class SegmentedStoreUtilsTest extends AbstractFileSystemTestCase private IDataSetDirectoryProvider dataSetDirectoryProvider; + private IChecksumProvider checksumProvider; + private File store; @BeforeMethod @@ -87,6 +94,7 @@ public class SegmentedStoreUtilsTest extends AbstractFileSystemTestCase timeProvider = context.mock(ITimeProvider.class); datasetLocation = context.mock(IDatasetLocation.class); dataSetDirectoryProvider = context.mock(IDataSetDirectoryProvider.class); + checksumProvider = context.mock(IChecksumProvider.class); context.checking(new Expectations() { @@ -195,23 +203,26 @@ public class SegmentedStoreUtilsTest extends AbstractFileSystemTestCase } @Test - public void testMoveDataSetToAnothetShareAndDelete() + public void testMoveDataSetToAnothetShareAndDelete() throws IOException { File share1 = new File(workingDirectory, "store/1"); File share1uuid01 = new File(share1, "uuid/01"); File ds2 = new File(share1uuid01, "0b/0c/ds-2/original"); ds2.mkdirs(); - FileUtilities.writeToFile(new File(ds2, "read.me"), "do nothing"); + final File readmeFile = new File(ds2, "read.me"); + FileUtilities.writeToFile(readmeFile, "do nothing"); final File dataSetDirInStore = new File(share1uuid01, "02/03/ds-1"); File original = new File(dataSetDirInStore, "original"); original.mkdirs(); - FileUtilities.writeToFile(new File(original, "hello.txt"), "hello world"); + final File helloFile = new File(original, "hello.txt"); + FileUtilities.writeToFile(helloFile, "hello world"); final File share2 = new File(workingDirectory, "store/2"); share2.mkdirs(); File share2uuid01 = new File(share2, "uuid/01"); File file = new File(share2uuid01, "22/33/orig"); file.mkdirs(); - FileUtilities.writeToFile(new File(file, "hi.txt"), "hi"); + final File hiFile = new File(file, "hi.txt"); + FileUtilities.writeToFile(hiFile, "hi"); context.checking(new Expectations() { { @@ -230,6 +241,9 @@ public class SegmentedStoreUtilsTest extends AbstractFileSystemTestCase one(dataSetDirectoryProvider).getDataSetDirectory(datasetLocation); will(returnValue(new File( "targets/unit-test-wd/ch.systemsx.cisd.openbis.dss.generic.shared.utils.SegmentedStoreUtilsTest/store/2/uuid/01/02/03/ds-1"))); + + one(checksumProvider).getChecksum("ds-1", "original/hello.txt"); + will(returnValue(FileUtils.checksumCRC32(helloFile))); } }); assertEquals(true, dataSetDirInStore.exists()); @@ -252,7 +266,7 @@ public class SegmentedStoreUtilsTest extends AbstractFileSystemTestCase moveChannel.send(LOCK); deletionChannel.assertNextMessage(START_DELETION); } - }, log); + }, checksumProvider, log); moveChannel.send(MOVED); } }).start(); @@ -332,7 +346,7 @@ public class SegmentedStoreUtilsTest extends AbstractFileSystemTestCase assertFileNames(share2uuid01, "22"); SegmentedStoreUtils.moveDataSetToAnotherShare(dataSetDirInStore, share2, service, - shareIdManager, log); + shareIdManager, null, log); log.assertNextLogMessage("Start moving directory 'targets/unit-test-wd/ch.systemsx.cisd." + "openbis.dss.generic.shared.utils.SegmentedStoreUtilsTest/store/1/uuid/01/02/03/ds-1' " @@ -354,6 +368,38 @@ public class SegmentedStoreUtilsTest extends AbstractFileSystemTestCase log.assertNoMoreLogMessages(); } + @Test(groups = "slow", expectedExceptions = EnvironmentFailureException.class) + public void testMoveDataSetToAnotherShareWithDifferentChecksums() throws IOException + { + File share1 = new File(workingDirectory, "store/1"); + File share1uuid01 = new File(share1, "uuid/01"); + File dataSetDirInStore = new File(share1uuid01, "02/03/ds-1"); + File original = new File(dataSetDirInStore, "original"); + original.mkdirs(); + final File helloFile = new File(original, "hello.txt"); + FileUtilities.writeToFile(helloFile, "hello world"); + File share2 = new File(workingDirectory, "store/2"); + + context.checking(new Expectations() + { + { + one(service).tryGetDataSet("ds-1"); + will(returnValue(new DataSet())); + + one(shareIdManager).lock("ds-1"); + one(shareIdManager).releaseLock("ds-1"); + + one(checksumProvider).getChecksum("ds-1", "original/hello.txt"); + will(returnValue(1L)); + } + }); + + SegmentedStoreUtils.moveDataSetToAnotherShare(dataSetDirInStore, share2, service, + shareIdManager, checksumProvider, log); + + fail(); + } + @Test public void testCleanupOld() { @@ -461,4 +507,5 @@ public class SegmentedStoreUtilsTest extends AbstractFileSystemTestCase dataSet.setDataSetSize(size); return dataSet; } + } -- GitLab