diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractSampleBusinessObject.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractSampleBusinessObject.java index b289839ac758d7f2ee5991e357ec2dc5b1f79da0..a939ff3f69c2d753e2ec7ba5ba65616359266690 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractSampleBusinessObject.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractSampleBusinessObject.java @@ -18,6 +18,7 @@ package ch.systemsx.cisd.openbis.generic.server.business.bo; import java.util.List; import java.util.Map; +import java.util.Set; import ch.systemsx.cisd.common.exceptions.UserFailureException; import ch.systemsx.cisd.openbis.generic.server.business.bo.util.SampleOwner; @@ -26,9 +27,11 @@ import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory; import ch.systemsx.cisd.openbis.generic.server.dataaccess.IExternalDataDAO; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample; +import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE; import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePE; import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePropertyTypePE; import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE; +import ch.systemsx.cisd.openbis.generic.shared.dto.GroupPE; import ch.systemsx.cisd.openbis.generic.shared.dto.ProjectPE; import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE; import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePropertyPE; @@ -98,19 +101,7 @@ abstract class AbstractSampleBusinessObject extends AbstractSampleIdentifierBusi { final SampleIdentifier sampleIdentifier = SampleIdentifierFactory.parse(newSample.getIdentifier()); - final SampleOwnerIdentifier sampleOwnerIdentifier = - sampleIdentifier.createSampleOwnerIdentifier(); - SampleOwner sampleOwner = - (sampleOwnerCacheOrNull != null) ? sampleOwnerCacheOrNull - .get(sampleOwnerIdentifier) : null; - if (sampleOwner == null) - { - sampleOwner = getSampleOwnerFinder().figureSampleOwner(sampleIdentifier); - if (sampleOwnerCacheOrNull != null) - { - sampleOwnerCacheOrNull.put(sampleOwnerIdentifier, sampleOwner); - } - } + SampleOwner sampleOwner = getSampleOwner(sampleOwnerCacheOrNull, sampleIdentifier); SampleTypePE sampleTypePE = (sampleTypeCacheOrNull != null) ? sampleTypeCacheOrNull.get(newSample .getSampleType().getCode()) : null; @@ -140,6 +131,26 @@ abstract class AbstractSampleBusinessObject extends AbstractSampleIdentifierBusi return samplePE; } + protected SampleOwner getSampleOwner( + Map<SampleOwnerIdentifier, SampleOwner> sampleOwnerCacheOrNull, + final SampleIdentifier sampleIdentifier) + { + final SampleOwnerIdentifier sampleOwnerIdentifier = + sampleIdentifier.createSampleOwnerIdentifier(); + SampleOwner sampleOwner = + (sampleOwnerCacheOrNull != null) ? sampleOwnerCacheOrNull + .get(sampleOwnerIdentifier) : null; + if (sampleOwner == null) + { + sampleOwner = getSampleOwnerFinder().figureSampleOwner(sampleIdentifier); + if (sampleOwnerCacheOrNull != null) + { + sampleOwnerCacheOrNull.put(sampleOwnerIdentifier, sampleOwner); + } + } + return sampleOwner; + } + private ExperimentPE tryFindExperiment(Map<String, ExperimentPE> experimentCacheOrNull, String experimentIdentifier) { @@ -288,4 +299,91 @@ abstract class AbstractSampleBusinessObject extends AbstractSampleIdentifierBusi // check. return (onlyNewSamples == false) && SampleUtils.hasDatasets(externalDataDAO, sample); } + + protected void updateGroup(SamplePE sample, SampleOwnerIdentifier sampleOwnerIdentifier) + { + if (sampleOwnerIdentifier != null) + { + final SampleOwner sampleOwner = + getSampleOwnerFinder().figureSampleOwner(sampleOwnerIdentifier); + GroupPE group = sampleOwner.tryGetGroup(); + sample.setDatabaseInstance(sampleOwner.tryGetDatabaseInstance()); + sample.setGroup(group); + } + } + + protected void updateExperiment(SamplePE sample, ExperimentIdentifier expIdentifierOrNull) + { + if (expIdentifierOrNull != null) + { + fillGroupIdentifier(expIdentifierOrNull); + changeExperiment(sample, expIdentifierOrNull); + } else + { + removeFromExperiment(sample); + } + } + + private void removeFromExperiment(SamplePE sample) + { + if (hasDatasets(getExternalDataDAO(), sample)) + { + throw UserFailureException.fromTemplate( + "Cannot detach the sample '%s' from the experiment " + + "because there are already datasets attached to the sample.", sample + .getIdentifier()); + } + sample.setExperiment(null); + } + + private void changeExperiment(SamplePE sample, ExperimentIdentifier identifier) + { + ExperimentPE newExperiment = findExperiment(identifier); + if (isExperimentUnchanged(newExperiment, sample.getExperiment())) + { + return; + } + ensureExperimentIsValid(identifier, newExperiment, sample); + ensureSampleAttachableToExperiment(sample); + + changeDatasetsExperiment(sample.getDatasets(), newExperiment); + sample.setExperiment(newExperiment); + } + + private void changeDatasetsExperiment(Set<DataPE> datasets, ExperimentPE experiment) + { + for (DataPE dataset : datasets) + { + dataset.setExperiment(experiment); + } + } + + private void ensureSampleAttachableToExperiment(SamplePE sample) + { + if (sample.getGroup() == null) + { + throw UserFailureException.fromTemplate( + "It is not allowed to connect a shared sample '%s' to the experiment.", sample + .getIdentifier()); + } + } + + private void ensureExperimentIsValid(ExperimentIdentifier identOrNull, + ExperimentPE experimentOrNull, SamplePE sample) + { + if (experimentOrNull != null && experimentOrNull.getInvalidation() != null) + { + throw UserFailureException.fromTemplate( + "The sample '%s' cannot be assigned to the experiment '%s' " + + "because the experiment has been invalidated.", sample + .getSampleIdentifier(), identOrNull); + } + } + + private boolean isExperimentUnchanged(ExperimentPE newExperimentOrNull, + ExperimentPE experimentOrNull) + { + return experimentOrNull == null ? newExperimentOrNull == null : experimentOrNull + .equals(newExperimentOrNull); + } } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ISampleBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ISampleBO.java index 124906a2b3175f87f76dfa0981e5f7921ccfee6a..a1cb6678204ccc8fe1cf9bd3d6f38876bd8f8417 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ISampleBO.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ISampleBO.java @@ -21,7 +21,6 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.TechId; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample; import ch.systemsx.cisd.openbis.generic.shared.dto.AttachmentPE; import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE; -import ch.systemsx.cisd.openbis.generic.shared.dto.SampleBatchUpdatesDTO; import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE; import ch.systemsx.cisd.openbis.generic.shared.dto.SampleUpdatesDTO; import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier; @@ -84,12 +83,6 @@ public interface ISampleBO extends IEntityBusinessObject */ void update(SampleUpdatesDTO updates); - /** - * Changes given sample as a result of batch update (some attributes/properties will remain - * unchanged). - */ - void batchUpdate(SampleBatchUpdatesDTO updates); - /** * Adds the specified experiment attachment to the sample. */ diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ISampleTable.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ISampleTable.java index 920936a201ac803fc548851604b2fb48b85ba997..96060a8c234b8da7543100decbf2df73ac373790 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ISampleTable.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ISampleTable.java @@ -23,6 +23,7 @@ import ch.systemsx.cisd.openbis.generic.server.business.bo.samplelister.ISampleL import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample; import ch.systemsx.cisd.openbis.generic.shared.dto.ListSampleCriteriaDTO; import ch.systemsx.cisd.openbis.generic.shared.dto.ListSamplesByPropertyCriteria; +import ch.systemsx.cisd.openbis.generic.shared.dto.SampleBatchUpdatesDTO; import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE; /** @@ -68,4 +69,6 @@ public interface ISampleTable */ public void save() throws UserFailureException; + void update(List<SampleBatchUpdatesDTO> updates); + } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java index dc2204c1a1b14d795bdda849f5de2daaad72c1cf..afe5b0ce7c5dd7b38b717ca83f6483b340dd7a14 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java @@ -24,7 +24,6 @@ import org.springframework.dao.DataAccessException; import org.springframework.dao.DataIntegrityViolationException; import ch.systemsx.cisd.common.exceptions.UserFailureException; -import ch.systemsx.cisd.openbis.generic.server.business.bo.util.SampleOwner; import ch.systemsx.cisd.openbis.generic.server.business.bo.util.SampleUtils; import ch.systemsx.cisd.openbis.generic.server.dataaccess.IAttachmentDAO; import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory; @@ -32,25 +31,19 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.TechId; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewAttachment; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample; -import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleBatchUpdateDetails; import ch.systemsx.cisd.openbis.generic.shared.dto.AttachmentPE; -import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE; import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePE; import ch.systemsx.cisd.openbis.generic.shared.dto.EventPE; import ch.systemsx.cisd.openbis.generic.shared.dto.EventType; import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE; -import ch.systemsx.cisd.openbis.generic.shared.dto.GroupPE; import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE; -import ch.systemsx.cisd.openbis.generic.shared.dto.SampleBatchUpdatesDTO; import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE; import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePropertyPE; import ch.systemsx.cisd.openbis.generic.shared.dto.SampleUpdatesDTO; import ch.systemsx.cisd.openbis.generic.shared.dto.Session; import ch.systemsx.cisd.openbis.generic.shared.dto.EventPE.EntityType; -import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifier; import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.IdentifierHelper; import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier; -import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleOwnerIdentifier; import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind; import ch.systemsx.cisd.openbis.generic.shared.translator.AttachmentTranslator; import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils; @@ -277,8 +270,8 @@ public final class SampleBO extends AbstractSampleBusinessObject implements ISam throwModifiedEntityException("Sample"); } updateProperties(updates.getProperties()); - updateGroup(updates.getSampleIdentifier()); - updateExperiment(updates.getExperimentIdentifierOrNull()); + updateGroup(sample, updates.getSampleIdentifier()); + updateExperiment(sample, updates.getExperimentIdentifierOrNull()); setGeneratedFrom(updates.getSampleIdentifier(), sample, updates.getParentIdentifierOrNull()); setContainer(updates.getSampleIdentifier(), sample, updates.getContainerIdentifierOrNull()); for (NewAttachment attachment : updates.getAttachments()) @@ -288,121 +281,6 @@ public final class SampleBO extends AbstractSampleBusinessObject implements ISam dataChanged = true; } - public void batchUpdate(SampleBatchUpdatesDTO updates) - { - // batch update doesn't use tech id, check version and update attributes - loadBySampleIdentifier(updates.getOldSampleIdentifierOrNull()); - - SampleBatchUpdateDetails details = updates.getDetails(); - - batchUpdateProperties(updates.getProperties(), details.getPropertiesToUpdate()); - - if (details.isExperimentUpdateRequested()) - { - updateGroup(updates.getSampleIdentifier()); - updateExperiment(updates.getExperimentIdentifierOrNull()); - } - if (details.isParentUpdateRequested()) - { - setGeneratedFrom(updates.getSampleIdentifier(), sample, updates - .getParentIdentifierOrNull()); - } - if (details.isContainerUpdateRequested()) - { - setContainer(updates.getSampleIdentifier(), sample, updates - .getContainerIdentifierOrNull()); - } - - dataChanged = true; - } - - private void updateGroup(SampleOwnerIdentifier sampleOwnerIdentifier) - { - if (sampleOwnerIdentifier != null) - { - final SampleOwner sampleOwner = - getSampleOwnerFinder().figureSampleOwner(sampleOwnerIdentifier); - GroupPE group = sampleOwner.tryGetGroup(); - sample.setDatabaseInstance(sampleOwner.tryGetDatabaseInstance()); - sample.setGroup(group); - } - } - - private void updateExperiment(ExperimentIdentifier expIdentifierOrNull) - { - if (expIdentifierOrNull != null) - { - fillGroupIdentifier(expIdentifierOrNull); - changeExperiment(expIdentifierOrNull); - } else - { - removeFromExperiment(); - } - } - - private void removeFromExperiment() - { - if (hasDatasets(getExternalDataDAO(), sample)) - { - throw UserFailureException.fromTemplate( - "Cannot detach the sample '%s' from the experiment " - + "because there are already datasets attached to the sample.", sample - .getIdentifier()); - } - sample.setExperiment(null); - } - - private void changeExperiment(ExperimentIdentifier identifier) - { - ExperimentPE newExperiment = findExperiment(identifier); - if (isExperimentUnchanged(newExperiment, sample.getExperiment())) - { - return; - } - ensureExperimentIsValid(identifier, newExperiment); - ensureSampleAttachableToExperiment(); - - changeDatasetsExperiment(sample.getDatasets(), newExperiment); - sample.setExperiment(newExperiment); - } - - private void changeDatasetsExperiment(Set<DataPE> datasets, ExperimentPE experiment) - { - for (DataPE dataset : datasets) - { - dataset.setExperiment(experiment); - } - } - - private void ensureSampleAttachableToExperiment() - { - if (sample.getGroup() == null) - { - throw UserFailureException.fromTemplate( - "It is not allowed to connect a shared sample '%s' to the experiment.", sample - .getIdentifier()); - } - } - - private void ensureExperimentIsValid(ExperimentIdentifier identOrNull, - ExperimentPE experimentOrNull) - { - if (experimentOrNull != null && experimentOrNull.getInvalidation() != null) - { - throw UserFailureException.fromTemplate( - "The sample '%s' cannot be assigned to the experiment '%s' " - + "because the experiment has been invalidated.", sample - .getSampleIdentifier(), identOrNull); - } - } - - private boolean isExperimentUnchanged(ExperimentPE newExperimentOrNull, - ExperimentPE experimentOrNull) - { - return experimentOrNull == null ? newExperimentOrNull == null : experimentOrNull - .equals(newExperimentOrNull); - } - private void updateProperties(List<IEntityProperty> properties) { final Set<SamplePropertyPE> existingProperties = sample.getProperties(); @@ -412,16 +290,6 @@ public final class SampleBO extends AbstractSampleBusinessObject implements ISam properties, registrator)); } - private void batchUpdateProperties(List<IEntityProperty> properties, - Set<String> propertiesToUpdate) - { - final Set<SamplePropertyPE> existingProperties = sample.getProperties(); - final EntityTypePE type = sample.getSampleType(); - final PersonPE registrator = findRegistrator(); - sample.setProperties(entityPropertiesConverter.updateProperties(existingProperties, type, - properties, registrator, propertiesToUpdate)); - } - public void setGeneratedCode() { final String code = createCode(EntityKind.SAMPLE); diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleTable.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleTable.java index b6dde4322632b24f533ee82f0c03d6404bd7d604..36591ab36a9ee365e93f8259b344fc8738454ad3 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleTable.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleTable.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.springframework.dao.DataAccessException; @@ -29,20 +30,26 @@ import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory; import ch.systemsx.cisd.openbis.generic.server.dataaccess.ISampleDAO; import ch.systemsx.cisd.openbis.generic.shared.basic.TechId; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityType; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleBatchUpdateDetails; import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePE; import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePropertyTypePE; import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE; import ch.systemsx.cisd.openbis.generic.shared.dto.GroupPE; import ch.systemsx.cisd.openbis.generic.shared.dto.ListSampleCriteriaDTO; import ch.systemsx.cisd.openbis.generic.shared.dto.ListSamplesByPropertyCriteria; +import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE; import ch.systemsx.cisd.openbis.generic.shared.dto.ProjectPE; +import ch.systemsx.cisd.openbis.generic.shared.dto.SampleBatchUpdatesDTO; import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE; +import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePropertyPE; import ch.systemsx.cisd.openbis.generic.shared.dto.SampleTypePE; import ch.systemsx.cisd.openbis.generic.shared.dto.Session; import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifier; import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.LocalExperimentIdentifier; import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ProjectIdentifier; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier; import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleOwnerIdentifier; import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils; @@ -352,4 +359,63 @@ public final class SampleTable extends AbstractSampleBusinessObject implements I } } + public SamplePE prepareBatchUpdate(SampleBatchUpdatesDTO updates) + { + // batch update doesn't use tech id, check version and update attributes + SampleIdentifier identifier = updates.getOldSampleIdentifierOrNull(); + SamplePE sample = tryToGetSampleByIdentifier(identifier); + if (sample == null) + { + throw UserFailureException.fromTemplate( + "No sample could be found with given identifier '%s'.", identifier); + } + SampleBatchUpdateDetails details = updates.getDetails(); + + batchUpdateProperties(sample, updates.getProperties(), details.getPropertiesToUpdate()); + + if (details.isExperimentUpdateRequested()) + { + updateGroup(sample, updates.getSampleIdentifier()); + updateExperiment(sample, updates.getExperimentIdentifierOrNull()); + } + if (details.isParentUpdateRequested()) + { + setGeneratedFrom(updates.getSampleIdentifier(), sample, updates + .getParentIdentifierOrNull()); + } + if (details.isContainerUpdateRequested()) + { + setContainer(updates.getSampleIdentifier(), sample, updates + .getContainerIdentifierOrNull()); + } + return sample; + } + + private void batchUpdateProperties(SamplePE sample, List<IEntityProperty> properties, + Set<String> propertiesToUpdate) + { + final Set<SamplePropertyPE> existingProperties = sample.getProperties(); + final EntityTypePE type = sample.getSampleType(); + final PersonPE registrator = findRegistrator(); + sample.setProperties(entityPropertiesConverter.updateProperties(existingProperties, type, + properties, registrator, propertiesToUpdate)); + } + + public void update(List<SampleBatchUpdatesDTO> updates) + { + setBatchUpdateMode(true); + + assert updates != null : "Unspecified samples."; + if (samples == null) + { + samples = new ArrayList<SamplePE>(); + } + for (SampleBatchUpdatesDTO sample : updates) + { + samples.add(prepareBatchUpdate(sample)); + } + dataChanged = true; + setBatchUpdateMode(false); + } + } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/plugin/ISampleTypeSlaveServerPlugin.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/plugin/ISampleTypeSlaveServerPlugin.java index c1a22f64314771f4204a6fb14db65202f00ab78c..99ebea72a5d164efff21140809c609c06b3e467c 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/plugin/ISampleTypeSlaveServerPlugin.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/plugin/ISampleTypeSlaveServerPlugin.java @@ -21,6 +21,7 @@ import java.util.List; import ch.systemsx.cisd.common.exceptions.UserFailureException; import ch.systemsx.cisd.openbis.generic.server.dataaccess.db.DAOFactory; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample; +import ch.systemsx.cisd.openbis.generic.shared.dto.SampleBatchUpdatesDTO; import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE; import ch.systemsx.cisd.openbis.generic.shared.dto.SampleParentWithDerivedDTO; import ch.systemsx.cisd.openbis.generic.shared.dto.Session; @@ -48,4 +49,9 @@ public interface ISampleTypeSlaveServerPlugin void registerSamples(final Session session, final List<NewSample> newSamples) throws UserFailureException; + /** + * Updates given list of samples. + */ + void updateSamples(Session session, List<SampleBatchUpdatesDTO> convertSamples); + } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericSampleTypeSlaveServerPlugin.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericSampleTypeSlaveServerPlugin.java index e0dd63e8e3ff789b4dceab3be54f6867428f164b..b259533ed43d9f5a8b9414d3c3f86347be869c8d 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericSampleTypeSlaveServerPlugin.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericSampleTypeSlaveServerPlugin.java @@ -16,27 +16,26 @@ package ch.systemsx.cisd.openbis.plugin.generic.server; -import java.util.ArrayList; import java.util.List; import javax.annotation.Resource; -import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import ch.systemsx.cisd.common.exceptions.UserFailureException; -import ch.systemsx.cisd.common.logging.LogCategory; -import ch.systemsx.cisd.common.logging.LogFactory; import ch.systemsx.cisd.openbis.generic.server.ComponentNames; -import ch.systemsx.cisd.openbis.generic.server.business.bo.ISampleTable; import ch.systemsx.cisd.openbis.generic.server.business.bo.SampleHierarchyFiller; import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory; import ch.systemsx.cisd.openbis.generic.server.plugin.ISampleTypeSlaveServerPlugin; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample; +import ch.systemsx.cisd.openbis.generic.shared.dto.SampleBatchUpdatesDTO; import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE; import ch.systemsx.cisd.openbis.generic.shared.dto.SampleParentWithDerivedDTO; import ch.systemsx.cisd.openbis.generic.shared.dto.Session; import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils; +import ch.systemsx.cisd.openbis.plugin.generic.server.batch.BatchOperationExecutor; +import ch.systemsx.cisd.openbis.plugin.generic.server.batch.SampleBatchRegistration; +import ch.systemsx.cisd.openbis.plugin.generic.server.batch.SampleBatchUpdate; import ch.systemsx.cisd.openbis.plugin.generic.shared.ResourceNames; /** @@ -47,10 +46,8 @@ import ch.systemsx.cisd.openbis.plugin.generic.shared.ResourceNames; @Component(ch.systemsx.cisd.openbis.generic.shared.ResourceNames.GENERIC_SAMPLE_TYPE_SLAVE_SERVER_PLUGIN) public final class GenericSampleTypeSlaveServerPlugin implements ISampleTypeSlaveServerPlugin { - private static final Logger operationLog = - LogFactory.getLogger(LogCategory.OPERATION, GenericSampleTypeSlaveServerPlugin.class); - private static final int REGISTRATION_BATCH_SIZE = 10000; + private static final int BATCH_SIZE = 10000; @Resource(name = ResourceNames.GENERIC_BUSINESS_OBJECT_FACTORY) private IGenericBusinessObjectFactory businessObjectFactory; @@ -85,30 +82,16 @@ public final class GenericSampleTypeSlaveServerPlugin implements ISampleTypeSlav assert session != null : "Unspecified session."; assert newSamples != null && newSamples.size() > 0 : "Unspecified sample or empty samples."; - List<NewSample> batch = new ArrayList<NewSample>(); - int counter = 0; - for (NewSample newSample : newSamples) - { - batch.add(newSample); - if (batch.size() >= REGISTRATION_BATCH_SIZE) - { - doRegisterSamples(session, batch); - counter += batch.size(); - operationLog.info("Sample registration progress: " + counter + "/" - + newSamples.size()); - batch.clear(); - } - } - if (batch.size() > 0) - { - doRegisterSamples(session, batch); - } + new BatchOperationExecutor<NewSample>().executeInBatches(new SampleBatchRegistration( + businessObjectFactory.createSampleTable(session), newSamples), BATCH_SIZE); } - private void doRegisterSamples(Session session, List<NewSample> newSamples) + public void updateSamples(Session session, List<SampleBatchUpdatesDTO> updateSamples) { - ISampleTable sampleTable = businessObjectFactory.createSampleTable(session); - sampleTable.add(newSamples); - sampleTable.save(); + assert session != null : "Unspecified session."; + assert updateSamples != null && updateSamples.size() > 0 : "Unspecified sample or empty samples."; + + new BatchOperationExecutor<SampleBatchUpdatesDTO>().executeInBatches(new SampleBatchUpdate( + businessObjectFactory.createSampleTable(session), updateSamples), BATCH_SIZE); } } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServer.java index 4ce8a4a08834123b0afe0616402b7eff5afd5b50..5c333650ff6aae9c89d10657d3e3dc0793e38be9 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServer.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServer.java @@ -46,6 +46,7 @@ import ch.systemsx.cisd.openbis.generic.server.business.bo.IMaterialTable; import ch.systemsx.cisd.openbis.generic.server.business.bo.IProjectBO; import ch.systemsx.cisd.openbis.generic.server.business.bo.ISampleBO; import ch.systemsx.cisd.openbis.generic.server.business.bo.MaterialUpdateDTO; +import ch.systemsx.cisd.openbis.generic.server.business.bo.samplelister.ISampleLister; import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory; import ch.systemsx.cisd.openbis.generic.server.plugin.IDataSetTypeSlaveServerPlugin; import ch.systemsx.cisd.openbis.generic.server.plugin.ISampleTypeSlaveServerPlugin; @@ -96,6 +97,8 @@ import ch.systemsx.cisd.openbis.generic.shared.translator.ExperimentTranslator; import ch.systemsx.cisd.openbis.generic.shared.translator.MaterialTranslator; import ch.systemsx.cisd.openbis.generic.shared.translator.MaterialTypeTranslator; import ch.systemsx.cisd.openbis.generic.shared.translator.SampleTranslator; +import ch.systemsx.cisd.openbis.plugin.generic.server.batch.BatchOperationExecutor; +import ch.systemsx.cisd.openbis.plugin.generic.server.batch.IBatchOperation; import ch.systemsx.cisd.openbis.plugin.generic.shared.IGenericServer; import ch.systemsx.cisd.openbis.plugin.generic.shared.ResourceNames; @@ -268,38 +271,74 @@ public final class GenericServer extends AbstractServer<IGenericServer> implemen registerSamples(session, samples); } else { - registerOrUpdate(session, samples); + new BatchOperationExecutor<NewSample>().executeInBatches( + new SampleBatchRegisterOrUpdate(businessObjectFactory + .createSampleLister(session), samples.getNewSamples(), samples + .getSampleType(), session), 1000); } } } - private void registerOrUpdate(final Session session, NewSamplesWithTypes samples) + private class SampleBatchRegisterOrUpdate implements IBatchOperation<NewSample> { - List<Sample> existingSamples = new ArrayList<Sample>(); - List<String> extractCodes = - SampleRegisterOrUpdateUtil.extractCodes(samples.getNewSamples(), false); - List<Sample> list = - businessObjectFactory.createSampleLister(session).list( - SampleRegisterOrUpdateUtil.createListSamplesByCodeCriteria(extractCodes)); - existingSamples.addAll(list); - List<String> codes = SampleRegisterOrUpdateUtil.extractCodes(samples.getNewSamples(), true); - ListOrSearchSampleCriteria criteria = - SampleRegisterOrUpdateUtil.createListSamplesByCodeCriteria(codes); - List<Sample> existingContainers = - businessObjectFactory.createSampleLister(session).list(criteria); - for (Sample s : existingContainers) + + private final List<NewSample> entities; + + private final SampleType sampleType; + + private final ISampleLister sampleLister; + + private final Session session; + + public SampleBatchRegisterOrUpdate(ISampleLister sampleLister, List<NewSample> entities, + SampleType sampleType, Session session) { - existingSamples.addAll(businessObjectFactory.createSampleLister(session).list( - new ListOrSearchSampleCriteria(ListOrSearchSampleCriteria - .createForContainer(new TechId(s.getId()))))); + this.sampleLister = sampleLister; + this.entities = entities; + this.sampleType = sampleType; + this.session = session; } - List<NewSample> samplesToUpdate = - SampleRegisterOrUpdateUtil.getSamplesToUpdate(samples, existingSamples); - List<NewSample> samplesToRegister = new ArrayList<NewSample>(samples.getNewSamples()); - samplesToRegister.removeAll(samplesToUpdate); - registerSamples(session, - new NewSamplesWithTypes(samples.getSampleType(), samplesToRegister)); - updateSamples(session, new NewSamplesWithTypes(samples.getSampleType(), samplesToUpdate)); + + public void execute(List<NewSample> newSamples) + { + List<Sample> existingSamples = new ArrayList<Sample>(); + List<String> extractCodes = SampleRegisterOrUpdateUtil.extractCodes(newSamples, false); + List<Sample> list = + sampleLister.list(SampleRegisterOrUpdateUtil + .createListSamplesByCodeCriteria(extractCodes)); + existingSamples.addAll(list); + List<String> codes = SampleRegisterOrUpdateUtil.extractCodes(newSamples, true); + ListOrSearchSampleCriteria criteria = + SampleRegisterOrUpdateUtil.createListSamplesByCodeCriteria(codes); + List<Sample> existingContainers = sampleLister.list(criteria); + for (Sample s : existingContainers) + { + existingSamples.addAll(sampleLister.list(new ListOrSearchSampleCriteria( + ListOrSearchSampleCriteria.createForContainer(new TechId(s.getId()))))); + } + List<NewSample> samplesToUpdate = + SampleRegisterOrUpdateUtil.getSamplesToUpdate(newSamples, existingSamples); + List<NewSample> samplesToRegister = new ArrayList<NewSample>(newSamples); + samplesToRegister.removeAll(samplesToUpdate); + registerSamples(session, new NewSamplesWithTypes(sampleType, samplesToRegister)); + updateSamples(session, new NewSamplesWithTypes(sampleType, samplesToUpdate)); + } + + public List<NewSample> getAllEntities() + { + return entities; + } + + public String getEntityName() + { + return "sample"; + } + + public String getOperationName() + { + return "update/register preprocessing"; + } + } public final void registerSamples(final String sessionToken, @@ -391,6 +430,14 @@ public final class GenericServer extends AbstractServer<IGenericServer> implemen sampleTypeCode); } + getSampleTypeSlaveServerPlugin(sampleTypePE).updateSamples(session, + convertSamples(updatedSamples)); + } + + private List<SampleBatchUpdatesDTO> convertSamples(final List<NewSample> updatedSamples) + { + List<SampleBatchUpdatesDTO> samples = new ArrayList<SampleBatchUpdatesDTO>(); + for (NewSample updatedSample : updatedSamples) { final SampleIdentifier oldSampleIdentifier = @@ -419,10 +466,11 @@ public final class GenericServer extends AbstractServer<IGenericServer> implemen final SampleBatchUpdateDetails batchUpdateDetails = createBatchUpdateDetails(updatedSample); - batchUpdateSample(session, new SampleBatchUpdatesDTO(oldSampleIdentifier, properties, + samples.add(new SampleBatchUpdatesDTO(oldSampleIdentifier, properties, experimentIdentifierOrNull, newSampleIdentifier, parentIdentifierOrNull, containerIdentifierOrNull, batchUpdateDetails)); } + return samples; } SampleBatchUpdateDetails createBatchUpdateDetails(NewSample sample) @@ -640,13 +688,6 @@ public final class GenericServer extends AbstractServer<IGenericServer> implemen return sampleBO.getSample().getModificationDate(); } - private void batchUpdateSample(Session session, SampleBatchUpdatesDTO updates) - { - final ISampleBO sampleBO = businessObjectFactory.createSampleBO(session); - sampleBO.batchUpdate(updates); - sampleBO.save(); - } - public DataSetUpdateResult updateDataSet(String sessionToken, DataSetUpdatesDTO updates) { final Session session = getSession(sessionToken); diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/SampleRegisterOrUpdateUtil.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/SampleRegisterOrUpdateUtil.java index ab9fd599696cfad928b284bd2f57d21904d3c363..8b44fc1f21290b6517e10a4abc4326ea0dc19539 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/SampleRegisterOrUpdateUtil.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/SampleRegisterOrUpdateUtil.java @@ -22,7 +22,6 @@ import java.util.List; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ListOrSearchSampleCriteria; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample; -import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSamplesWithTypes; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample; import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier; import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifierFactory; @@ -41,11 +40,10 @@ public class SampleRegisterOrUpdateUtil /** * Returns a list of samples that already exist and should be updated. */ - static List<NewSample> getSamplesToUpdate(NewSamplesWithTypes samples, - List<Sample> existingSamples) + static List<NewSample> getSamplesToUpdate(List<NewSample> samples, List<Sample> existingSamples) { List<NewSample> samplesToUpdate = new ArrayList<NewSample>(); - for (NewSample ns : samples.getNewSamples()) + for (NewSample ns : samples) { for (Sample es : existingSamples) { diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/batch/BatchOperationExecutor.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/batch/BatchOperationExecutor.java new file mode 100644 index 0000000000000000000000000000000000000000..06353d2a647d3861f50e3771045e2ad14195d471 --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/batch/BatchOperationExecutor.java @@ -0,0 +1,44 @@ +package ch.systemsx.cisd.openbis.plugin.generic.server.batch; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import ch.systemsx.cisd.common.logging.LogCategory; +import ch.systemsx.cisd.common.logging.LogFactory; + +/** + * Executes provided operation in batches of chosen size. + * + * @author Izabela Adamczyk + */ +public class BatchOperationExecutor<S> +{ + private static final Logger operationLog = + LogFactory.getLogger(LogCategory.OPERATION, BatchOperationExecutor.class); + + public void executeInBatches(IBatchOperation<S> strategy, int batchSize) + { + assert strategy != null : "Unspecified operation."; + + List<S> batch = new ArrayList<S>(); + int counter = 0; + for (S entity : strategy.getAllEntities()) + { + batch.add(entity); + if (batch.size() >= batchSize) + { + strategy.execute(batch); + counter += batch.size(); + operationLog.info(String.format("%s %s progress: %d/%d", strategy.getEntityName(), + strategy.getOperationName(), counter, strategy.getAllEntities().size())); + batch.clear(); + } + } + if (batch.size() > 0) + { + strategy.execute(batch); + } + } +} \ No newline at end of file diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/batch/IBatchOperation.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/batch/IBatchOperation.java new file mode 100644 index 0000000000000000000000000000000000000000..990d3936f258fb637a77eef9e33da23ac9614e3b --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/batch/IBatchOperation.java @@ -0,0 +1,35 @@ +/* + * 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.plugin.generic.server.batch; + +import java.util.List; + +/** + * Describes operation that will be performed in batches on the entities. + * + * @author Izabela Adamczyk + */ +public interface IBatchOperation<S> +{ + void execute(List<S> entities); + + List<S> getAllEntities(); + + String getEntityName(); + + String getOperationName(); +} \ No newline at end of file diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/batch/SampleBatchRegistration.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/batch/SampleBatchRegistration.java new file mode 100644 index 0000000000000000000000000000000000000000..e6f261235f8e04f9d2105db955e07751805f3749 --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/batch/SampleBatchRegistration.java @@ -0,0 +1,62 @@ +/* + * 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.plugin.generic.server.batch; + +import java.util.List; + +import ch.systemsx.cisd.openbis.generic.server.business.bo.ISampleTable; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample; + +/** + * {@link IBatchOperation} registering samples. + * + * @author Izabela Adamczyk + */ +public class SampleBatchRegistration implements IBatchOperation<NewSample> +{ + private final ISampleTable businessTable; + + private final List<NewSample> entities; + + public SampleBatchRegistration(ISampleTable businessTable, List<NewSample> entities) + { + this.businessTable = businessTable; + this.entities = entities; + } + + public void execute(List<NewSample> batch) + { + businessTable.add(batch); + businessTable.save(); + } + + public List<NewSample> getAllEntities() + { + return entities; + } + + public String getEntityName() + { + return "sample"; + } + + public String getOperationName() + { + return "registration"; + } + +} \ No newline at end of file diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/batch/SampleBatchUpdate.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/batch/SampleBatchUpdate.java new file mode 100644 index 0000000000000000000000000000000000000000..3a5f25217a84a0928589272e24ee8fcedeabaeb0 --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/batch/SampleBatchUpdate.java @@ -0,0 +1,62 @@ +/* + * 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.plugin.generic.server.batch; + +import java.util.List; + +import ch.systemsx.cisd.openbis.generic.server.business.bo.ISampleTable; +import ch.systemsx.cisd.openbis.generic.shared.dto.SampleBatchUpdatesDTO; + +/** + * {@link IBatchOperation} updating samples. + * + * @author Izabela Adamczyk + */ +public class SampleBatchUpdate implements IBatchOperation<SampleBatchUpdatesDTO> +{ + private final ISampleTable businessTable; + + private final List<SampleBatchUpdatesDTO> entities; + + public SampleBatchUpdate(ISampleTable businessTable, List<SampleBatchUpdatesDTO> entities) + { + this.businessTable = businessTable; + this.entities = entities; + } + + public void execute(List<SampleBatchUpdatesDTO> updates) + { + businessTable.update(updates); + businessTable.save(); + } + + public List<SampleBatchUpdatesDTO> getAllEntities() + { + return entities; + } + + public String getEntityName() + { + return "sample"; + } + + public String getOperationName() + { + return "update"; + } + +} \ No newline at end of file