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 f9f23f8f157135f0ed49317af089372c0c5d0137..4fab86835d92c2da74b87c8fdd37c7f8fc9f29fc 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 @@ -1620,12 +1620,14 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements // in the first pass register samples without container to avoid dependency inversion BatchOperationExecutor.executeInBatches(new SampleBatchRegistration(sampleTable, - containerSamples, registratorOrNull), progress, "createContainerSamples"); + containerSamples, registratorOrNull), getBatchSize(operationDetails), progress, + "createContainerSamples"); // register samples with a container identifier // (container should have been created in the first pass) BatchOperationExecutor.executeInBatches(new SampleBatchRegistration(sampleTable, - containedSamples, registratorOrNull), progress, "createContainedSamples"); + containedSamples, registratorOrNull), getBatchSize(operationDetails), progress, + "createContainedSamples"); return newSamples.size(); } @@ -1662,7 +1664,7 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements final ISampleTable sampleTable = businessObjectFactory.createSampleTable(session); sampleTable.checkBeforeUpdate(sampleUpdates); BatchOperationExecutor.executeInBatches(new SampleUpdate(sampleTable, sampleUpdates), - progress, "updateSamples"); + getBatchSize(operationDetails), progress, "updateSamples"); return sampleUpdateCount; } @@ -1751,7 +1753,8 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements final IDataSetTable dataSetTable = businessObjectFactory.createDataSetTable(session); dataSetTable.checkBeforeUpdate(dataSetUpdates); BatchOperationExecutor.executeInBatches( - new DataSetBatchUpdate(dataSetTable, dataSetUpdates), progress, "updateDataSets"); + new DataSetBatchUpdate(dataSetTable, dataSetUpdates), + getBatchSize(operationDetails), progress, "updateDataSets"); return dataSetUpdatesCount; } @@ -2057,4 +2060,11 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements return null; } } + + private int getBatchSize(AtomicEntityOperationDetails details) + { + return details == null || details.getBatchSizeOrNull() == null ? BatchOperationExecutor + .getDefaultBatchSize() : details.getBatchSizeOrNull(); + } + } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/batch/BatchOperationExecutor.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/batch/BatchOperationExecutor.java index cc36cabd0bfa58a5f37c4bac3f5ccb5b6f550ef3..ca94d6ef787e6a23ad3ccfee2d510f1f6172aefe 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/batch/BatchOperationExecutor.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/batch/BatchOperationExecutor.java @@ -84,6 +84,11 @@ public class BatchOperationExecutor } } + public static int getDefaultBatchSize() + { + return DEFAULT_BATCH_SIZE; + } + private static void notifyProgressListener(IProgressListener progressListenerOrNull, String progressPhaseOrNull, int maxIndex, int currentIndex) { diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/AtomicEntityOperationDetails.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/AtomicEntityOperationDetails.java index d85b3b4a0627063b762ebb06a1c7cc2e99dddb7a..e75942fd576a61a14dd051a6d59a66df2caca400 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/AtomicEntityOperationDetails.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/AtomicEntityOperationDetails.java @@ -68,6 +68,8 @@ public class AtomicEntityOperationDetails implements Serializable private final List<DataSetBatchUpdatesDTO> dataSetUpdates; + private Integer batchSizeOrNull; + public AtomicEntityOperationDetails(TechId registrationId, String userIdOrNull, List<NewSpace> spaceRegistrations, List<NewProject> projectRegistrations, List<NewExperiment> experimentRegistrations, List<SampleUpdatesDTO> sampleUpdates, @@ -89,6 +91,20 @@ public class AtomicEntityOperationDetails implements Serializable this.dataSetUpdates = new ArrayList<DataSetBatchUpdatesDTO>(dataSetUpdates); } + public AtomicEntityOperationDetails(TechId registrationId, String userIdOrNull, + List<NewSpace> spaceRegistrations, List<NewProject> projectRegistrations, + List<NewExperiment> experimentRegistrations, List<SampleUpdatesDTO> sampleUpdates, + List<NewSample> sampleRegistrations, + Map<String, List<NewMaterial>> materialRegistrations, + List<? extends NewExternalData> dataSetRegistrations, + List<DataSetBatchUpdatesDTO> dataSetUpdates, Integer batchSizeOrNull) + { + this(registrationId, userIdOrNull, spaceRegistrations, projectRegistrations, + experimentRegistrations, sampleUpdates, sampleRegistrations, materialRegistrations, + dataSetRegistrations, dataSetUpdates); + this.batchSizeOrNull = batchSizeOrNull; + } + public TechId getRegistrationIdOrNull() { return registrationIdOrNull; @@ -144,6 +160,11 @@ public class AtomicEntityOperationDetails implements Serializable return materialRegistrations; } + public Integer getBatchSizeOrNull() + { + return batchSizeOrNull; + } + @Override public String toString() { diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceDatabaseTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceDatabaseTest.java index 3641ffc386180c22aac9f3d07e39a1c042f64118..ca7987900ab637dc9a3557359f70b816547ebeea 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceDatabaseTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceDatabaseTest.java @@ -35,6 +35,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; import ch.systemsx.cisd.openbis.generic.server.dataaccess.db.AbstractDAOTest; import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService; import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria; @@ -45,6 +46,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentFetchOption; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentFetchOptions; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityPropertiesHolder; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ListSampleCriteria; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewAttachment; @@ -140,53 +142,56 @@ public class ETLServiceDatabaseTest extends AbstractDAOTest @Test public void testPerformEntityOperationsUpdateSample() { - // Find the samples to add - SearchCriteria searchCriteria = new SearchCriteria(); - searchCriteria.addMatchClause(SearchCriteria.MatchClause.createAttributeMatch( - MatchClauseAttribute.CODE, "3VCP7")); - List<Sample> samplesToUpdate = service.searchForSamples(sessionToken, searchCriteria); - assertEquals(1, samplesToUpdate.size()); - Sample sampleToUpdate = samplesToUpdate.get(0); + Sample sample = findSampleByCode("3VCP7"); // Update the comment - String newComment = "This is a new comment. This is not the old comment."; - String oldComment = EntityHelper.tryFindPropertyValue(sampleToUpdate, "COMMENT"); - assertFalse(newComment.equals(oldComment)); - EntityHelper.createOrUpdateProperty(sampleToUpdate, "COMMENT", newComment); + String sampleComment = "This is a new comment for a sample."; + updateEntityProperty(sample, "COMMENT", sampleComment); // Update the parents - SearchCriteria parentSearchCriteria = new SearchCriteria(); - parentSearchCriteria.addMatchClause(SearchCriteria.MatchClause.createAttributeMatch( - MatchClauseAttribute.CODE, "3V-126")); - List<Sample> parentsToAdd = service.searchForSamples(sessionToken, parentSearchCriteria); - assertEquals(1, parentsToAdd.size()); - Sample parentToAdd = parentsToAdd.get(0); - assertEquals(1, sampleToUpdate.getParents().size()); - sampleToUpdate.addParent(parentToAdd); + Sample parent = findSampleByCode("3V-126"); + String parentComment = "This is a new comment for a parent."; + updateEntityProperty(parent, "COMMENT", parentComment); + assertEquals(1, sample.getParents().size()); + sample.addParent(parent); - performSampleUpdate(sampleToUpdate); + SampleUpdatesDTO parentUpdate = convertToSampleUpdateDTO(parent); + SampleUpdatesDTO sampleUpdate = convertToSampleUpdateDTO(sample); + performSampleUpdate(Arrays.asList(sampleUpdate, parentUpdate), 1); // Now retrieve the sample again and check that the properties were updated. - List<Sample> updatedSamples = service.searchForSamples(sessionToken, searchCriteria); - assertEquals(1, updatedSamples.size()); - Sample updatedSample = updatedSamples.get(0); + Sample updatedSample = findSampleByCode("3VCP7"); + Sample updatedParent = findSampleByCode("3V-126"); + assertTrue("The modification date should have been updated", updatedSample - .getModificationDate().compareTo(sampleToUpdate.getModificationDate()) > 0); - assertEquals(newComment, EntityHelper.tryFindPropertyValue(updatedSample, "COMMENT")); + .getModificationDate().compareTo(sample.getModificationDate()) > 0); + assertTrue("The modification date should have been updated", updatedParent + .getModificationDate().compareTo(sample.getModificationDate()) > 0); + assertEquals(sampleComment, EntityHelper.tryFindPropertyValue(updatedSample, "COMMENT")); + assertEquals(parentComment, EntityHelper.tryFindPropertyValue(updatedParent, "COMMENT")); assertEquals(2, updatedSample.getParents().size()); } + @Test(expectedExceptions = EnvironmentFailureException.class) + public void testPerformEntityOperationsUpdateStaleSample() + { + Sample sampleToUpdate = findSampleByCode("3VCP7"); + + // Update the comment + String newComment = "This is a new comment. This is not the old comment."; + updateEntityProperty(sampleToUpdate, "COMMENT", newComment); + + SampleUpdatesDTO sampleUpdate = convertToSampleUpdateDTO(sampleToUpdate); + sampleUpdate.setVersion(new Date()); + performSampleUpdate(Arrays.asList(sampleUpdate), 1); + } + @SuppressWarnings("null") @Test public void testPerformEntityOperationsCreateSample() { // Get the parents - SearchCriteria parentSearchCriteria = new SearchCriteria(); - parentSearchCriteria.addMatchClause(SearchCriteria.MatchClause.createAttributeMatch( - MatchClauseAttribute.CODE, "3VCP7")); - List<Sample> parentsToAdd = service.searchForSamples(sessionToken, parentSearchCriteria); - assertEquals(1, parentsToAdd.size()); - Sample parent = parentsToAdd.get(0); + Sample parent = findSampleByCode("3VCP7"); NewSample sampleToCreate = new NewSample(); String newSampleIdentifier = "/" + parent.getSpace().getCode() + "/" + "NEW-SAMPLE"; @@ -251,7 +256,7 @@ public class ETLServiceDatabaseTest extends AbstractDAOTest dataSetUpdates.add(updates); } - performDataSetUpdates(dataSetUpdates); + performDataSetUpdates(dataSetUpdates, 1); // Now retrieve the sample again and check that the properties were updated. List<ExternalData> updatedDataSets = @@ -284,7 +289,21 @@ public class ETLServiceDatabaseTest extends AbstractDAOTest } - private void performDataSetUpdates(List<DataSetBatchUpdatesDTO> dataSetUpdates) + @Test(expectedExceptions = EnvironmentFailureException.class) + public void testPerformEntityOperationsUpdateStaleDataSet() + { + ExternalData dataSetToUpdate = findDatasetByCode("20081105092159188-3"); + + DataSetBatchUpdatesDTO update = + createDataSetUpdateDTO(dataSetToUpdate, "COMMENT", + "This is a new comment. This is not the old comment."); + + update.setVersion(new Date()); + performDataSetUpdates(Arrays.asList(update), 1); + } + + private void performDataSetUpdates(List<DataSetBatchUpdatesDTO> dataSetUpdates, + Integer batchSizeOrNull) { TechId registrationid = new TechId(service.drawANewUniqueID(sessionToken)); List<NewSpace> spaceRegistrations = Collections.emptyList(); @@ -299,19 +318,17 @@ public class ETLServiceDatabaseTest extends AbstractDAOTest new AtomicEntityOperationDetails(registrationid, null, spaceRegistrations, projectRegistrations, experimentRegistrations, sampleUpdates, sampleRegistrations, materialRegistrations, dataSetRegistrations, - dataSetUpdates); + dataSetUpdates, batchSizeOrNull); service.performEntityOperations(sessionToken, details); } - private void performSampleUpdate(Sample sampleToUpdate) + private void performSampleUpdate(List<SampleUpdatesDTO> sampleUpdates, Integer batchSizeOrNull) { TechId registrationid = new TechId(service.drawANewUniqueID(sessionToken)); List<NewSpace> spaceRegistrations = Collections.emptyList(); List<NewProject> projectRegistrations = Collections.emptyList(); List<NewExperiment> experimentRegistrations = Collections.emptyList(); - SampleUpdatesDTO sampleUpdate = convertToSampleUpdateDTO(sampleToUpdate); - List<SampleUpdatesDTO> sampleUpdates = Arrays.asList(sampleUpdate); List<NewSample> sampleRegistrations = Collections.emptyList(); Map<String, List<NewMaterial>> materialRegistrations = Collections.emptyMap(); List<? extends NewExternalData> dataSetRegistrations = Collections.emptyList(); @@ -320,7 +337,7 @@ public class ETLServiceDatabaseTest extends AbstractDAOTest new AtomicEntityOperationDetails(registrationid, null, spaceRegistrations, projectRegistrations, experimentRegistrations, sampleUpdates, sampleRegistrations, materialRegistrations, dataSetRegistrations, - dataSetUpdates); + dataSetUpdates, batchSizeOrNull); service.performEntityOperations(sessionToken, details); } @@ -479,4 +496,33 @@ public class ETLServiceDatabaseTest extends AbstractDAOTest assertFalse(result.contains("/TESTGROUP/SAMPLE_EXAMPLE")); } + + private Sample findSampleByCode(String code) + { + SearchCriteria searchCriteria = new SearchCriteria(); + searchCriteria.addMatchClause(SearchCriteria.MatchClause.createAttributeMatch( + MatchClauseAttribute.CODE, code)); + List<Sample> samples = service.searchForSamples(sessionToken, searchCriteria); + assertEquals(1, samples.size()); + return samples.get(0); + } + + private ExternalData findDatasetByCode(String code) + { + SearchCriteria searchCriteria = new SearchCriteria(); + searchCriteria.addMatchClause(SearchCriteria.MatchClause.createAttributeMatch( + MatchClauseAttribute.CODE, code)); + List<ExternalData> dataSets = service.searchForDataSets(sessionToken, searchCriteria); + assertEquals(1, dataSets.size()); + return dataSets.get(0); + } + + private void updateEntityProperty(IEntityPropertiesHolder entity, String propertyName, + String propertyValue) + { + String oldValue = EntityHelper.tryFindPropertyValue(entity, propertyName); + assertFalse(propertyValue.equals(oldValue)); + EntityHelper.createOrUpdateProperty(entity, propertyName, propertyValue); + } + }