From 59b3fdaf081f95689bb64c73dbe9cc132c9a650d Mon Sep 17 00:00:00 2001
From: felmer <felmer>
Date: Wed, 29 Apr 2015 12:08:59 +0000
Subject: [PATCH] SSDM-1804: Release restriction that component sample can not
 be deleted if they belong to outside entity. Bug fixed concerning update of
 modification date and modifier of entities related to the deleted ones.

SVN: 33922
---
 .../openbis/generic/server/CommonServer.java  | 104 ++-------------
 .../generic/server/business/bo/TrashBO.java   | 121 +++++++-----------
 .../shared/util/RelationshipUtils.java        |  77 +++++++++++
 .../openbis/systemtest/DeletionTestCase.java  |  11 +-
 .../systemtest/EntityDeletionTest.java        |  24 ++--
 .../openbis/systemtest/base/BaseTest.java     |   7 +-
 .../entitygraph/EntityGraphManager.java       |   5 +
 7 files changed, 173 insertions(+), 176 deletions(-)

diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
index 2ef82420b64..5cd21516eec 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
@@ -170,7 +170,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.AttachmentHolderPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.AttachmentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.AuthorizationGroupPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
-import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetRelationshipPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetUpdatesDTO;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetUploadContext;
@@ -201,7 +200,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.ProjectUpdatesDTO;
 import ch.systemsx.cisd.openbis.generic.shared.dto.PropertyTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.RoleAssignmentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
-import ch.systemsx.cisd.openbis.generic.shared.dto.SampleRelationshipPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SampleTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SampleUpdatesDTO;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ScriptPE;
@@ -2069,10 +2067,11 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
         Session session = getSession(sessionToken);
         // NOTE: logical deletion and new implementation of permanent deletion doesn't use
         // IDataSetTypeSlaveServerPlugin (we have just 1 implementation!)
-        updateModificationDateAndModifierOfRelatedEntitiesOfDataSets(dataSetCodes, session);
         switch (type)
         {
             case PERMANENT:
+                List<DataPE> dataSets1 = getDAOFactory().getDataDAO().listByCode(new HashSet<String>(dataSetCodes));
+                RelationshipUtils.updateModificationDateAndModifierOfRelatedEntitiesOfDataSets(dataSets1, session);
                 if (isTrashEnabled)
                 {
                     IDeletedDataSetTable deletedDataSetTable =
@@ -2100,41 +2099,6 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
         }
     }
 
-    private void updateModificationDateAndModifierOfRelatedEntitiesOfDataSets(
-            Collection<String> dataSetCodes, Session session)
-    {
-        List<DataPE> dataSets =
-                getDAOFactory().getDataDAO().listByCode(new HashSet<String>(dataSetCodes));
-        for (DataPE dataSet : dataSets)
-        {
-            ExperimentPE experiment = dataSet.getExperiment();
-            RelationshipUtils.updateModificationDateAndModifier(experiment, session);
-            SamplePE sample = dataSet.tryGetSample();
-            if (sample != null)
-            {
-                RelationshipUtils.updateModificationDateAndModifier(sample, session);
-            }
-            updateModificationDateAndModifierOfDataSets(dataSet.getChildren(), session);
-            updateModificationDateAndModifierOfDataSets(dataSet.getParents(), session);
-            Set<DataSetRelationshipPE> relationships = dataSet.getParentRelationships();
-            for (DataSetRelationshipPE relationship : RelationshipUtils.getContainerComponentRelationships(relationships))
-            {
-                RelationshipUtils.updateModificationDateAndModifier(relationship.getParentDataSet(), session);
-            }
-        }
-    }
-
-    private void updateModificationDateAndModifierOfDataSets(List<DataPE> dataSets, Session session)
-    {
-        if (dataSets != null)
-        {
-            for (DataPE child : dataSets)
-            {
-                RelationshipUtils.updateModificationDateAndModifier(child, session);
-            }
-        }
-    }
-
     @Override
     @RolesAllowed(RoleWithHierarchy.SPACE_POWER_USER)
     @Capability("DELETE_SAMPLE")
@@ -2143,10 +2107,11 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
             List<TechId> sampleIds, String reason, DeletionType deletionType)
     {
         Session session = getSession(sessionToken);
-        updateModificationDateAndModifierOfRelatedEntitiesOfSamples(sampleIds, session);
         switch (deletionType)
         {
             case PERMANENT:
+                List<SamplePE> samples = getDAOFactory().getSampleDAO().listByIDs(TechId.asLongs(sampleIds));
+                RelationshipUtils.updateModificationDateAndModifierOfRelatedEntitiesOfSamples(samples, session);
                 ISampleTable sampleTableBO = businessObjectFactory.createSampleTable(session);
                 sampleTableBO.deleteByTechIds(sampleIds, reason);
                 break;
@@ -2158,43 +2123,6 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
         }
     }
 
-    private void updateModificationDateAndModifierOfRelatedEntitiesOfSamples(
-            Collection<TechId> sampleIds, Session session)
-    {
-        List<SamplePE> samples =
-                getDAOFactory().getSampleDAO().listByIDs(TechId.asLongs(sampleIds));
-        for (SamplePE sample : samples)
-        {
-            ExperimentPE experiment = sample.getExperiment();
-            if (experiment != null)
-            {
-                RelationshipUtils.updateModificationDateAndModifier(experiment, session);
-            }
-            SamplePE container = sample.getContainer();
-            if (container != null)
-            {
-                RelationshipUtils.updateModificationDateAndModifier(container, session);
-            }
-            List<SamplePE> parents = sample.getParents();
-            if (parents != null)
-            {
-                for (SamplePE parent : parents)
-                {
-                    RelationshipUtils.updateModificationDateAndModifier(parent, session);
-                }
-            }
-            Set<SampleRelationshipPE> childRelationships = sample.getChildRelationships();
-            if (childRelationships != null)
-            {
-                for (SampleRelationshipPE childRelationship : childRelationships)
-                {
-                    SamplePE childSample = childRelationship.getChildSample();
-                    RelationshipUtils.updateModificationDateAndModifier(childSample, session);
-                }
-            }
-        }
-    }
-
     @Override
     @RolesAllowed(RoleWithHierarchy.SPACE_POWER_USER)
     @Capability("DELETE_EXPERIMENT")
@@ -2203,11 +2131,12 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
             List<TechId> experimentIds, String reason, DeletionType deletionType)
     {
         Session session = getSession(sessionToken);
-        updateModificationDateAndModifierOfRelatedProjectsOfExperiments(experimentIds, session);
         IExperimentBO experimentBO = businessObjectFactory.createExperimentBO(session);
         switch (deletionType)
         {
             case PERMANENT:
+                List<ExperimentPE> experiments = getDAOFactory().getExperimentDAO().listByIDs(TechId.asLongs(experimentIds));
+                RelationshipUtils.updateModificationDateAndModifierOfRelatedProjectsOfExperiments(experiments, session);
                 experimentBO.deleteByTechIds(experimentIds, reason);
                 break;
             case TRASH:
@@ -2218,17 +2147,6 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
         }
     }
 
-    private void updateModificationDateAndModifierOfRelatedProjectsOfExperiments(
-            Collection<TechId> experimentIds, Session session)
-    {
-        List<ExperimentPE> experiments =
-                getDAOFactory().getExperimentDAO().listByIDs(TechId.asLongs(experimentIds));
-        for (ExperimentPE experiment : experiments)
-        {
-            RelationshipUtils.updateModificationDateAndModifier(experiment.getProject(), session);
-        }
-    }
-
     @Override
     @RolesAllowed(RoleWithHierarchy.INSTANCE_ADMIN)
     @Capability("DELETE_VOCABULARY")
@@ -4038,10 +3956,12 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
                 trashBO.revertDeletion(new TechId(deletionId));
             }
         }
-        updateModificationDateAndModifierOfRelatedProjectsOfExperiments(deletedExperimentIds,
-                session);
-        updateModificationDateAndModifierOfRelatedEntitiesOfSamples(deletedSampleIds, session);
-        updateModificationDateAndModifierOfRelatedEntitiesOfDataSets(deletedDataSetCodes, session);
+        List<ExperimentPE> experiments = getDAOFactory().getExperimentDAO().listByIDs(TechId.asLongs(deletedExperimentIds));
+        RelationshipUtils.updateModificationDateAndModifierOfRelatedProjectsOfExperiments(experiments, session);
+        List<SamplePE> samples = getDAOFactory().getSampleDAO().listByIDs(TechId.asLongs(deletedSampleIds));
+        RelationshipUtils.updateModificationDateAndModifierOfRelatedEntitiesOfSamples(samples, session);
+        List<DataPE> dataSets = getDAOFactory().getDataDAO().listByCode(new HashSet<String>(deletedDataSetCodes));
+        RelationshipUtils.updateModificationDateAndModifierOfRelatedEntitiesOfDataSets(dataSets, session);
     }
 
     @Override
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/TrashBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/TrashBO.java
index e29fc214e74..c7147e20c61 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/TrashBO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/TrashBO.java
@@ -19,6 +19,7 @@ package ch.systemsx.cisd.openbis.generic.server.business.bo;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.EnumSet;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -43,7 +44,6 @@ import ch.systemsx.cisd.openbis.generic.server.dataaccess.ISampleDAO;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSetFetchOption;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IIdAndCodeHolder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IIdHolder;
-import ch.systemsx.cisd.openbis.generic.shared.basic.IIdentifierHolder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.AbstractExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetArchivingStatus;
@@ -59,6 +59,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
 import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.managed_property.IManagedPropertyEvaluatorFactory;
 import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils;
+import ch.systemsx.cisd.openbis.generic.shared.util.RelationshipUtils;
 
 /**
  * @author Piotr Buczek
@@ -122,13 +123,14 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO
             if (sample != null)
             {
                 sampleIds.add(new TechId(sample));
-            }  if (dataSet.getExperiment() != null)
+            }  
+            if (dataSet.getExperiment() != null)
             {
                 experimentIds.add(new TechId(dataSet.getExperiment()));
             }
         }
         assertDataSetDeletionBusinessRules(experimentIds, sampleIds, dataSetIds);
-        TrashOperationsManager trashManager = new TrashOperationsManager(deletion, getDeletionDAO());
+        TrashOperationsManager trashManager = new TrashOperationsManager(session, deletion, this);
         trashDataSets(trashManager, dataSetIds, true, new IDataSetFilter()
             {
                 @Override
@@ -159,12 +161,10 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO
     public void trashExperiments(List<TechId> experimentIds)
     {
         assert deletion != null;
-        
-        TrashOperationsManager trashManager = new TrashOperationsManager(deletion, getDeletionDAO());
+        TrashOperationsManager trashManager = new TrashOperationsManager(session, deletion, this);
         trashManager.addTrashOperation(EntityKind.EXPERIMENT, experimentIds, true);
         Set<TechId> eIds = new LinkedHashSet<TechId>(experimentIds);
         Set<TechId> dependentSampleIds = trashExperimentDependentSamples(trashManager, eIds);
-        assertSampleDeletionBusinessRules(eIds, dependentSampleIds, dependentSampleIds);
         trashExperimentDependentDataSets(trashManager, eIds, dependentSampleIds);
         trashManager.trash();
     }
@@ -174,13 +174,12 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO
     {
         assert deletion != null;
 
-        TrashOperationsManager trashManager = new TrashOperationsManager(deletion, getDeletionDAO());
+        TrashOperationsManager trashManager = new TrashOperationsManager(session, deletion, this);
         Set<TechId> allSampleIds 
                 = trashSamples(trashManager, sampleIds, CascadeSampleDependentComponents.TRUE, true);
         Set<TechId> dependentSampleIds = new HashSet<TechId>(allSampleIds);
         dependentSampleIds.removeAll(sampleIds);
         Set<TechId> experimentsOfSamples = getExperimentsOfSamples(sampleIds);
-        assertSampleDeletionBusinessRules(experimentsOfSamples, new HashSet<TechId>(sampleIds), dependentSampleIds);
         trashSampleDependentDataSets(trashManager, experimentsOfSamples, allSampleIds);
         trashManager.trash();
     }
@@ -197,11 +196,6 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO
             {
                 experimentIds.add(new TechId(experiment));
             }
-            Sample container = sample.getContainer();
-            if (container != null)
-            {
-                
-            }
         }
         return experimentIds;
     }
@@ -282,20 +276,8 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO
         {
             allDeletables.removeAll(deletableOriginals);
             trashManager.addTrashOperation(EntityKind.DATA_SET, deletableOriginals, true);
-            trashManager.addTrashOperation(EntityKind.DATA_SET, allDeletables, false);
-        } else
-        {
-            int nonDeletable = dataSetIds.size() - deletableOriginals.size();
-            if (nonDeletable > 0)
-            {
-                dataSetIds.removeAll(deletableOriginals);
-//                throw new UserFailureException("The following related data sets couldn't be deleted "
-//                        + "because they are contained in data sets outside the deletion set: " 
-//                        + Code.extractCodes(datasetLister.listByDatasetIds(TechId.asLongs(dataSetIds), 
-//                                EnumSet.of(DataSetFetchOption.BASIC))));
-            }
-            trashManager.addTrashOperation(EntityKind.DATA_SET, allDeletables, false);
         }
+        trashManager.addTrashOperation(EntityKind.DATA_SET, allDeletables, false);
     }
 
     private void checkForNonDeletableDataSets(List<ExternalDataPE> unavailableDataSets)
@@ -423,51 +405,6 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO
                                 }));
     }
     
-    private void assertSampleDeletionBusinessRules(Set<TechId> experimentIds, Set<TechId> originalSampleIds, Set<TechId> sampleIds)
-    {
-        Set<Long> eIds = new LinkedHashSet<Long>(TechId.asLongs(experimentIds));
-        Set<Long> sIds = new LinkedHashSet<Long>(TechId.asLongs(originalSampleIds));
-        ISampleLister sampleLister = boFactory.createSampleLister(session);
-        List<Sample> samples = sampleLister.list(new ListOrSearchSampleCriteria(TechId.asLongs(sampleIds)));
-        StringBuilder builder = new StringBuilder();
-        int numberOfForeignSamples = 0;
-        for (Sample sample : samples)
-        {
-            if (numberOfForeignSamples >= 10)
-            {
-                break;
-            }
-            Experiment experiment = sample.getExperiment();
-            if (experiment != null)
-            {
-                if (eIds.contains(experiment.getId()) == false)
-                {
-                    addTo(builder, "belongs to the experiment", sample, experiment);
-                    numberOfForeignSamples++;
-                }
-            }
-            Sample container = sample.getContainer();
-            if (container != null)
-            {
-                if (sIds.contains(container.getId()) == false)
-                {
-                    addTo(builder, "is a component of the sample", sample, container);
-                    numberOfForeignSamples++;
-                }
-            }
-        }
-        if (numberOfForeignSamples > 0)
-        {
-            throw new UserFailureException(builder.toString().trim());
-        }
-    }
-    
-    private void addTo(StringBuilder builder, String entityDescription, Sample sample, IIdentifierHolder outsider)
-    {
-        builder.append("The sample " + sample.getIdentifier() + " " + entityDescription + " " 
-                + outsider.getIdentifier() + " which is outside the deletion set.\n");
-    }
-    
     private void assertDataSetDeletionBusinessRules(Set<TechId> experimentIds, Set<TechId> sampleIdes, 
             List<TechId> dataSetIds)
     {
@@ -561,31 +498,67 @@ public class TrashBO extends AbstractBusinessObject implements ITrashBO
     
     private static final class TrashOperationsManager
     {
+        private final Session session;
         private final DeletionPE deletion;
-        private final IDeletionDAO deletionDAO;
+        private final IDAOFactory daoFactory;
+        
+        private final Map<EntityKind, Set<TechId>> entityIdsByKind = new HashMap<EntityKind, Set<TechId>>();
         private final List<TrashBatchOperation> operations = new ArrayList<TrashBatchOperation>();
         
-        TrashOperationsManager(DeletionPE deletion, IDeletionDAO deletionDAO)
+        TrashOperationsManager(Session session, DeletionPE deletion, IDAOFactory daoFactory)
         {
+            this.session = session;
             this.deletion = deletion;
-            this.deletionDAO = deletionDAO;
+            this.daoFactory = daoFactory;
+            EntityKind[] values = EntityKind.values();
+            for (EntityKind entityKind : values)
+            {
+                entityIdsByKind.put(entityKind, new HashSet<TechId>());
+            }
         }
         
         void addTrashOperation(EntityKind entityKind, List<TechId> entityIds, boolean isOriginalDeletion)
         {
             if (entityIds.isEmpty() == false)
             {
+                entityIdsByKind.get(entityKind).addAll(entityIds);
+                IDeletionDAO deletionDAO = daoFactory.getDeletionDAO();
                 operations.add(new TrashBatchOperation(entityKind, entityIds, deletion, deletionDAO, isOriginalDeletion));
             }
         }
         
         void trash()
         {
+            updateModificationDateAndModifierOfRelatedProjectsOfExperiments();
+            updateModificationDateAndModifierOfRelatedEntitiesOfSamples();
+            updateModificationDateAndModifierOfRelatedEntitiesOfDataSets();
             for (TrashBatchOperation operation : operations)
             {
                 BatchOperationExecutor.executeInBatches(operation);
             }
         }
+        
+        private void updateModificationDateAndModifierOfRelatedProjectsOfExperiments()
+        {
+            List<Long> ids = TechId.asLongs(entityIdsByKind.get(EntityKind.EXPERIMENT));
+            List<ExperimentPE> experiments = daoFactory.getExperimentDAO().listByIDs(ids);
+            RelationshipUtils.updateModificationDateAndModifierOfRelatedProjectsOfExperiments(experiments, session);
+        }
+        
+        private void updateModificationDateAndModifierOfRelatedEntitiesOfSamples()
+        {
+            List<Long> ids = TechId.asLongs(entityIdsByKind.get(EntityKind.SAMPLE));
+            List<SamplePE> samples = daoFactory.getSampleDAO().listByIDs(ids);
+            RelationshipUtils.updateModificationDateAndModifierOfRelatedEntitiesOfSamples(samples, session);
+        }
+        
+        private void updateModificationDateAndModifierOfRelatedEntitiesOfDataSets()
+        {
+            List<Long> ids = TechId.asLongs(entityIdsByKind.get(EntityKind.DATA_SET));
+            List<DataPE> dataSets = daoFactory.getDataDAO().listByIDs(ids);
+            RelationshipUtils.updateModificationDateAndModifierOfRelatedEntitiesOfDataSets(dataSets, session);
+
+        }
     }
 
     private static class TrashBatchOperation implements IBatchOperation<TechId>
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/util/RelationshipUtils.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/util/RelationshipUtils.java
index 2785bf4f7d3..820f996a26d 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/util/RelationshipUtils.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/util/RelationshipUtils.java
@@ -20,6 +20,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
 import java.util.List;
+import java.util.Set;
 
 import ch.systemsx.cisd.openbis.generic.shared.basic.BasicConstant;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
@@ -29,6 +30,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.IAuthSession;
 import ch.systemsx.cisd.openbis.generic.shared.dto.IModifierAndModificationDateBean;
 import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.SampleRelationshipPE;
 
 /**
  * Utility function for relation ship.
@@ -37,6 +39,81 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
  */
 public class RelationshipUtils
 {
+
+    public static void updateModificationDateAndModifierOfRelatedProjectsOfExperiments(List<ExperimentPE> experiments, IAuthSession session)
+    {
+        for (ExperimentPE experiment : experiments)
+        {
+            updateModificationDateAndModifier(experiment.getProject(), session);
+        }
+    }
+
+    public static void updateModificationDateAndModifierOfRelatedEntitiesOfSamples(List<SamplePE> samples, IAuthSession session)
+    {
+        for (SamplePE sample : samples)
+        {
+            ExperimentPE experiment = sample.getExperiment();
+            if (experiment != null)
+            {
+                updateModificationDateAndModifier(experiment, session);
+            }
+            SamplePE container = sample.getContainer();
+            if (container != null)
+            {
+                updateModificationDateAndModifier(container, session);
+            }
+            List<SamplePE> parents = sample.getParents();
+            if (parents != null)
+            {
+                for (SamplePE parent : parents)
+                {
+                    updateModificationDateAndModifier(parent, session);
+                }
+            }
+            Set<SampleRelationshipPE> childRelationships = sample.getChildRelationships();
+            if (childRelationships != null)
+            {
+                for (SampleRelationshipPE childRelationship : childRelationships)
+                {
+                    SamplePE childSample = childRelationship.getChildSample();
+                    updateModificationDateAndModifier(childSample, session);
+                }
+            }
+        }
+    }
+
+    public static void updateModificationDateAndModifierOfRelatedEntitiesOfDataSets(List<DataPE> dataSets, IAuthSession session)
+    {
+        for (DataPE dataSet : dataSets)
+        {
+            ExperimentPE experiment = dataSet.getExperiment();
+            updateModificationDateAndModifier(experiment, session);
+            SamplePE sample = dataSet.tryGetSample();
+            if (sample != null)
+            {
+                updateModificationDateAndModifier(sample, session);
+            }
+            RelationshipUtils.updateModificationDateAndModifierOfDataSets(dataSet.getChildren(), session);
+            RelationshipUtils.updateModificationDateAndModifierOfDataSets(dataSet.getParents(), session);
+            Set<DataSetRelationshipPE> relationships = dataSet.getParentRelationships();
+            for (DataSetRelationshipPE relationship : getContainerComponentRelationships(relationships))
+            {
+                updateModificationDateAndModifier(relationship.getParentDataSet(), session);
+            }
+        }
+    }
+
+    private static void updateModificationDateAndModifierOfDataSets(List<DataPE> dataSets, IAuthSession session)
+    {
+        if (dataSets != null)
+        {
+            for (DataPE child : dataSets)
+            {
+                updateModificationDateAndModifier(child, session);
+            }
+        }
+    }
+
     public static boolean isParentChildRelationship(DataSetRelationshipPE relationship)
     {
         return isRelationshipOfType(relationship, BasicConstant.PARENT_CHILD_INTERNAL_RELATIONSHIP);
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/DeletionTestCase.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/DeletionTestCase.java
index 6f00a7805e3..07f59affe13 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/DeletionTestCase.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/DeletionTestCase.java
@@ -31,6 +31,7 @@ import java.util.Set;
 
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.Predicate;
+import org.hibernate.Session;
 import org.hibernate.SessionFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.testng.annotations.AfterMethod;
@@ -183,6 +184,7 @@ public class DeletionTestCase extends SystemTestCase
                     }
                 }
             }
+            flushAndClearHibernateSession();
             commonServer.deleteExperiments(sessionToken, TechId.createList(existingExperiments),
                     REASON, DeletionType.TRASH);
             commonServer.deletePermanently(sessionToken, TechId.createList(listDeletions()));
@@ -220,6 +222,7 @@ public class DeletionTestCase extends SystemTestCase
         assertSamplesExist(registeredSamples);
 
         // delete permanently
+        flushAndClearHibernateSession();
         commonServer.deleteExperiments(sessionToken, Collections.singletonList(experimentId),
                 REASON, DeletionType.TRASH);
         final TechId deletionId2 = TechId.create(listDeletions().get(0));
@@ -228,7 +231,6 @@ public class DeletionTestCase extends SystemTestCase
         assertSamplesDoNotExist(registeredSamplesThatShouldBeDeleted);
     }
 
-    
     @Autowired
     SessionFactory sessionFactory;
     @Test
@@ -529,4 +531,11 @@ public class DeletionTestCase extends SystemTestCase
         return result;
     }
 
+    private void flushAndClearHibernateSession()
+    {
+        Session currentSession = daoFactory.getSessionFactory().getCurrentSession();
+        currentSession.flush();
+        currentSession.clear();
+    }
+
 }
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/EntityDeletionTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/EntityDeletionTest.java
index 2101db3b6b1..49f1aa67278 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/EntityDeletionTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/EntityDeletionTest.java
@@ -89,10 +89,12 @@ public class EntityDeletionTest extends BaseTest
         EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S2\n"
                 + "S1, components: S2\n");
         
-        failTrashExperiment(g.e(1), createExpectedErrorMessage(g.s(2), g.s(1)));
+        deleteExperiments(g.e(1));
         
-        assertEquals("E1, samples: S2\n"
-                + "S1, components: S2\n", renderGraph(g));
+        assertEquals("", renderGraph(g));
+        assertModified(g.s(1));
+        assertDeleted(g.e(1));
+        assertDeleted(g.s(2));
         assertUnmodifiedAndUndeleted(g);
     }
     
@@ -265,12 +267,14 @@ public class EntityDeletionTest extends BaseTest
     @Test
     public final void testTrashComponentSample()
     {
-        EntityGraphGenerator g = parseAndCreateGraph("S1, components: S2\n");
+        EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S2\n"
+                + "S1, components: S2\n");
         
         deleteSamples(g.s(2));
         
         assertEquals("", renderGraph(g));
         assertDeleted(g.s(2));
+        assertModified(g.e(1));
         assertModified(g.s(1));
         assertUnmodifiedAndUndeleted(g);
     }
@@ -312,11 +316,12 @@ public class EntityDeletionTest extends BaseTest
                 + "S1, components: S2\n"
                 + "S2, data sets: DS1\n");
         
-        failTrashSample(g.s(1), createExpectedErrorMessage(g.s(2), g.e(1)));
+        deleteSamples(g.s(1));
         
-        assertEquals("E1, samples: S2, data sets: DS1\n"
-                + "S1, components: S2\n"
-                + "S2, data sets: DS1\n", renderGraph(g));
+        assertEquals("", renderGraph(g));
+        assertDeleted(g.s(1), g.s(2));
+        assertDeleted(g.ds(1));
+        assertModified(g.e(1));
         assertUnmodifiedAndUndeleted(g);
     }
     
@@ -552,6 +557,7 @@ public class EntityDeletionTest extends BaseTest
             experimentIdentifiers.add(entityGraphManager.getExperimentIdentifierOrNull(experimentNode));
         }
         deleteExperiments(experimentIdentifiers, createAdminUser());
+        flushAndClearHibernateSession();
     }
 
     private void deleteSamples(SampleNode...sampleNodes)
@@ -562,6 +568,7 @@ public class EntityDeletionTest extends BaseTest
             samplePermIds.add(entityGraphManager.getSamplePermIdOrNull(sampleNode));
         }
         deleteSamples(samplePermIds, createAdminUser());
+        flushAndClearHibernateSession();
     }
     
     private void deleteDataSets(DataSetNode...dataSetNodes)
@@ -572,6 +579,7 @@ public class EntityDeletionTest extends BaseTest
             dataSetCodes.add(entityGraphManager.getDataSetCodeOrNull(dataSetNode));
         }
         deleteDataSets(dataSetCodes, createAdminUser());
+        flushAndClearHibernateSession();
     }
     
     private String createAdminUser()
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/base/BaseTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/base/BaseTest.java
index ad6a27abc0d..df369108ff1 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/base/BaseTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/base/BaseTest.java
@@ -477,10 +477,15 @@ public abstract class BaseTest extends AbstractTransactionalTestNGSpringContextT
     protected EntityGraphGenerator parseAndCreateGraph(String graphDefinition)
     {
         EntityGraphGenerator graphGenerator = entityGraphManager.parseAndCreateGraph(graphDefinition);
+        flushAndClearHibernateSession();
+        return graphGenerator;
+    }
+
+    protected void flushAndClearHibernateSession()
+    {
         org.hibernate.Session currentSession = daoFactory.getSessionFactory().getCurrentSession();
         currentSession.flush();
         currentSession.clear();
-        return graphGenerator;
     }
 
     protected String renderGraph(EntityGraphGenerator g)
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/entitygraph/EntityGraphManager.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/entitygraph/EntityGraphManager.java
index 7e917a592c5..fdae11f7e8c 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/entitygraph/EntityGraphManager.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/entitygraph/EntityGraphManager.java
@@ -689,6 +689,11 @@ public class EntityGraphManager
                 CodeWithRegistrationAndModificationDate<?> entity)
         {
             builder.append(prefix).append(id).append(" -> ").append(entity.getCode()).append(" (");
+            if (entity instanceof IIdHolder)
+            {
+                IIdHolder idHolder = (IIdHolder) entity;
+                builder.append(idHolder.getId()).append(", ");
+            }
             builder.append(entity.getModifier().getUserId()).append(", ");
             builder.append(entity.getModificationDate()).append(")\n");
         }
-- 
GitLab