diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/context/Context.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/context/Context.java
index 28a53e6aa89da9a39a0cfbdd56a34297b8041221..1b6641cef3d6398dfd22dd4f4e61446803ea06ef 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/context/Context.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/context/Context.java
@@ -63,7 +63,8 @@ public class Context implements IContext
     public void pushProgress(IProgress progress)
     {
         progressStack.push(progress);
-        notifyProgressListeners();
+        // temporarily comment out until SSDM-3543 is finished
+        // notifyProgressListeners();
     }
 
     @Override
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/context/IProgress.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/context/IProgress.java
index 1a5daa1e5bf2049d7292745225281088aa930718..d30010a40d6f7a0b45c954c264444092b516b984 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/context/IProgress.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/context/IProgress.java
@@ -26,6 +26,8 @@ public interface IProgress extends Serializable
 
     String getLabel();
 
+    String getDetails();
+
     Integer getTotalItemsToProcess();
 
     Integer getNumItemsProcessed();
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/context/LazyLoadedProgress.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/context/LazyLoadedProgress.java
new file mode 100644
index 0000000000000000000000000000000000000000..cdc8553582ab17a9d089bde45b06cf36bacff378
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/context/LazyLoadedProgress.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016 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.ethz.sis.openbis.generic.server.asapi.v3.context;
+
+/**
+ * @author pkupczyk
+ */
+public abstract class LazyLoadedProgress implements IProgress
+{
+
+    private static final long serialVersionUID = 1L;
+
+    private IProgress loadedProgress;
+
+    protected abstract IProgress load();
+
+    @Override
+    public String getLabel()
+    {
+        return getLoadedProgress().getLabel();
+    }
+
+    @Override
+    public String getDetails()
+    {
+        return getLoadedProgress().getDetails();
+    }
+
+    @Override
+    public Integer getTotalItemsToProcess()
+    {
+        return getLoadedProgress().getTotalItemsToProcess();
+    }
+
+    @Override
+    public Integer getNumItemsProcessed()
+    {
+        return getLoadedProgress().getNumItemsProcessed();
+    }
+
+    @Override
+    public String toString()
+    {
+        return getLoadedProgress().toString();
+    }
+
+    private IProgress getLoadedProgress()
+    {
+        if (loadedProgress == null)
+        {
+            loadedProgress = load();
+        }
+        return loadedProgress;
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/context/Progress.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/context/Progress.java
index 311ce8c8397c6cc5d5fb2a1803c026c85e06afca..eb7fcf55fa5ecd8661c49e9db646a2c6dd32b0a8 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/context/Progress.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/context/Progress.java
@@ -26,20 +26,23 @@ public class Progress implements IProgress
 
     private String label;
 
-    private Integer totalItemsToProcess;
+    private String details;
 
     private Integer numItemsProcessed;
 
+    private Integer totalItemsToProcess;
+
     public Progress(String label)
     {
         this.label = label;
     }
 
-    public Progress(String label, int totalItemsToProcess, int numItemsProcessed)
+    public Progress(String label, String details, int numItemsProcessed, int totalItemsToProcess)
     {
         this.label = label;
-        this.totalItemsToProcess = totalItemsToProcess;
+        this.details = details;
         this.numItemsProcessed = numItemsProcessed;
+        this.totalItemsToProcess = totalItemsToProcess;
     }
 
     @Override
@@ -48,6 +51,12 @@ public class Progress implements IProgress
         return label;
     }
 
+    @Override
+    public String getDetails()
+    {
+        return details;
+    }
+
     @Override
     public Integer getTotalItemsToProcess()
     {
@@ -63,13 +72,22 @@ public class Progress implements IProgress
     @Override
     public String toString()
     {
+        StringBuilder result = new StringBuilder();
+
         if (getTotalItemsToProcess() == null && getNumItemsProcessed() == null)
         {
-            return getLabel();
+            result.append(getLabel());
         } else
         {
-            return getLabel() + " (" + getNumItemsProcessed() + "/" + getTotalItemsToProcess() + ")";
+            result.append(getLabel() + " (" + getNumItemsProcessed() + "/" + getTotalItemsToProcess() + ")");
         }
+
+        if (getDetails() != null)
+        {
+            result.append(" [" + getDetails() + "]");
+        }
+
+        return result.toString();
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/CreateDataSetExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/CreateDataSetExecutor.java
index a1b778caed0a10a7ea85362bd1e529c16f564976..fc44d90c7e1cbe32fce56932c5303b15344d660f 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/CreateDataSetExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/CreateDataSetExecutor.java
@@ -36,11 +36,16 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.id.IEntityTypeId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.ObjectNotFoundException;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessException;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractCreateEntityExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IMapEntityTypeByIdExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property.IUpdateEntityPropertyExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.tag.IAddTagToEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.CheckDataProgress;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.authorization.validator.DataSetPEByExperimentOrSampleIdentifierValidator;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.DataAccessExceptionTranslator;
@@ -98,28 +103,38 @@ public class CreateDataSetExecutor extends AbstractCreateEntityExecutor<DataSetC
     private IVerifyDataSetExecutor verifyDataSetExecutor;
 
     @Override
-    protected List<DataPE> createEntities(IOperationContext context, Collection<DataSetCreation> creations)
+    protected List<DataPE> createEntities(final IOperationContext context, CollectionBatch<DataSetCreation> batch)
     {
         // Get Types
         Collection<IEntityTypeId> typeIds = new HashSet<IEntityTypeId>();
 
-        for (DataSetCreation creation : creations)
+        for (DataSetCreation creation : batch.getObjects())
         {
             typeIds.add(creation.getTypeId());
         }
 
-        Map<IEntityTypeId, EntityTypePE> types = mapEntityTypeByIdExecutor.map(context, EntityKind.DATA_SET, typeIds);
+        final Map<IEntityTypeId, EntityTypePE> types = mapEntityTypeByIdExecutor.map(context, EntityKind.DATA_SET, typeIds);
 
         // Validate DataSet creations
-        for (DataSetCreation creation : creations)
-        {
-            checkData(context, creation, types.get(creation.getTypeId()));
-        }
+        new CollectionBatchProcessor<DataSetCreation>(context, batch)
+            {
+                @Override
+                public void process(DataSetCreation object)
+                {
+                    checkData(context, object, types.get(object.getTypeId()));
+                }
+
+                @Override
+                public IProgress createProgress(DataSetCreation object, int objectIndex, int totalObjectCount)
+                {
+                    return new CheckDataProgress(object, objectIndex, totalObjectCount);
+                }
+            };
 
         IPermIdDAO codeGenerator = daoFactory.getPermIdDAO();
 
         List<DataPE> dataSets = new LinkedList<DataPE>();
-        for (DataSetCreation creation : creations)
+        for (DataSetCreation creation : batch.getObjects())
         {
             DataSetTypePE type = (DataSetTypePE) types.get(creation.getTypeId());
 
@@ -206,22 +221,22 @@ public class CreateDataSetExecutor extends AbstractCreateEntityExecutor<DataSetC
     }
 
     @Override
-    protected void checkBusinessRules(IOperationContext context, Collection<DataPE> entities)
+    protected void checkBusinessRules(IOperationContext context, CollectionBatch<DataPE> batch)
     {
-        verifyDataSetExecutor.verify(context, entities);
+        verifyDataSetExecutor.verify(context, batch);
     }
 
     @Override
-    protected void updateBatch(IOperationContext context, Map<DataSetCreation, DataPE> entitiesMap)
+    protected void updateBatch(IOperationContext context, MapBatch<DataSetCreation, DataPE> batch)
     {
-        setDataSetPhysicalDataExecutor.set(context, entitiesMap);
-        setDataSetLinkedDataExecutor.set(context, entitiesMap);
-        setDataSetDataStoreExecutor.set(context, entitiesMap);
-        setDataSetSampleExecutor.set(context, entitiesMap);
-        setDataSetExperimentExecutor.set(context, entitiesMap);
+        setDataSetPhysicalDataExecutor.set(context, batch);
+        setDataSetLinkedDataExecutor.set(context, batch);
+        setDataSetDataStoreExecutor.set(context, batch);
+        setDataSetSampleExecutor.set(context, batch);
+        setDataSetExperimentExecutor.set(context, batch);
 
         Map<IEntityPropertiesHolder, Map<String, String>> propertyMap = new HashMap<IEntityPropertiesHolder, Map<String, String>>();
-        for (Map.Entry<DataSetCreation, DataPE> entry : entitiesMap.entrySet())
+        for (Map.Entry<DataSetCreation, DataPE> entry : batch.getObjects().entrySet())
         {
             propertyMap.put(entry.getValue(), entry.getKey().getProperties());
         }
@@ -229,11 +244,11 @@ public class CreateDataSetExecutor extends AbstractCreateEntityExecutor<DataSetC
     }
 
     @Override
-    protected void updateAll(IOperationContext context, Map<DataSetCreation, DataPE> entitiesMap)
+    protected void updateAll(IOperationContext context, MapBatch<DataSetCreation, DataPE> batch)
     {
         Map<IEntityWithMetaprojects, Collection<? extends ITagId>> tagMap = new HashMap<IEntityWithMetaprojects, Collection<? extends ITagId>>();
 
-        for (Map.Entry<DataSetCreation, DataPE> entry : entitiesMap.entrySet())
+        for (Map.Entry<DataSetCreation, DataPE> entry : batch.getObjects().entrySet())
         {
             DataSetCreation creation = entry.getKey();
             DataPE entity = entry.getValue();
@@ -241,7 +256,7 @@ public class CreateDataSetExecutor extends AbstractCreateEntityExecutor<DataSetC
         }
 
         addTagToEntityExecutor.add(context, tagMap);
-        setDataSetRelatedDataSetsExecutor.set(context, entitiesMap);
+        setDataSetRelatedDataSetsExecutor.set(context, batch);
     }
 
     @Override
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetDataStoreExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetDataStoreExecutor.java
index c9b5eec766fbe3b6581bdb23867f60196cdc4c71..fd51c814cdb5fb3aabed4b4826360d84aaa38c9e 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetDataStoreExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetDataStoreExecutor.java
@@ -16,10 +16,9 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.dataset;
 
-import java.util.Map;
-
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.DataSetCreation;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 
 /**
@@ -28,6 +27,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 public interface ISetDataSetDataStoreExecutor
 {
 
-    void set(IOperationContext context, Map<DataSetCreation, DataPE> entitiesMap);
+    void set(IOperationContext context, MapBatch<DataSetCreation, DataPE> batch);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetExternalDmsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetExternalDmsExecutor.java
index 0f7e4462c7b7e848106c812c8c5804091d13924c..04aebac7efbcb9603647cf2af5e3472d5a75fae7 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetExternalDmsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetExternalDmsExecutor.java
@@ -16,10 +16,9 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.dataset;
 
-import java.util.Map;
-
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.DataSetCreation;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 
 /**
@@ -28,6 +27,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 public interface ISetDataSetExternalDmsExecutor
 {
 
-    void set(IOperationContext context, Map<DataSetCreation, DataPE> entitiesMap);
+    void set(IOperationContext context, MapBatch<DataSetCreation, DataPE> batch);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetFileFormatTypeExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetFileFormatTypeExecutor.java
index 91bb0306543879e5b8023d70e55e796831e28e1a..94502544f53d36a5e38d8db9c887ba1d5f72e5be 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetFileFormatTypeExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetFileFormatTypeExecutor.java
@@ -16,10 +16,9 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.dataset;
 
-import java.util.Map;
-
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.DataSetCreation;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 
 /**
@@ -28,6 +27,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 public interface ISetDataSetFileFormatTypeExecutor
 {
 
-    void set(IOperationContext context, Map<DataSetCreation, DataPE> entitiesMap);
+    void set(IOperationContext context, MapBatch<DataSetCreation, DataPE> batch);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetLinkedDataExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetLinkedDataExecutor.java
index 6179c4f5468466823e5ba59025c5cdebe203bb9a..8cf2884c4745f87ad73be514dbf9848873b4bfed 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetLinkedDataExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetLinkedDataExecutor.java
@@ -16,10 +16,9 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.dataset;
 
-import java.util.Map;
-
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.DataSetCreation;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 
 /**
@@ -28,6 +27,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 public interface ISetDataSetLinkedDataExecutor
 {
 
-    void set(IOperationContext context, Map<DataSetCreation, DataPE> entitiesMap);
+    void set(IOperationContext context, MapBatch<DataSetCreation, DataPE> batch);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetLocatorTypeExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetLocatorTypeExecutor.java
index 3d1c909e93f68779f7644c7b2271b31f1b87100c..458efc4526aa70e90da4cfd9ab9824e89b862fc3 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetLocatorTypeExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetLocatorTypeExecutor.java
@@ -16,10 +16,9 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.dataset;
 
-import java.util.Map;
-
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.DataSetCreation;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 
 /**
@@ -28,6 +27,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 public interface ISetDataSetLocatorTypeExecutor
 {
 
-    void set(IOperationContext context, Map<DataSetCreation, DataPE> entitiesMap);
+    void set(IOperationContext context, MapBatch<DataSetCreation, DataPE> batch);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetPhysicalDataExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetPhysicalDataExecutor.java
index 4661f9ae5a287c96fbdfc66280f068f8a03fc25e..edf1f48c03bee7f8d12a139e65c835ffcd3b8001 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetPhysicalDataExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetPhysicalDataExecutor.java
@@ -16,10 +16,9 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.dataset;
 
-import java.util.Map;
-
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.DataSetCreation;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 
 /**
@@ -28,6 +27,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 public interface ISetDataSetPhysicalDataExecutor
 {
 
-    void set(IOperationContext context, Map<DataSetCreation, DataPE> entitiesMap);
+    void set(IOperationContext context, MapBatch<DataSetCreation, DataPE> batch);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetStorageFormatExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetStorageFormatExecutor.java
index 08631a52236dbb4da835fba4e9fcac69c222ef06..a9a1fe34dee1e561d9862fbe8e4195605af09397 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetStorageFormatExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/ISetDataSetStorageFormatExecutor.java
@@ -16,10 +16,9 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.dataset;
 
-import java.util.Map;
-
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.DataSetCreation;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 
 /**
@@ -28,6 +27,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 public interface ISetDataSetStorageFormatExecutor
 {
 
-    void set(IOperationContext context, Map<DataSetCreation, DataPE> entitiesMap);
+    void set(IOperationContext context, MapBatch<DataSetCreation, DataPE> batch);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/IUpdateDataSetRelatedDataSetsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/IUpdateDataSetRelatedDataSetsExecutor.java
index 6ef944893d4582f87ca1ec851af03c5dc70d7aa9..1e6eeb95c7cdd0d1473b0a85d00215a3870209b6 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/IUpdateDataSetRelatedDataSetsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/IUpdateDataSetRelatedDataSetsExecutor.java
@@ -16,10 +16,9 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.dataset;
 
-import java.util.Map;
-
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.update.DataSetUpdate;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 
 /**
@@ -28,6 +27,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 public interface IUpdateDataSetRelatedDataSetsExecutor
 {
 
-    public void update(IOperationContext context, Map<DataSetUpdate, DataPE> entitiesMap);
+    public void update(IOperationContext context, MapBatch<DataSetUpdate, DataPE> batch);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/IVerifyDataSetExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/IVerifyDataSetExecutor.java
index dc618c54d5d1aafde8bf82c9bfe6a64e71cec3b1..61c6b42dd8df2673132df4563383ed160ae69da2 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/IVerifyDataSetExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/IVerifyDataSetExecutor.java
@@ -16,9 +16,8 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.dataset;
 
-import java.util.Collection;
-
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 
 /**
@@ -27,6 +26,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 public interface IVerifyDataSetExecutor
 {
 
-    public void verify(IOperationContext context, Collection<DataPE> entities);
+    public void verify(IOperationContext context, CollectionBatch<DataPE> entities);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetChildrenExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetChildrenExecutor.java
index bb0753745ccd4b0713db05dd8f8b78f0eb843083..0be0bc7fe52d19d41d706ab7aab77d0274603f27 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetChildrenExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetChildrenExecutor.java
@@ -22,7 +22,6 @@ import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.DataSetCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.IDataSetId;
-import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractSetEntityToManyRelationExecutor;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
@@ -35,6 +34,12 @@ public class SetDataSetChildrenExecutor extends AbstractSetEntityToManyRelationE
         ISetDataSetChildrenExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-children";
+    }
+
     @Override
     protected Collection<? extends IDataSetId> getRelatedIds(IOperationContext context, DataSetCreation creation)
     {
@@ -44,14 +49,10 @@ public class SetDataSetChildrenExecutor extends AbstractSetEntityToManyRelationE
     @Override
     protected void setRelated(IOperationContext context, DataPE parent, Collection<DataPE> children)
     {
-        context.pushProgress(new Progress("set children for dataset " + parent.getCode()));
-
         for (DataPE child : children)
         {
             relationshipService.addParentToDataSet(context.getSession(), child, parent);
         }
-
-        context.popProgress();
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetComponentsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetComponentsExecutor.java
index 4bf2e385e0832cb3a706ce85231e10722a599a15..0bb9f82ba8c6d4492642d61827ed4a3bf9493c6a 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetComponentsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetComponentsExecutor.java
@@ -22,7 +22,6 @@ import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.DataSetCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.IDataSetId;
-import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractSetEntityToManyRelationExecutor;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
@@ -36,6 +35,12 @@ public class SetDataSetComponentsExecutor extends AbstractSetEntityToManyRelatio
         ISetDataSetComponentsExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-components";
+    }
+
     @Override
     protected Collection<? extends IDataSetId> getRelatedIds(IOperationContext context, DataSetCreation creation)
     {
@@ -45,8 +50,6 @@ public class SetDataSetComponentsExecutor extends AbstractSetEntityToManyRelatio
     @Override
     protected void setRelated(IOperationContext context, DataPE container, Collection<DataPE> components)
     {
-        context.pushProgress(new Progress("set components for dataset " + container.getCode()));
-
         if (false == components.isEmpty())
         {
             if (false == container.isContainer())
@@ -60,8 +63,6 @@ public class SetDataSetComponentsExecutor extends AbstractSetEntityToManyRelatio
                 relationshipService.assignDataSetToContainer(context.getSession(), component, container);
             }
         }
-
-        context.popProgress();
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetContainerExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetContainerExecutor.java
index 343f817bf9bdf314f89849c47025606f3126a444..26743a78425bf9a44ef192cd17d99a76e2d7442b 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetContainerExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetContainerExecutor.java
@@ -22,7 +22,6 @@ import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.DataSetCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.IDataSetId;
-import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractSetEntityToManyRelationExecutor;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
@@ -36,6 +35,12 @@ public class SetDataSetContainerExecutor extends AbstractSetEntityToManyRelation
         ISetDataSetContainerExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-containers";
+    }
+
     @Override
     protected Collection<? extends IDataSetId> getRelatedIds(IOperationContext context, DataSetCreation creation)
     {
@@ -45,8 +50,6 @@ public class SetDataSetContainerExecutor extends AbstractSetEntityToManyRelation
     @Override
     protected void setRelated(IOperationContext context, DataPE component, Collection<DataPE> containers)
     {
-        context.pushProgress(new Progress("set containers for dataset " + component.getCode()));
-
         for (DataPE container : containers)
         {
             if (false == container.isContainer())
@@ -56,8 +59,6 @@ public class SetDataSetContainerExecutor extends AbstractSetEntityToManyRelation
             }
             relationshipService.assignDataSetToContainer(context.getSession(), component, container);
         }
-
-        context.popProgress();
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetDataStoreExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetDataStoreExecutor.java
index 379e82599abe73c7de4a1b324fd9a1afc3412f30..5f11c1e8d82d3eb681e3e625c549b1b1f3bcd58c 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetDataStoreExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetDataStoreExecutor.java
@@ -39,6 +39,12 @@ public class SetDataSetDataStoreExecutor extends AbstractSetEntityToOneRelationE
         implements ISetDataSetDataStoreExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-datastore";
+    }
+
     @Autowired
     private IMapDataStoreByIdExecutor mapDataStoreByIdExecutor;
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetExperimentExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetExperimentExecutor.java
index 391310f6c5097043a5e0b444a83587a87b44b6b4..1211dd97402e27a864cc9ebcd983750e3cd1e32f 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetExperimentExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetExperimentExecutor.java
@@ -41,6 +41,12 @@ public class SetDataSetExperimentExecutor extends AbstractSetEntityExperimentRel
     @Autowired
     private IDAOFactory daoFactory;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-experiment";
+    }
+
     @Override
     protected IExperimentId getRelatedId(DataSetCreation creation)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetExternalDmsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetExternalDmsExecutor.java
index ce8a7c1c4ba07361997cc67830b3c5fcc980d766..e9bd47e2bcf84bba8dbca041972fb5acfa529121 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetExternalDmsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetExternalDmsExecutor.java
@@ -44,6 +44,12 @@ public class SetDataSetExternalDmsExecutor extends
     @Autowired
     private IMapExternalDmsByIdExecutor mapExternalDmsByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-externaldms";
+    }
+
     @Override
     protected IExternalDmsId getRelatedId(DataSetCreation creation)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetFileFormatTypeExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetFileFormatTypeExecutor.java
index 0d8fd4025fdd01e9942c574d4ea41b327ef92ad0..070008c749dda0dbeab40c18ecea52c0dcb9c3e8 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetFileFormatTypeExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetFileFormatTypeExecutor.java
@@ -43,6 +43,12 @@ public class SetDataSetFileFormatTypeExecutor extends
     @Autowired
     private IMapFileFormatTypeByIdExecutor mapFileFormatTypeByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-fileformattype";
+    }
+
     @Override
     protected IFileFormatTypeId getRelatedId(DataSetCreation creation)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetLinkedDataExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetLinkedDataExecutor.java
index 4817acdf9c8e760e062a9b105d89ff139de22daa..b8dd2494fa4e5360d5a05407b9662b485765d83d 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetLinkedDataExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetLinkedDataExecutor.java
@@ -24,6 +24,7 @@ import org.springframework.stereotype.Component;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.DataSetCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.LinkedDataCreation;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.LinkDataPE;
@@ -39,9 +40,9 @@ public class SetDataSetLinkedDataExecutor implements ISetDataSetLinkedDataExecut
     private ISetDataSetExternalDmsExecutor setDataSetExternalDmsExecutor;
 
     @Override
-    public void set(IOperationContext context, Map<DataSetCreation, DataPE> entitiesMap)
+    public void set(IOperationContext context, MapBatch<DataSetCreation, DataPE> batch)
     {
-        for (Map.Entry<DataSetCreation, DataPE> entry : entitiesMap.entrySet())
+        for (Map.Entry<DataSetCreation, DataPE> entry : batch.getObjects().entrySet())
         {
             DataSetCreation creation = entry.getKey();
             LinkedDataCreation linkedCreation = creation.getLinkedData();
@@ -63,7 +64,7 @@ public class SetDataSetLinkedDataExecutor implements ISetDataSetLinkedDataExecut
             }
         }
 
-        setDataSetExternalDmsExecutor.set(context, entitiesMap);
+        setDataSetExternalDmsExecutor.set(context, batch);
     }
 
     private void set(IOperationContext context, LinkedDataCreation linkedCreation, LinkDataPE dataSet)
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetLocatorTypeExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetLocatorTypeExecutor.java
index 5314f4b3f88ddb8e96053cc210d5b3b7bdd62662..d18263bf389f2cfb818d00ce863e22b2cf8814f1 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetLocatorTypeExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetLocatorTypeExecutor.java
@@ -44,6 +44,12 @@ public class SetDataSetLocatorTypeExecutor extends
     @Autowired
     private IMapLocatorTypeByIdExecutor mapLocatorTypeByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-locatortype";
+    }
+
     @Override
     protected ILocatorTypeId getRelatedId(DataSetCreation creation)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetParentsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetParentsExecutor.java
index 721d3c0fdc00d215b2858a739b00db8f5a5f7ce0..83c28592b28b5ac7f5c544e0d46cc8843310d9a6 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetParentsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetParentsExecutor.java
@@ -22,7 +22,6 @@ import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.DataSetCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.IDataSetId;
-import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractSetEntityToManyRelationExecutor;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
@@ -35,6 +34,12 @@ public class SetDataSetParentsExecutor extends AbstractSetEntityToManyRelationEx
         ISetDataSetParentsExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-parents";
+    }
+
     @Override
     protected Collection<? extends IDataSetId> getRelatedIds(IOperationContext context, DataSetCreation creation)
     {
@@ -44,14 +49,10 @@ public class SetDataSetParentsExecutor extends AbstractSetEntityToManyRelationEx
     @Override
     protected void setRelated(IOperationContext context, DataPE child, Collection<DataPE> parents)
     {
-        context.pushProgress(new Progress("set parents for dataset " + child.getCode()));
-
         for (DataPE parent : parents)
         {
             relationshipService.addParentToDataSet(context.getSession(), child, parent);
         }
-
-        context.popProgress();
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetPhysicalDataExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetPhysicalDataExecutor.java
index d483b967c1ed9e2fdd0d72f5a29ac043f7c2926c..b313394b0e3bcf8cbc3df7d116985c48e0ad7efa 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetPhysicalDataExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetPhysicalDataExecutor.java
@@ -25,6 +25,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.Complete;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.DataSetCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.PhysicalDataCreation;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.common.types.BooleanOrUnknown;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
@@ -47,9 +48,9 @@ public class SetDataSetPhysicalDataExecutor implements ISetDataSetPhysicalDataEx
     private ISetDataSetLocatorTypeExecutor setDataSetLocatorTypeExecutor;
 
     @Override
-    public void set(IOperationContext context, Map<DataSetCreation, DataPE> entitiesMap)
+    public void set(IOperationContext context, MapBatch<DataSetCreation, DataPE> batch)
     {
-        for (Map.Entry<DataSetCreation, DataPE> entry : entitiesMap.entrySet())
+        for (Map.Entry<DataSetCreation, DataPE> entry : batch.getObjects().entrySet())
         {
             DataSetCreation creation = entry.getKey();
             PhysicalDataCreation physicalCreation = creation.getPhysicalData();
@@ -71,9 +72,9 @@ public class SetDataSetPhysicalDataExecutor implements ISetDataSetPhysicalDataEx
             }
         }
 
-        setDataSetStorageFormatExecutor.set(context, entitiesMap);
-        setDataSetFileFormatTypeExecutor.set(context, entitiesMap);
-        setDataSetLocatorTypeExecutor.set(context, entitiesMap);
+        setDataSetStorageFormatExecutor.set(context, batch);
+        setDataSetFileFormatTypeExecutor.set(context, batch);
+        setDataSetLocatorTypeExecutor.set(context, batch);
     }
 
     private void set(IOperationContext context, PhysicalDataCreation physicalCreation, ExternalDataPE dataSet)
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetRelatedDataSetsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetRelatedDataSetsExecutor.java
index 6f4d75d052dd98256e9a63dbcf6f7a8e9e8a5a33..0b241fdfade43a9c2e780f534af12853fd1d0fe0 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetRelatedDataSetsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetRelatedDataSetsExecutor.java
@@ -28,6 +28,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.IDataSetId;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessException;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractSetEntityMultipleRelationsExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.server.authorization.validator.DataSetPEByExperimentOrSampleIdentifierValidator;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 
@@ -86,12 +87,12 @@ public class SetDataSetRelatedDataSetsExecutor extends AbstractSetEntityMultiple
     }
 
     @Override
-    protected void set(IOperationContext context, Map<DataSetCreation, DataPE> creationsMap, Map<IDataSetId, DataPE> relatedMap)
+    protected void set(IOperationContext context, MapBatch<DataSetCreation, DataPE> batch, Map<IDataSetId, DataPE> relatedMap)
     {
-        setDataSetContainerExecutor.set(context, creationsMap, relatedMap);
-        setDataSetComponentsExecutor.set(context, creationsMap, relatedMap);
-        setDataSetParentsExecutor.set(context, creationsMap, relatedMap);
-        setDataSetChildrenExecutor.set(context, creationsMap, relatedMap);
+        setDataSetContainerExecutor.set(context, batch, relatedMap);
+        setDataSetComponentsExecutor.set(context, batch, relatedMap);
+        setDataSetParentsExecutor.set(context, batch, relatedMap);
+        setDataSetChildrenExecutor.set(context, batch, relatedMap);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetSampleExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetSampleExecutor.java
index 55041d9289cc62eae0c5098e66ae9a30011c1432..02a40e498bf1e67d2968e44a2fd372f60bafa1a6 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetSampleExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetSampleExecutor.java
@@ -42,6 +42,12 @@ public class SetDataSetSampleExecutor extends AbstractSetEntitySampleRelationExe
     @Autowired
     private IDAOFactory daoFactory;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-sample";
+    }
+
     @Override
     protected ISampleId getRelatedId(DataSetCreation creation)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetStorageFormatExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetStorageFormatExecutor.java
index f1a9b0b4896d066777b9a12759f09deea1ce239f..1bfccdc2d0b42b726d1651f958c33e700a390fb8 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetStorageFormatExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/SetDataSetStorageFormatExecutor.java
@@ -43,6 +43,12 @@ public class SetDataSetStorageFormatExecutor extends
     @Autowired
     private IMapStorageFormatByIdExecutor mapStorageFormatByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-storageformat";
+    }
+
     @Override
     protected IStorageFormatId getRelatedId(DataSetCreation creation)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetChildrenExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetChildrenExecutor.java
index 4ef08d22164b8ff6e2f745b8be959db8a3dc8c86..d91e928d31e6139f1deff1054e7720d5746fb608 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetChildrenExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetChildrenExecutor.java
@@ -37,6 +37,12 @@ public class UpdateDataSetChildrenExecutor extends AbstractUpdateEntityToManyRel
         implements IUpdateDataSetChildrenExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-children";
+    }
+
     @Override
     protected Collection<DataPE> getCurrentlyRelated(DataPE entity)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetComponentsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetComponentsExecutor.java
index dfdf2eabbd6775eb9cbbe1ee2a56b54fb88ea3bd..b5473a78367ec71d1e8f4544e0664cfebe7fcf3f 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetComponentsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetComponentsExecutor.java
@@ -38,6 +38,12 @@ public class UpdateDataSetComponentsExecutor extends AbstractUpdateEntityToManyR
         implements IUpdateDataSetComponentsExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-components";
+    }
+
     @Override
     protected Collection<DataPE> getCurrentlyRelated(DataPE entity)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetContainersExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetContainersExecutor.java
index 951e10e746be70332cd63aa79c370f093abcba74..c3505e133a4f298811233327c3b95b0ad9330c61 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetContainersExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetContainersExecutor.java
@@ -38,6 +38,12 @@ public class UpdateDataSetContainersExecutor extends AbstractUpdateEntityToManyR
         implements IUpdateDataSetContainersExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-containers";
+    }
+
     @Override
     protected Collection<DataPE> getCurrentlyRelated(DataPE entity)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetExecutor.java
index b3708903a134b60de5e8822142efcdba99733c43..e22f512c8b6c65e99c38e9caf9b61843b7a204d0 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetExecutor.java
@@ -32,6 +32,8 @@ import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessE
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractUpdateEntityExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.tag.IUpdateTagForEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.authorization.validator.DataSetPEByExperimentOrSampleIdentifierValidator;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.DataAccessExceptionTranslator;
@@ -104,23 +106,23 @@ public class UpdateDataSetExecutor extends AbstractUpdateEntityExecutor<DataSetU
     }
 
     @Override
-    protected void checkBusinessRules(IOperationContext context, Collection<DataPE> entities)
+    protected void checkBusinessRules(IOperationContext context, CollectionBatch<DataPE> batch)
     {
-        verifyDataSetExecutor.verify(context, entities);
+        verifyDataSetExecutor.verify(context, batch);
     }
 
     @Override
-    protected void updateBatch(IOperationContext context, Map<DataSetUpdate, DataPE> entitiesMap)
+    protected void updateBatch(IOperationContext context, MapBatch<DataSetUpdate, DataPE> batch)
     {
-        updateDataSetPhysicalDataExecutor.update(context, entitiesMap);
-        updateDataSetLinkedDataExecutor.update(context, entitiesMap);
-        updateDataSetExperimentExecutor.update(context, entitiesMap);
-        updateDataSetSampleExecutor.update(context, entitiesMap);
+        updateDataSetPhysicalDataExecutor.update(context, batch);
+        updateDataSetLinkedDataExecutor.update(context, batch);
+        updateDataSetExperimentExecutor.update(context, batch);
+        updateDataSetSampleExecutor.update(context, batch);
 
         PersonPE person = context.getSession().tryGetPerson();
         Date timeStamp = daoFactory.getTransactionTimestamp();
         Map<IEntityPropertiesHolder, Map<String, String>> propertyMap = new HashMap<IEntityPropertiesHolder, Map<String, String>>();
-        for (Map.Entry<DataSetUpdate, DataPE> entry : entitiesMap.entrySet())
+        for (Map.Entry<DataSetUpdate, DataPE> entry : batch.getObjects().entrySet())
         {
             DataSetUpdate update = entry.getKey();
             DataPE entity = entry.getValue();
@@ -141,9 +143,9 @@ public class UpdateDataSetExecutor extends AbstractUpdateEntityExecutor<DataSetU
     }
 
     @Override
-    protected void updateAll(IOperationContext context, Map<DataSetUpdate, DataPE> entitiesMap)
+    protected void updateAll(IOperationContext context, MapBatch<DataSetUpdate, DataPE> batch)
     {
-        updateDataSetRelatedDataSetsExecutor.update(context, entitiesMap);
+        updateDataSetRelatedDataSetsExecutor.update(context, batch);
     }
 
     @Override
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetExperimentExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetExperimentExecutor.java
index abe907bc71a1fc7a4b2e9df45f9ce2436a4ac664..4f48e7811e84ef7f7538b70789c917ea84109c7a 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetExperimentExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetExperimentExecutor.java
@@ -43,6 +43,12 @@ public class UpdateDataSetExperimentExecutor extends
         implements IUpdateDataSetExperimentExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-experiment";
+    }
+
     @Autowired
     private IMapExperimentByIdExecutor mapExperimentByIdExecutor;
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetExternalDmsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetExternalDmsExecutor.java
index 2c759d8a40714dd90f7f19a657852af32d9c38ae..712c990f53139475b2e1bdb68663a8e81793657e 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetExternalDmsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetExternalDmsExecutor.java
@@ -46,6 +46,12 @@ public class UpdateDataSetExternalDmsExecutor extends
     @Autowired
     private IMapExternalDmsByIdExecutor mapExternalDmsByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-externaldms";
+    }
+
     @Override
     protected IExternalDmsId getRelatedId(ExternalDataManagementSystemPE related)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetFileFormatTypeExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetFileFormatTypeExecutor.java
index af9c660e025734e7fdef77cadc8ffcd7d0fa830d..f587d90fe4f4cba35ebd2a7ee611fbc54381b3a3 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetFileFormatTypeExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetFileFormatTypeExecutor.java
@@ -45,6 +45,12 @@ public class UpdateDataSetFileFormatTypeExecutor extends
     @Autowired
     private IMapFileFormatTypeByIdExecutor mapFileFormatTypeByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-fileformattype";
+    }
+
     @Override
     protected IFileFormatTypeId getRelatedId(FileFormatTypePE related)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetLinkedDataExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetLinkedDataExecutor.java
index 54c275d4856258e1bbd45cae964d1caa230908a6..a7c54b0fbddd70360a7eaf2153dd8368c3febcef 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetLinkedDataExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetLinkedDataExecutor.java
@@ -24,6 +24,7 @@ import org.springframework.stereotype.Component;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.update.DataSetUpdate;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.update.LinkedDataUpdate;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.LinkDataPE;
 
@@ -38,9 +39,9 @@ public class UpdateDataSetLinkedDataExecutor implements IUpdateDataSetLinkedData
     private IUpdateDataSetExternalDmsExecutor updateDataSetExternalDmsExecutor;
 
     @Override
-    public void update(IOperationContext context, Map<DataSetUpdate, DataPE> entitiesMap)
+    public void update(IOperationContext context, MapBatch<DataSetUpdate, DataPE> batch)
     {
-        for (Map.Entry<DataSetUpdate, DataPE> entry : entitiesMap.entrySet())
+        for (Map.Entry<DataSetUpdate, DataPE> entry : batch.getObjects().entrySet())
         {
             DataSetUpdate update = entry.getKey();
             DataPE entity = entry.getValue();
@@ -51,7 +52,7 @@ public class UpdateDataSetLinkedDataExecutor implements IUpdateDataSetLinkedData
             }
         }
 
-        updateDataSetExternalDmsExecutor.update(context, entitiesMap);
+        updateDataSetExternalDmsExecutor.update(context, batch);
     }
 
     private void update(IOperationContext context, LinkedDataUpdate update, LinkDataPE entity)
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetParentsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetParentsExecutor.java
index c2c241393c860a00ee5a9c264211f820315f3e7a..321b1a830ef54d801279b466908ba3c8c06d2c23 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetParentsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetParentsExecutor.java
@@ -37,6 +37,12 @@ public class UpdateDataSetParentsExecutor extends AbstractUpdateEntityToManyRela
         implements IUpdateDataSetParentsExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-parents";
+    }
+
     @Override
     protected Collection<DataPE> getCurrentlyRelated(DataPE entity)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetPhysicalDataExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetPhysicalDataExecutor.java
index dc375708ee724b31a50977299538f7b119b6514b..a6571d981a2dde25143a463348d71ad379d96d28 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetPhysicalDataExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetPhysicalDataExecutor.java
@@ -16,13 +16,12 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.dataset;
 
-import java.util.Map;
-
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.update.DataSetUpdate;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 
 /**
@@ -36,9 +35,9 @@ public class UpdateDataSetPhysicalDataExecutor implements IUpdateDataSetPhysical
     private IUpdateDataSetFileFormatTypeExecutor updateDataSetFileFormatTypeExecutor;
 
     @Override
-    public void update(IOperationContext context, Map<DataSetUpdate, DataPE> entitiesMap)
+    public void update(IOperationContext context, MapBatch<DataSetUpdate, DataPE> batch)
     {
-        updateDataSetFileFormatTypeExecutor.update(context, entitiesMap);
+        updateDataSetFileFormatTypeExecutor.update(context, batch);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetRelatedDataSetsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetRelatedDataSetsExecutor.java
index 753a1276f496f2a354d9e5f9b7fb898d73c1ec91..4a55775efddd724a174b997837ffad924a80cf5b 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetRelatedDataSetsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetRelatedDataSetsExecutor.java
@@ -27,6 +27,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.IDataSetId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.update.DataSetUpdate;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractUpdateEntityMultipleRelationsExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 
 /**
@@ -68,12 +69,12 @@ public class UpdateDataSetRelatedDataSetsExecutor extends AbstractUpdateEntityMu
     }
 
     @Override
-    protected void update(IOperationContext context, Map<DataSetUpdate, DataPE> entitiesMap, Map<IDataSetId, DataPE> relatedMap)
+    protected void update(IOperationContext context, MapBatch<DataSetUpdate, DataPE> batch, Map<IDataSetId, DataPE> relatedMap)
     {
-        updateDataSetContainersExecutor.update(context, entitiesMap, relatedMap);
-        updateDataSetComponentsExecutor.update(context, entitiesMap, relatedMap);
-        updateDataSetParentsExecutor.update(context, entitiesMap, relatedMap);
-        updateDataSetChildrenExecutor.update(context, entitiesMap, relatedMap);
+        updateDataSetContainersExecutor.update(context, batch, relatedMap);
+        updateDataSetComponentsExecutor.update(context, batch, relatedMap);
+        updateDataSetParentsExecutor.update(context, batch, relatedMap);
+        updateDataSetChildrenExecutor.update(context, batch, relatedMap);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetSampleExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetSampleExecutor.java
index 4a0a33040bce63fda73ecad28d20b598a4d27f79..9f231b766414554d0eb2bffdb40bcf979b1a8367 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetSampleExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/UpdateDataSetSampleExecutor.java
@@ -46,6 +46,12 @@ public class UpdateDataSetSampleExecutor extends AbstractUpdateEntityToOneRelati
     @Autowired
     private IMapSampleByIdExecutor mapSampleByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "dataset-sample";
+    }
+
     @Override
     protected ISampleId getRelatedId(SamplePE related)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/VerifyDataSetExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/VerifyDataSetExecutor.java
index b6d6735886d99f6c9f44e2ded626b1d68c59ac39..447c20e6b5cfdd0315a55ff8c8b362c5d701c04f 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/VerifyDataSetExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/VerifyDataSetExecutor.java
@@ -16,13 +16,12 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.dataset;
 
-import java.util.Collection;
-
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property.IVerifyEntityPropertyExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 
 /**
@@ -45,7 +44,7 @@ public class VerifyDataSetExecutor implements IVerifyDataSetExecutor
     private IVerifyDataSetParentsExecutor verifyDataSetParentsExecutor;
 
     @Override
-    public void verify(IOperationContext context, Collection<DataPE> dataSets)
+    public void verify(IOperationContext context, CollectionBatch<DataPE> dataSets)
     {
         verifyDataSetSampleAndExperimentExecutor.verify(context, dataSets);
         verifyEntityPropertyExecutor.verify(context, dataSets);
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/VerifyDataSetSampleAndExperimentExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/VerifyDataSetSampleAndExperimentExecutor.java
index 57188014e991bbfabeda1f842f17bf6a9721cd91..05d8626ac4f4f44280a670304ab6dcfff3e31ffe 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/VerifyDataSetSampleAndExperimentExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/dataset/VerifyDataSetSampleAndExperimentExecutor.java
@@ -16,7 +16,6 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.dataset;
 
-import java.util.Collection;
 import java.util.Properties;
 
 import javax.annotation.PostConstruct;
@@ -25,6 +24,7 @@ import javax.annotation.Resource;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.common.spring.ExposablePropertyPlaceholderConfigurer;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.util.DataSetTypeWithoutExperimentChecker;
@@ -51,9 +51,9 @@ public class VerifyDataSetSampleAndExperimentExecutor implements IVerifyDataSetS
     }
 
     @Override
-    public void verify(IOperationContext context, Collection<DataPE> dataSets)
+    public void verify(IOperationContext context, CollectionBatch<DataPE> batch)
     {
-        for (DataPE dataSet : dataSets)
+        for (DataPE dataSet : batch.getObjects())
         {
             verify(context, dataSet);
         }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractCreateEntityExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractCreateEntityExecutor.java
index 18a8e5e475d2720538c313014c11dd33ae7e6ca9..52b5c990afb1c356132fa2b59aacdf1dc11748db 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractCreateEntityExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractCreateEntityExecutor.java
@@ -29,15 +29,23 @@ import java.util.Map;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.dao.DataAccessException;
 
-import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.IObjectId;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.Batch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.CheckAccessProgress;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.CheckDataProgress;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IIdHolder;
 
 /**
  * @author pkupczyk
  */
-public abstract class AbstractCreateEntityExecutor<CREATION, PE extends IIdHolder, PERM_ID> implements
+public abstract class AbstractCreateEntityExecutor<CREATION, PE extends IIdHolder, PERM_ID extends IObjectId> implements
         ICreateEntityExecutor<CREATION, PERM_ID>
 {
 
@@ -47,31 +55,25 @@ public abstract class AbstractCreateEntityExecutor<CREATION, PE extends IIdHolde
     @Override
     public List<PERM_ID> create(IOperationContext context, List<CREATION> creations)
     {
-
         try
         {
             List<PERM_ID> permIdsAll = new LinkedList<PERM_ID>();
             Map<CREATION, PE> entitiesAll = new LinkedHashMap<CREATION, PE>();
 
-            int batchSize = 1000;
-            for (int batchStart = 0; batchStart < creations.size(); batchStart += batchSize)
+            for (CollectionBatch<CREATION> batch : Batch.createBatches(creations))
             {
-                int batchEnd = Math.min(batchStart + batchSize, creations.size());
-                List<CREATION> creationsBatch = creations.subList(batchStart, batchEnd);
-                context.pushProgress(new Progress("creating entities", batchEnd, creations.size()));
-                createEntities(context, creationsBatch, permIdsAll, entitiesAll);
-                context.popProgress();
+                createEntities(context, batch, permIdsAll, entitiesAll);
             }
 
             daoFactory.getSessionFactory().getCurrentSession().flush();
             reloadEntities(context, entitiesAll);
 
-            updateAll(context, entitiesAll);
+            updateAll(context, new MapBatch<CREATION, PE>(0, 0, entitiesAll.size(), entitiesAll, entitiesAll.size()));
 
             daoFactory.getSessionFactory().getCurrentSession().flush();
             reloadEntities(context, entitiesAll);
 
-            checkBusinessRules(context, entitiesAll.values());
+            checkBusinessRules(context, new CollectionBatch<PE>(0, 0, entitiesAll.size(), entitiesAll.values(), entitiesAll.size()));
 
             return permIdsAll;
         } catch (DataAccessException e)
@@ -81,20 +83,53 @@ public abstract class AbstractCreateEntityExecutor<CREATION, PE extends IIdHolde
         }
     }
 
-    private void createEntities(IOperationContext context, List<CREATION> creationsBatch,
+    private void checkData(final IOperationContext context, CollectionBatch<CREATION> batch)
+    {
+        new CollectionBatchProcessor<CREATION>(context, batch)
+            {
+                @Override
+                public void process(CREATION object)
+                {
+                    checkData(context, object);
+                }
+
+                @Override
+                public IProgress createProgress(CREATION object, int objectIndex, int totalObjectCount)
+                {
+                    return new CheckDataProgress(object, objectIndex, totalObjectCount);
+                }
+            };
+    }
+
+    private void checkAccess(final IOperationContext context, MapBatch<CREATION, PE> batch)
+    {
+        new MapBatchProcessor<CREATION, PE>(context, batch)
+            {
+                @Override
+                public void process(CREATION key, PE value)
+                {
+                    checkAccess(context, value);
+                }
+
+                @Override
+                public IProgress createProgress(CREATION key, PE value, int objectIndex, int totalObjectCount)
+                {
+                    return new CheckAccessProgress(key, objectIndex, totalObjectCount);
+                }
+            };
+    }
+
+    private void createEntities(final IOperationContext context, CollectionBatch<CREATION> batch,
             List<PERM_ID> permIdsAll, Map<CREATION, PE> entitiesAll)
     {
-        Map<CREATION, PE> batchMap = new LinkedHashMap<CREATION, PE>();
+        Map<CREATION, PE> creationToEntityMap = new LinkedHashMap<CREATION, PE>();
 
         daoFactory.setBatchUpdateMode(true);
 
-        for (CREATION creation : creationsBatch)
-        {
-            checkData(context, creation);
-        }
+        checkData(context, batch);
 
-        List<PE> entities = createEntities(context, creationsBatch);
-        Iterator<CREATION> iterCreations = creationsBatch.iterator();
+        List<PE> entities = createEntities(context, batch);
+        Iterator<CREATION> iterCreations = batch.getObjects().iterator();
         Iterator<PE> iterEntities = entities.iterator();
 
         while (iterCreations.hasNext() && iterEntities.hasNext())
@@ -102,19 +137,18 @@ public abstract class AbstractCreateEntityExecutor<CREATION, PE extends IIdHolde
             CREATION creation = iterCreations.next();
             PE entity = iterEntities.next();
             entitiesAll.put(creation, entity);
-            batchMap.put(creation, entity);
+            creationToEntityMap.put(creation, entity);
         }
 
-        updateBatch(context, batchMap);
+        MapBatch<CREATION, PE> mapBatch = new MapBatch<CREATION, PE>(batch.getBatchIndex(), batch.getFromObjectIndex(), batch.getToObjectIndex(),
+                creationToEntityMap, batch.getTotalObjectCount());
 
-        for (PE entity : batchMap.values())
-        {
-            checkAccess(context, entity);
-        }
+        updateBatch(context, mapBatch);
+        checkAccess(context, mapBatch);
 
-        save(context, new ArrayList<PE>(batchMap.values()), false);
+        save(context, new ArrayList<PE>(creationToEntityMap.values()), false);
 
-        for (PE entity : batchMap.values())
+        for (PE entity : creationToEntityMap.values())
         {
             PERM_ID permId = createPermId(context, entity);
             permIdsAll.add(permId);
@@ -149,17 +183,17 @@ public abstract class AbstractCreateEntityExecutor<CREATION, PE extends IIdHolde
 
     protected abstract void checkData(IOperationContext context, CREATION creation);
 
-    protected abstract List<PE> createEntities(IOperationContext context, Collection<CREATION> creations);
+    protected abstract void checkAccess(IOperationContext context, PE entity);
 
-    protected abstract PERM_ID createPermId(IOperationContext context, PE entity);
+    protected abstract void checkBusinessRules(IOperationContext context, CollectionBatch<PE> batch);
 
-    protected abstract void checkAccess(IOperationContext context, PE entity);
+    protected abstract List<PE> createEntities(IOperationContext context, CollectionBatch<CREATION> batch);
 
-    protected abstract void checkBusinessRules(IOperationContext context, Collection<PE> entities);
+    protected abstract PERM_ID createPermId(IOperationContext context, PE entity);
 
-    protected abstract void updateBatch(IOperationContext context, Map<CREATION, PE> entitiesMap);
+    protected abstract void updateBatch(IOperationContext context, MapBatch<CREATION, PE> batch);
 
-    protected abstract void updateAll(IOperationContext context, Map<CREATION, PE> entitiesMap);
+    protected abstract void updateAll(IOperationContext context, MapBatch<CREATION, PE> batch);
 
     protected abstract List<PE> list(IOperationContext context, Collection<Long> ids);
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractSetEntityMultipleRelationsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractSetEntityMultipleRelationsExecutor.java
index 64c9b63864751fefab2f8ca11d81ba9e290229b3..551945e33e56595c25a7ec2b0b87abc74bce05a4 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractSetEntityMultipleRelationsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractSetEntityMultipleRelationsExecutor.java
@@ -30,8 +30,8 @@ import org.springframework.stereotype.Component;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.CreationId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.IObjectId;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.ObjectNotFoundException;
-import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 
 /**
  * @author pkupczyk
@@ -42,17 +42,15 @@ public abstract class AbstractSetEntityMultipleRelationsExecutor<ENTITY_CREATION
 {
 
     @Override
-    public void set(IOperationContext context, Map<ENTITY_CREATION, ENTITY_PE> creationsMap)
+    public void set(IOperationContext context, MapBatch<ENTITY_CREATION, ENTITY_PE> batch)
     {
-        Map<RELATED_ID, RELATED_PE> relatedMap = getRelatedMap(context, creationsMap);
+        Map<RELATED_ID, RELATED_PE> relatedMap = getRelatedMap(context, batch.getObjects());
 
-        set(context, creationsMap, relatedMap);
+        set(context, batch, relatedMap);
     }
 
     private Map<RELATED_ID, RELATED_PE> getRelatedMap(IOperationContext context, Map<ENTITY_CREATION, ENTITY_PE> creationsMap)
     {
-        context.pushProgress(new Progress("load related entities"));
-
         Set<RELATED_ID> relatedIds = new HashSet<RELATED_ID>();
         for (Entry<ENTITY_CREATION, ENTITY_PE> entry : creationsMap.entrySet())
         {
@@ -100,8 +98,6 @@ public abstract class AbstractSetEntityMultipleRelationsExecutor<ENTITY_CREATION
             check(context, relatedId, related);
         }
 
-        context.popProgress();
-
         return relatedMap;
     }
 
@@ -137,6 +133,6 @@ public abstract class AbstractSetEntityMultipleRelationsExecutor<ENTITY_CREATION
 
     protected abstract void check(IOperationContext context, RELATED_ID relatedId, RELATED_PE related);
 
-    protected abstract void set(IOperationContext context, Map<ENTITY_CREATION, ENTITY_PE> creationsMap, Map<RELATED_ID, RELATED_PE> relatedMap);
+    protected abstract void set(IOperationContext context, MapBatch<ENTITY_CREATION, ENTITY_PE> batch, Map<RELATED_ID, RELATED_PE> relatedMap);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractSetEntityToManyRelationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractSetEntityToManyRelationExecutor.java
index 6a93ae57149b842e55a6b60a5a733281afb45918..d4485e7c330f2f85d70df76bb808ca651f9e5720 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractSetEntityToManyRelationExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractSetEntityToManyRelationExecutor.java
@@ -23,44 +23,58 @@ import java.util.Map;
 
 import javax.annotation.Resource;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.IObjectId;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.SetEntityRelationProgress;
 import ch.systemsx.cisd.openbis.generic.server.ComponentNames;
 import ch.systemsx.cisd.openbis.generic.server.business.IRelationshipService;
 
 /**
  * @author pkupczyk
  */
-public abstract class AbstractSetEntityToManyRelationExecutor<ENTITY_CREATION, ENTITY_PE, RELATED_ID, RELATED_PE>
+public abstract class AbstractSetEntityToManyRelationExecutor<ENTITY_CREATION, ENTITY_PE, RELATED_ID extends IObjectId, RELATED_PE>
 {
 
     @Resource(name = ComponentNames.RELATIONSHIP_SERVICE)
     protected IRelationshipService relationshipService;
 
-    public void set(IOperationContext context, Map<ENTITY_CREATION, ENTITY_PE> creationsMap, Map<RELATED_ID, RELATED_PE> relatedMap)
+    public void set(final IOperationContext context, final MapBatch<ENTITY_CREATION, ENTITY_PE> batch, final Map<RELATED_ID, RELATED_PE> relatedMap)
     {
-        Collection<RELATED_PE> allSet = new HashSet<RELATED_PE>();
+        final Collection<RELATED_PE> allSet = new HashSet<RELATED_PE>();
 
-        for (ENTITY_CREATION creation : creationsMap.keySet())
-        {
-            ENTITY_PE entity = creationsMap.get(creation);
-            Collection<? extends RELATED_ID> relatedIds = getRelatedIds(context, creation);
-
-            if (relatedIds != null)
+        new MapBatchProcessor<ENTITY_CREATION, ENTITY_PE>(context, batch)
             {
-                Collection<RELATED_PE> related = new LinkedList<RELATED_PE>();
-
-                for (RELATED_ID relatedId : relatedIds)
+                @Override
+                public void process(ENTITY_CREATION creation, ENTITY_PE entity)
                 {
-                    related.add(relatedMap.get(relatedId));
+                    Collection<? extends RELATED_ID> relatedIds = getRelatedIds(context, creation);
+
+                    if (relatedIds != null)
+                    {
+                        Collection<RELATED_PE> related = new LinkedList<RELATED_PE>();
+
+                        for (RELATED_ID relatedId : relatedIds)
+                        {
+                            related.add(relatedMap.get(relatedId));
+                        }
+
+                        if (false == related.isEmpty())
+                        {
+                            setRelated(context, entity, related);
+                            allSet.addAll(related);
+                        }
+                    }
                 }
 
-                if (false == related.isEmpty())
+                @Override
+                public IProgress createProgress(ENTITY_CREATION key, ENTITY_PE value, int objectIndex, int totalObjectCount)
                 {
-                    setRelated(context, entity, related);
-                    allSet.addAll(related);
+                    return new SetEntityRelationProgress(key, getRelationName(), objectIndex, totalObjectCount);
                 }
-            }
-        }
+            };
 
         postSet(context, allSet);
     }
@@ -70,6 +84,8 @@ public abstract class AbstractSetEntityToManyRelationExecutor<ENTITY_CREATION, E
         // by default do nothing
     }
 
+    protected abstract String getRelationName();
+
     protected abstract Collection<? extends RELATED_ID> getRelatedIds(IOperationContext context, ENTITY_CREATION creation);
 
     protected abstract void setRelated(IOperationContext context, ENTITY_PE entity, Collection<RELATED_PE> related);
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractSetEntityToOneRelationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractSetEntityToOneRelationExecutor.java
index 15a92c9055135828e4a18727e1e9e6929ecadf6b..51e67eeeaca75cfaa97a17468c18502079217015 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractSetEntityToOneRelationExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractSetEntityToOneRelationExecutor.java
@@ -24,7 +24,11 @@ import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.IObjectId;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.ObjectNotFoundException;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.SetEntityRelationProgress;
 
 /**
  * @author pkupczyk
@@ -35,11 +39,11 @@ public abstract class AbstractSetEntityToOneRelationExecutor<ENTITY_CREATION, EN
 {
 
     @Override
-    public void set(IOperationContext context, Map<ENTITY_CREATION, ENTITY_PE> entitiesMap)
+    public void set(final IOperationContext context, MapBatch<ENTITY_CREATION, ENTITY_PE> batch)
     {
         List<RELATED_ID> relatedIds = new LinkedList<RELATED_ID>();
 
-        for (ENTITY_CREATION creation : entitiesMap.keySet())
+        for (ENTITY_CREATION creation : batch.getObjects().keySet())
         {
             RELATED_ID relatedId = getRelatedId(creation);
 
@@ -49,33 +53,44 @@ public abstract class AbstractSetEntityToOneRelationExecutor<ENTITY_CREATION, EN
             }
         }
 
-        Map<RELATED_ID, RELATED_PE> relatedMap = map(context, relatedIds);
+        final Map<RELATED_ID, RELATED_PE> relatedMap = map(context, relatedIds);
 
-        for (Map.Entry<ENTITY_CREATION, ENTITY_PE> entry : entitiesMap.entrySet())
-        {
-            ENTITY_CREATION creation = entry.getKey();
-            ENTITY_PE entity = entry.getValue();
-            RELATED_ID relatedId = getRelatedId(creation);
-
-            if (relatedId == null)
+        new MapBatchProcessor<ENTITY_CREATION, ENTITY_PE>(context, batch)
             {
-                check(context, entity, null, null);
-                set(context, entity, null);
-            } else
-            {
-                RELATED_PE related = relatedMap.get(relatedId);
+                @Override
+                public void process(ENTITY_CREATION creation, ENTITY_PE entity)
+                {
+                    RELATED_ID relatedId = getRelatedId(creation);
+
+                    if (relatedId == null)
+                    {
+                        check(context, entity, null, null);
+                        set(context, entity, null);
+                    } else
+                    {
+                        RELATED_PE related = relatedMap.get(relatedId);
+
+                        if (related == null)
+                        {
+                            throw new ObjectNotFoundException((IObjectId) relatedId);
+                        }
+
+                        check(context, entity, relatedId, related);
+                        set(context, entity, related);
+                    }
+                }
 
-                if (related == null)
+                @Override
+                public IProgress createProgress(ENTITY_CREATION key, ENTITY_PE value, int objectIndex, int totalObjectCount)
                 {
-                    throw new ObjectNotFoundException((IObjectId) relatedId);
+                    return new SetEntityRelationProgress(key, getRelationName(), objectIndex, totalObjectCount);
                 }
 
-                check(context, entity, relatedId, related);
-                set(context, entity, related);
-            }
-        }
+            };
     }
 
+    protected abstract String getRelationName();
+
     protected abstract RELATED_ID getRelatedId(ENTITY_CREATION creation);
 
     protected abstract Map<RELATED_ID, RELATED_PE> map(IOperationContext context, List<RELATED_ID> relatedIds);
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityExecutor.java
index fa3d73f5fdd893706017046280690c880cd2295d..a0a08fdaa8dbf6e0b715c527881be31afe084f46 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityExecutor.java
@@ -31,14 +31,22 @@ import org.springframework.dao.DataAccessException;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.IObjectId;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.ObjectNotFoundException;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.Batch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.CheckAccessProgress;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.CheckDataProgress;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IIdHolder;
 
 /**
  * @author pkupczyk
  */
-public abstract class AbstractUpdateEntityExecutor<UPDATE, PE extends IIdHolder, ID> implements IUpdateEntityExecutor<UPDATE>
+public abstract class AbstractUpdateEntityExecutor<UPDATE, PE extends IIdHolder, ID extends IObjectId> implements IUpdateEntityExecutor<UPDATE>
 {
 
     @Autowired
@@ -51,20 +59,18 @@ public abstract class AbstractUpdateEntityExecutor<UPDATE, PE extends IIdHolder,
         {
             Map<UPDATE, PE> entitiesAll = new LinkedHashMap<UPDATE, PE>();
 
-            int batchSize = 1000;
-            for (int batchStart = 0; batchStart < updates.size(); batchStart += batchSize)
+            for (CollectionBatch<UPDATE> batch : Batch.createBatches(updates))
             {
-                List<UPDATE> updatesBatch = updates.subList(batchStart, Math.min(batchStart + batchSize, updates.size()));
-                updateEntities(context, updatesBatch, entitiesAll);
+                updateEntities(context, batch, entitiesAll);
             }
 
             reloadEntities(context, entitiesAll);
 
-            updateAll(context, entitiesAll);
+            updateAll(context, new MapBatch<UPDATE, PE>(0, 0, entitiesAll.size(), entitiesAll, entitiesAll.size()));
 
             reloadEntities(context, entitiesAll);
 
-            checkBusinessRules(context, entitiesAll.values());
+            checkBusinessRules(context, new CollectionBatch<PE>(0, 0, entitiesAll.size(), entitiesAll.values(), entitiesAll.size()));
 
         } catch (DataAccessException e)
         {
@@ -72,15 +78,45 @@ public abstract class AbstractUpdateEntityExecutor<UPDATE, PE extends IIdHolder,
         }
     }
 
-    private Map<UPDATE, PE> getEntitiesMap(IOperationContext context, List<UPDATE> updates)
+    private void checkData(final IOperationContext context, CollectionBatch<UPDATE> batch)
     {
+        new CollectionBatchProcessor<UPDATE>(context, batch)
+            {
+                @Override
+                public void process(UPDATE object)
+                {
+                    checkData(context, object);
+                }
 
-        for (UPDATE update : updates)
-        {
-            checkData(context, update);
-        }
+                @Override
+                public IProgress createProgress(UPDATE object, int objectIndex, int totalObjectCount)
+                {
+                    return new CheckDataProgress(object, objectIndex, totalObjectCount);
+                }
+            };
+    }
 
-        Collection<ID> entityIds = CollectionUtils.collect(updates, new Transformer<UPDATE, ID>()
+    private void checkAccess(final IOperationContext context, MapBatch<UPDATE, PE> batch)
+    {
+        new MapBatchProcessor<UPDATE, PE>(context, batch)
+            {
+                @Override
+                public void process(UPDATE key, PE value)
+                {
+                    checkAccess(context, getId(key), value);
+                }
+
+                @Override
+                public IProgress createProgress(UPDATE key, PE value, int objectIndex, int totalObjectCount)
+                {
+                    return new CheckAccessProgress(key, objectIndex, totalObjectCount);
+                }
+            };
+    }
+
+    private Map<UPDATE, PE> getEntitiesMap(IOperationContext context, CollectionBatch<UPDATE> batch)
+    {
+        Collection<ID> entityIds = CollectionUtils.collect(batch.getObjects(), new Transformer<UPDATE, ID>()
             {
                 @Override
                 public ID transform(UPDATE update)
@@ -97,15 +133,13 @@ public abstract class AbstractUpdateEntityExecutor<UPDATE, PE extends IIdHolder,
 
             if (entity == null)
             {
-                throw new ObjectNotFoundException((IObjectId) entityId);
+                throw new ObjectNotFoundException(entityId);
             }
-
-            checkAccess(context, entityId, entity);
         }
 
         Map<UPDATE, PE> result = new HashMap<UPDATE, PE>();
 
-        for (UPDATE update : updates)
+        for (UPDATE update : batch.getObjects())
         {
             ID id = getId(update);
             result.put(update, entityMap.get(id));
@@ -114,16 +148,22 @@ public abstract class AbstractUpdateEntityExecutor<UPDATE, PE extends IIdHolder,
         return result;
     }
 
-    private void updateEntities(IOperationContext context, List<UPDATE> updatesBatch, Map<UPDATE, PE> entitiesAll)
+    private void updateEntities(IOperationContext context, CollectionBatch<UPDATE> batch, Map<UPDATE, PE> entitiesAll)
     {
-        Map<UPDATE, PE> batchMap = getEntitiesMap(context, updatesBatch);
-        entitiesAll.putAll(batchMap);
+        checkData(context, batch);
+
+        Map<UPDATE, PE> updateToEntityMap = getEntitiesMap(context, batch);
+        entitiesAll.putAll(updateToEntityMap);
 
         daoFactory.setBatchUpdateMode(true);
 
-        updateBatch(context, batchMap);
+        MapBatch<UPDATE, PE> mapBatch = new MapBatch<UPDATE, PE>(batch.getBatchIndex(), batch.getFromObjectIndex(), batch.getToObjectIndex(),
+                updateToEntityMap, batch.getTotalObjectCount());
+
+        checkAccess(context, mapBatch);
+        updateBatch(context, mapBatch);
 
-        save(context, new ArrayList<PE>(batchMap.values()), false);
+        save(context, new ArrayList<PE>(updateToEntityMap.values()), false);
 
         daoFactory.setBatchUpdateMode(false);
     }
@@ -158,11 +198,11 @@ public abstract class AbstractUpdateEntityExecutor<UPDATE, PE extends IIdHolder,
 
     protected abstract void checkAccess(IOperationContext context, ID id, PE entity);
 
-    protected abstract void checkBusinessRules(IOperationContext context, Collection<PE> entities);
+    protected abstract void checkBusinessRules(IOperationContext context, CollectionBatch<PE> batch);
 
-    protected abstract void updateBatch(IOperationContext context, Map<UPDATE, PE> entitiesMap);
+    protected abstract void updateBatch(IOperationContext context, MapBatch<UPDATE, PE> batch);
 
-    protected abstract void updateAll(IOperationContext context, Map<UPDATE, PE> entitiesMap);
+    protected abstract void updateAll(IOperationContext context, MapBatch<UPDATE, PE> batch);
 
     protected abstract Map<ID, PE> map(IOperationContext context, Collection<ID> ids);
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityMultipleRelationsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityMultipleRelationsExecutor.java
index 98446b8055d3c9ad99e4ef9be36d497840387555..907263f12fd162d419788ee48e1b3d398c9f87cf 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityMultipleRelationsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityMultipleRelationsExecutor.java
@@ -27,6 +27,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.FieldUpdateValue;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.IdListUpdateValue;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.ListUpdateValue.ListUpdateAction;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 
 /**
  * @author pkupczyk
@@ -37,10 +38,10 @@ public abstract class AbstractUpdateEntityMultipleRelationsExecutor<ENTITY_UPDAT
 {
 
     @Override
-    public void update(IOperationContext context, Map<ENTITY_UPDATE, ENTITY_PE> entitiesMap)
+    public void update(IOperationContext context, MapBatch<ENTITY_UPDATE, ENTITY_PE> batch)
     {
-        Map<RELATED_ID, RELATED_PE> relatedMap = getRelatedMap(context, entitiesMap.keySet());
-        update(context, entitiesMap, relatedMap);
+        Map<RELATED_ID, RELATED_PE> relatedMap = getRelatedMap(context, batch.getObjects().keySet());
+        update(context, batch, relatedMap);
     }
 
     private Map<RELATED_ID, RELATED_PE> getRelatedMap(IOperationContext context, Collection<ENTITY_UPDATE> updates)
@@ -78,6 +79,6 @@ public abstract class AbstractUpdateEntityMultipleRelationsExecutor<ENTITY_UPDAT
 
     protected abstract Map<RELATED_ID, RELATED_PE> map(IOperationContext context, Collection<RELATED_ID> relatedIds);
 
-    protected abstract void update(IOperationContext context, Map<ENTITY_UPDATE, ENTITY_PE> entitiesMap, Map<RELATED_ID, RELATED_PE> relatedMap);
+    protected abstract void update(IOperationContext context, MapBatch<ENTITY_UPDATE, ENTITY_PE> batch, Map<RELATED_ID, RELATED_PE> relatedMap);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityToManyRelationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityToManyRelationExecutor.java
index 51437f0c79b1cfa285d0e8248e343b4f4b4963a4..7ba75e03112a18d878ed9286a46e62421c0241f2 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityToManyRelationExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityToManyRelationExecutor.java
@@ -31,7 +31,11 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.ListUpdateValue.Li
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.ListUpdateValue.ListUpdateActionRemove;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.ListUpdateValue.ListUpdateActionSet;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.ObjectNotFoundException;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.UpdateEntityRelationProgress;
 import ch.systemsx.cisd.openbis.generic.server.ComponentNames;
 import ch.systemsx.cisd.openbis.generic.server.business.IRelationshipService;
 
@@ -46,61 +50,68 @@ public abstract class AbstractUpdateEntityToManyRelationExecutor<ENTITY_UPDATE,
     protected IRelationshipService relationshipService;
 
     @Override
-    public void update(IOperationContext context, Map<ENTITY_UPDATE, ENTITY_PE> entitiesMap, Map<RELATED_ID, RELATED_PE> relatedMap)
+    public void update(final IOperationContext context, final MapBatch<ENTITY_UPDATE, ENTITY_PE> batch, final Map<RELATED_ID, RELATED_PE> relatedMap)
     {
-        Collection<RELATED_PE> allAdded = new HashSet<RELATED_PE>();
-        Collection<RELATED_PE> allRemoved = new HashSet<RELATED_PE>();
+        final Collection<RELATED_PE> allAdded = new HashSet<RELATED_PE>();
+        final Collection<RELATED_PE> allRemoved = new HashSet<RELATED_PE>();
 
-        for (ENTITY_UPDATE update : entitiesMap.keySet())
-        {
-            IdListUpdateValue<? extends RELATED_ID> listUpdate = getRelatedUpdate(context, update);
-
-            if (listUpdate != null && listUpdate.hasActions())
+        new MapBatchProcessor<ENTITY_UPDATE, ENTITY_PE>(context, batch)
             {
-                ENTITY_PE entity = entitiesMap.get(update);
-
-                for (ListUpdateAction<? extends RELATED_ID> action : listUpdate.getActions())
+                @Override
+                public void process(ENTITY_UPDATE update, ENTITY_PE entity)
                 {
-                    Collection<RELATED_PE> relatedCollection = new LinkedList<RELATED_PE>();
+                    IdListUpdateValue<? extends RELATED_ID> listUpdate = getRelatedUpdate(context, update);
 
-                    if (action instanceof ListUpdateActionSet<?> || action instanceof ListUpdateActionAdd<?>)
+                    if (listUpdate != null && listUpdate.hasActions())
                     {
-                        for (RELATED_ID relatedId : action.getItems())
+                        for (ListUpdateAction<? extends RELATED_ID> action : listUpdate.getActions())
                         {
-                            RELATED_PE related = relatedMap.get(relatedId);
-                            if (related == null)
+                            Collection<RELATED_PE> relatedCollection = new LinkedList<RELATED_PE>();
+
+                            if (action instanceof ListUpdateActionSet<?> || action instanceof ListUpdateActionAdd<?>)
                             {
-                                throw new ObjectNotFoundException((IObjectId) relatedId);
-                            }
-                            check(context, entity, relatedId, related);
-                            relatedCollection.add(related);
-                        }
-                        if (action instanceof ListUpdateActionSet<?>)
-                        {
-                            set(context, entity, relatedCollection, allAdded, allRemoved);
-                        } else
-                        {
-                            add(context, entity, relatedCollection, allAdded);
-                        }
-                    } else if (action instanceof ListUpdateActionRemove<?>)
-                    {
-                        for (RELATED_ID relatedId : action.getItems())
-                        {
-                            RELATED_PE related = relatedMap.get(relatedId);
-                            if (related != null)
+                                for (RELATED_ID relatedId : action.getItems())
+                                {
+                                    RELATED_PE related = relatedMap.get(relatedId);
+                                    if (related == null)
+                                    {
+                                        throw new ObjectNotFoundException((IObjectId) relatedId);
+                                    }
+                                    check(context, entity, relatedId, related);
+                                    relatedCollection.add(related);
+                                }
+                                if (action instanceof ListUpdateActionSet<?>)
+                                {
+                                    set(context, entity, relatedCollection, allAdded, allRemoved);
+                                } else
+                                {
+                                    add(context, entity, relatedCollection, allAdded);
+                                }
+                            } else if (action instanceof ListUpdateActionRemove<?>)
                             {
-                                relatedCollection.add(related);
-                                check(context, entity, relatedId, related);
+                                for (RELATED_ID relatedId : action.getItems())
+                                {
+                                    RELATED_PE related = relatedMap.get(relatedId);
+                                    if (related != null)
+                                    {
+                                        relatedCollection.add(related);
+                                        check(context, entity, relatedId, related);
+                                    }
+                                }
+                                remove(context, entity, relatedCollection, allRemoved);
                             }
                         }
-                        remove(context, entity, relatedCollection, allRemoved);
                     }
                 }
-            }
-        }
 
-        postUpdate(context, allAdded, allRemoved);
+                @Override
+                public IProgress createProgress(ENTITY_UPDATE key, ENTITY_PE value, int objectIndex, int totalObjectCount)
+                {
+                    return new UpdateEntityRelationProgress(key, getRelationName(), objectIndex, totalObjectCount);
+                }
+            };
 
+        postUpdate(context, allAdded, allRemoved);
     }
 
     protected void postUpdate(IOperationContext context, Collection<RELATED_PE> allAdded, Collection<RELATED_PE> allRemoved)
@@ -161,6 +172,8 @@ public abstract class AbstractUpdateEntityToManyRelationExecutor<ENTITY_UPDATE,
         }
     }
 
+    protected abstract String getRelationName();
+
     protected abstract Collection<RELATED_PE> getCurrentlyRelated(ENTITY_PE entity);
 
     protected abstract IdListUpdateValue<? extends RELATED_ID> getRelatedUpdate(IOperationContext context, ENTITY_UPDATE update);
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityToOneRelationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityToOneRelationExecutor.java
index 0f55c60dfd1e1ae19ec5057f31731ac8e16a4202..f2eca35e9157152587ef280770522d528ebb6281 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityToOneRelationExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityToOneRelationExecutor.java
@@ -28,7 +28,11 @@ import org.springframework.beans.factory.annotation.Autowired;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.IObjectId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.FieldUpdateValue;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.ObjectNotFoundException;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.UpdateEntityRelationProgress;
 import ch.systemsx.cisd.openbis.generic.server.business.IRelationshipService;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.ICommonBusinessObjectFactory;
 
@@ -47,13 +51,11 @@ public abstract class AbstractUpdateEntityToOneRelationExecutor<ENTITY_UPDATE, E
     protected ICommonBusinessObjectFactory boFactory;
 
     @Override
-    public void update(IOperationContext context, Map<ENTITY_UPDATE, ENTITY_PE> entitiesMap)
+    public void update(IOperationContext context, MapBatch<ENTITY_UPDATE, ENTITY_PE> batch)
     {
-        Set<RELATED_PE> allAdded = new HashSet<RELATED_PE>();
-        Set<RELATED_PE> allRemoved = new HashSet<RELATED_PE>();
         List<RELATED_ID> relatedIds = new LinkedList<RELATED_ID>();
 
-        for (ENTITY_UPDATE update : entitiesMap.keySet())
+        for (ENTITY_UPDATE update : batch.getObjects().keySet())
         {
             FieldUpdateValue<RELATED_ID> relatedUpdate = getRelatedUpdate(update);
 
@@ -65,68 +67,73 @@ public abstract class AbstractUpdateEntityToOneRelationExecutor<ENTITY_UPDATE, E
 
         Map<RELATED_ID, RELATED_PE> relatedMap = map(context, relatedIds);
 
-        update(context, entitiesMap, relatedMap, allAdded, allRemoved);
-
-        postUpdate(context, allAdded, allRemoved);
+        updateCommon(context, batch, relatedMap);
     }
 
     @Override
-    public void update(IOperationContext context, Map<ENTITY_UPDATE, ENTITY_PE> entitiesMap, Map<RELATED_ID, RELATED_PE> relatedMap)
+    public void update(IOperationContext context, MapBatch<ENTITY_UPDATE, ENTITY_PE> batch, Map<RELATED_ID, RELATED_PE> relatedMap)
     {
-        Set<RELATED_PE> allAdded = new HashSet<RELATED_PE>();
-        Set<RELATED_PE> allRemoved = new HashSet<RELATED_PE>();
-
-        update(context, entitiesMap, relatedMap, allAdded, allRemoved);
-
-        postUpdate(context, allAdded, allRemoved);
+        updateCommon(context, batch, relatedMap);
     }
 
-    private void update(IOperationContext context, Map<ENTITY_UPDATE, ENTITY_PE> entitiesMap, Map<RELATED_ID, RELATED_PE> relatedMap,
-            Set<RELATED_PE> allAdded, Set<RELATED_PE> allRemoved)
+    private void updateCommon(final IOperationContext context, final MapBatch<ENTITY_UPDATE, ENTITY_PE> batch,
+            final Map<RELATED_ID, RELATED_PE> relatedMap)
     {
-        for (Map.Entry<ENTITY_UPDATE, ENTITY_PE> entry : entitiesMap.entrySet())
-        {
-            ENTITY_UPDATE update = entry.getKey();
-            ENTITY_PE entity = entry.getValue();
+        final Set<RELATED_PE> allAdded = new HashSet<RELATED_PE>();
+        final Set<RELATED_PE> allRemoved = new HashSet<RELATED_PE>();
 
-            FieldUpdateValue<RELATED_ID> relatedUpdate = getRelatedUpdate(update);
-            RELATED_PE currentlyRelated = getCurrentlyRelated(entity);
-
-            if (relatedUpdate != null && relatedUpdate.isModified())
+        new MapBatchProcessor<ENTITY_UPDATE, ENTITY_PE>(context, batch)
             {
-                RELATED_ID relatedId = relatedUpdate.getValue();
-
-                if (relatedId == null)
-                {
-                    if (currentlyRelated != null)
-                    {
-                        check(context, entity, getRelatedId(currentlyRelated), currentlyRelated);
-                        update(context, entity, null);
-                        allRemoved.add(currentlyRelated);
-                    }
-                } else
+                @Override
+                public void process(ENTITY_UPDATE update, ENTITY_PE entity)
                 {
-                    RELATED_PE related = relatedMap.get(relatedId);
+                    FieldUpdateValue<RELATED_ID> relatedUpdate = getRelatedUpdate(update);
+                    RELATED_PE currentlyRelated = getCurrentlyRelated(entity);
 
-                    if (related == null)
+                    if (relatedUpdate != null && relatedUpdate.isModified())
                     {
-                        throw new ObjectNotFoundException((IObjectId) relatedId);
-                    }
+                        RELATED_ID relatedId = relatedUpdate.getValue();
 
-                    if (false == related.equals(currentlyRelated))
-                    {
-                        check(context, entity, relatedId, related);
-                        update(context, entity, related);
-                        allAdded.add(related);
-                        if (currentlyRelated != null)
+                        if (relatedId == null)
                         {
-                            allRemoved.add(currentlyRelated);
+                            if (currentlyRelated != null)
+                            {
+                                check(context, entity, getRelatedId(currentlyRelated), currentlyRelated);
+                                update(context, entity, null);
+                                allRemoved.add(currentlyRelated);
+                            }
+                        } else
+                        {
+                            RELATED_PE related = relatedMap.get(relatedId);
+
+                            if (related == null)
+                            {
+                                throw new ObjectNotFoundException((IObjectId) relatedId);
+                            }
+
+                            if (false == related.equals(currentlyRelated))
+                            {
+                                check(context, entity, relatedId, related);
+                                update(context, entity, related);
+                                allAdded.add(related);
+                                if (currentlyRelated != null)
+                                {
+                                    allRemoved.add(currentlyRelated);
+                                }
+                            }
                         }
+
                     }
                 }
 
-            }
-        }
+                @Override
+                public IProgress createProgress(ENTITY_UPDATE key, ENTITY_PE value, int objectIndex, int totalObjectCount)
+                {
+                    return new UpdateEntityRelationProgress(key, getRelationName(), objectIndex, totalObjectCount);
+                }
+            };
+
+        postUpdate(context, allAdded, allRemoved);
     }
 
     protected void postUpdate(IOperationContext context, Collection<RELATED_PE> allAdded, Collection<RELATED_PE> allRemoved)
@@ -134,6 +141,8 @@ public abstract class AbstractUpdateEntityToOneRelationExecutor<ENTITY_UPDATE, E
         // by default do nothing
     }
 
+    protected abstract String getRelationName();
+
     protected abstract RELATED_ID getRelatedId(RELATED_PE related);
 
     protected abstract RELATED_PE getCurrentlyRelated(ENTITY_PE entity);
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractVerifyEntityCyclesExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractVerifyEntityCyclesExecutor.java
index 24b07637fe72d57a0721225c79594a3147df5a8a..e716dc3e0422fe1b42d5664e210fea493d7bf182 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractVerifyEntityCyclesExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractVerifyEntityCyclesExecutor.java
@@ -26,6 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 import ch.systemsx.cisd.common.collection.CycleFoundException;
 import ch.systemsx.cisd.common.collection.GroupingDAG;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
@@ -42,9 +43,9 @@ public abstract class AbstractVerifyEntityCyclesExecutor<ENTITY_PE> implements I
     protected IDAOFactory daoFactory;
 
     @Override
-    public void verify(IOperationContext context, Collection<ENTITY_PE> entities)
+    public void verify(IOperationContext context, CollectionBatch<ENTITY_PE> batch)
     {
-        Map<Long, Collection<Long>> graph = getGraph(context, entities);
+        Map<Long, Collection<Long>> graph = getGraph(context, batch.getObjects());
 
         checkCycles(graph);
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/ISetEntityRelationsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/ISetEntityRelationsExecutor.java
index f70a9eed5061bcb2263c71b456ef02cd57a0f572..927aa6169c05e8acf9d65e3a2805e19a7780eea4 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/ISetEntityRelationsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/ISetEntityRelationsExecutor.java
@@ -16,9 +16,8 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity;
 
-import java.util.Map;
-
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 
 /**
  * @author pkupczyk
@@ -26,6 +25,6 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 public interface ISetEntityRelationsExecutor<ENTITY_CREATION, ENTITY_PE>
 {
 
-    public void set(IOperationContext context, Map<ENTITY_CREATION, ENTITY_PE> entitiesMap);
+    public void set(IOperationContext context, MapBatch<ENTITY_CREATION, ENTITY_PE> batch);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/ISetEntityRelationsWithCacheExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/ISetEntityRelationsWithCacheExecutor.java
index 9eeb0032010b89657dd11b7a12acdac2c80c3042..1f59eae266d1d2927068adcdb7f26674014f4a10 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/ISetEntityRelationsWithCacheExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/ISetEntityRelationsWithCacheExecutor.java
@@ -19,6 +19,7 @@ package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity;
 import java.util.Map;
 
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 
 /**
  * @author pkupczyk
@@ -26,6 +27,6 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 public interface ISetEntityRelationsWithCacheExecutor<ENTITY_CREATION, ENTITY_PE, RELATED_ID, RELATED_PE>
 {
 
-    public void set(IOperationContext context, Map<ENTITY_CREATION, ENTITY_PE> entitiesMap, Map<RELATED_ID, RELATED_PE> relatedMap);
+    public void set(IOperationContext context, MapBatch<ENTITY_CREATION, ENTITY_PE> batch, Map<RELATED_ID, RELATED_PE> relatedMap);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/IUpdateEntityRelationsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/IUpdateEntityRelationsExecutor.java
index a1b963f8048f7598710c5a6a88414e7fe8e0fd33..67779694b2998ab680481bc113b39b4ec2ab84a6 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/IUpdateEntityRelationsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/IUpdateEntityRelationsExecutor.java
@@ -16,9 +16,8 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity;
 
-import java.util.Map;
-
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 
 /**
  * @author pkupczyk
@@ -26,6 +25,6 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 public interface IUpdateEntityRelationsExecutor<ENTITY_UPDATE, ENTITY_PE>
 {
 
-    public void update(IOperationContext context, Map<ENTITY_UPDATE, ENTITY_PE> entitiesMap);
+    public void update(IOperationContext context, MapBatch<ENTITY_UPDATE, ENTITY_PE> batch);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/IUpdateEntityRelationsWithCacheExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/IUpdateEntityRelationsWithCacheExecutor.java
index a9ba8fec546948f25b1b88df8bb77f9c35f09ff2..5d749a8687caa122d9778129d0fcd2a9869c5112 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/IUpdateEntityRelationsWithCacheExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/IUpdateEntityRelationsWithCacheExecutor.java
@@ -19,6 +19,7 @@ package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity;
 import java.util.Map;
 
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 
 /**
  * @author pkupczyk
@@ -26,6 +27,6 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 public interface IUpdateEntityRelationsWithCacheExecutor<ENTITY_UPDATE, ENTITY_PE, RELATED_ID, RELATED_PE>
 {
 
-    public void update(IOperationContext context, Map<ENTITY_UPDATE, ENTITY_PE> entitiesMap, Map<RELATED_ID, RELATED_PE> relatedMap);
+    public void update(IOperationContext context, MapBatch<ENTITY_UPDATE, ENTITY_PE> batch, Map<RELATED_ID, RELATED_PE> relatedMap);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/IVerifyEntityRelationsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/IVerifyEntityRelationsExecutor.java
index b53585be60a5ce7803b785d6e8f7821b88dd55fd..c709220ba1a4d811fc14b4bdf3e4b579adbb275d 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/IVerifyEntityRelationsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/IVerifyEntityRelationsExecutor.java
@@ -16,9 +16,8 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity;
 
-import java.util.Collection;
-
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 
 /**
  * @author pkupczyk
@@ -26,6 +25,6 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 public interface IVerifyEntityRelationsExecutor<ENTITY_PE>
 {
 
-    public void verify(IOperationContext context, Collection<ENTITY_PE> entities);
+    public void verify(IOperationContext context, CollectionBatch<ENTITY_PE> batch);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/CreateExperimentExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/CreateExperimentExecutor.java
index 8df09df19e3f0eac485ef7a95661aae0d13c1778..5c2af3fa4d6378fffd2318860bb6a3758557a4c8 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/CreateExperimentExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/CreateExperimentExecutor.java
@@ -34,11 +34,16 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.ExperimentIdentifi
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.ExperimentPermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessException;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.attachment.ICreateAttachmentExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractCreateEntityExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property.IUpdateEntityPropertyExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.tag.IAddTagToEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.CreateEntityProgress;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.authorization.validator.ExperimentByIdentiferValidator;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.DataAccessExceptionTranslator;
@@ -82,22 +87,32 @@ public class CreateExperimentExecutor extends AbstractCreateEntityExecutor<Exper
     private IVerifyExperimentExecutor verifyExperimentExecutor;
 
     @Override
-    protected List<ExperimentPE> createEntities(IOperationContext context, Collection<ExperimentCreation> creations)
+    protected List<ExperimentPE> createEntities(IOperationContext context, CollectionBatch<ExperimentCreation> batch)
     {
-        List<ExperimentPE> experiments = new LinkedList<ExperimentPE>();
-
-        PersonPE person = context.getSession().tryGetPerson();
-        Date timeStamp = daoFactory.getTransactionTimestamp();
-        for (ExperimentCreation creation : creations)
-        {
-            ExperimentPE experiment = new ExperimentPE();
-            experiment.setCode(creation.getCode());
-            String createdPermId = daoFactory.getPermIdDAO().createPermId();
-            experiment.setPermId(createdPermId);
-            experiment.setRegistrator(person);
-            RelationshipUtils.updateModificationDateAndModifier(experiment, person, timeStamp);
-            experiments.add(experiment);
-        }
+        final List<ExperimentPE> experiments = new LinkedList<ExperimentPE>();
+        final PersonPE person = context.getSession().tryGetPerson();
+        final Date timeStamp = daoFactory.getTransactionTimestamp();
+
+        new CollectionBatchProcessor<ExperimentCreation>(context, batch)
+            {
+                @Override
+                public void process(ExperimentCreation object)
+                {
+                    ExperimentPE experiment = new ExperimentPE();
+                    experiment.setCode(object.getCode());
+                    String createdPermId = daoFactory.getPermIdDAO().createPermId();
+                    experiment.setPermId(createdPermId);
+                    experiment.setRegistrator(person);
+                    RelationshipUtils.updateModificationDateAndModifier(experiment, person, timeStamp);
+                    experiments.add(experiment);
+                }
+
+                @Override
+                public IProgress createProgress(ExperimentCreation object, int objectIndex, int totalObjectCount)
+                {
+                    return new CreateEntityProgress(object, objectIndex, totalObjectCount);
+                }
+            };
 
         return experiments;
     }
@@ -129,19 +144,19 @@ public class CreateExperimentExecutor extends AbstractCreateEntityExecutor<Exper
     }
 
     @Override
-    protected void checkBusinessRules(IOperationContext context, Collection<ExperimentPE> entities)
+    protected void checkBusinessRules(IOperationContext context, CollectionBatch<ExperimentPE> batch)
     {
-        verifyExperimentExecutor.verify(context, entities);
+        verifyExperimentExecutor.verify(context, batch);
     }
 
     @Override
-    protected void updateBatch(IOperationContext context, Map<ExperimentCreation, ExperimentPE> entitiesMap)
+    protected void updateBatch(IOperationContext context, MapBatch<ExperimentCreation, ExperimentPE> batch)
     {
-        setExperimentProjectExecutor.set(context, entitiesMap);
-        setExperimentTypeExecutor.set(context, entitiesMap);
+        setExperimentProjectExecutor.set(context, batch);
+        setExperimentTypeExecutor.set(context, batch);
 
         Map<IEntityPropertiesHolder, Map<String, String>> propertyMap = new HashMap<IEntityPropertiesHolder, Map<String, String>>();
-        for (Map.Entry<ExperimentCreation, ExperimentPE> entry : entitiesMap.entrySet())
+        for (Map.Entry<ExperimentCreation, ExperimentPE> entry : batch.getObjects().entrySet())
         {
             propertyMap.put(entry.getValue(), entry.getKey().getProperties());
         }
@@ -149,13 +164,13 @@ public class CreateExperimentExecutor extends AbstractCreateEntityExecutor<Exper
     }
 
     @Override
-    protected void updateAll(IOperationContext context, Map<ExperimentCreation, ExperimentPE> entitiesMap)
+    protected void updateAll(IOperationContext context, MapBatch<ExperimentCreation, ExperimentPE> batch)
     {
         Map<AttachmentHolderPE, Collection<? extends AttachmentCreation>> attachmentMap =
                 new HashMap<AttachmentHolderPE, Collection<? extends AttachmentCreation>>();
         Map<IEntityWithMetaprojects, Collection<? extends ITagId>> tagMap = new HashMap<IEntityWithMetaprojects, Collection<? extends ITagId>>();
 
-        for (Map.Entry<ExperimentCreation, ExperimentPE> entry : entitiesMap.entrySet())
+        for (Map.Entry<ExperimentCreation, ExperimentPE> entry : batch.getObjects().entrySet())
         {
             ExperimentCreation creation = entry.getKey();
             ExperimentPE entity = entry.getValue();
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/IVerifyExperimentExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/IVerifyExperimentExecutor.java
index 2e6e5a7132bb595d59add02659c6d88a53add4a6..d9696e0cc41c00636f067d1370f2c1d35676edcd 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/IVerifyExperimentExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/IVerifyExperimentExecutor.java
@@ -16,9 +16,8 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.experiment;
 
-import java.util.Collection;
-
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 
 /**
@@ -27,6 +26,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 public interface IVerifyExperimentExecutor
 {
 
-    public void verify(IOperationContext context, Collection<ExperimentPE> experiments);
+    public void verify(IOperationContext context, CollectionBatch<ExperimentPE> batch);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/SetExperimentProjectExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/SetExperimentProjectExecutor.java
index 3ddf75fb3a4235cc61faf7208a1062c34c770840..fbfe8d1fee5d43e50800f29aa335feab0fd6c3bf 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/SetExperimentProjectExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/SetExperimentProjectExecutor.java
@@ -45,6 +45,12 @@ public class SetExperimentProjectExecutor extends AbstractSetEntityToOneRelation
     @Autowired
     private IMapProjectByIdExecutor mapProjectByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "experiment-project";
+    }
+
     @Override
     protected IProjectId getRelatedId(ExperimentCreation creation)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/SetExperimentTypeExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/SetExperimentTypeExecutor.java
index 0ed2864dd0ae54bdc82196bf26efc95b873045c7..eab13b05acfc9e490e9e39a9905c3174d0aa019c 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/SetExperimentTypeExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/SetExperimentTypeExecutor.java
@@ -44,6 +44,12 @@ public class SetExperimentTypeExecutor extends AbstractSetEntityToOneRelationExe
     @Autowired
     private IMapEntityTypeByIdExecutor mapEntityTypeByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "experiment-type";
+    }
+
     @Override
     protected IEntityTypeId getRelatedId(ExperimentCreation creation)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/UpdateExperimentExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/UpdateExperimentExecutor.java
index f3587f453c8849dd044ce3ada8e8d78c183336b0..a75d67113ed79acf4329fa3df560e7327751445f 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/UpdateExperimentExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/UpdateExperimentExecutor.java
@@ -32,6 +32,8 @@ import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessE
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractUpdateEntityExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.tag.IUpdateTagForEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.authorization.validator.ExperimentByIdentiferValidator;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.DataAccessExceptionTranslator;
@@ -96,20 +98,20 @@ public class UpdateExperimentExecutor extends AbstractUpdateEntityExecutor<Exper
     }
 
     @Override
-    protected void checkBusinessRules(IOperationContext context, Collection<ExperimentPE> entities)
+    protected void checkBusinessRules(IOperationContext context, CollectionBatch<ExperimentPE> batch)
     {
-        verifyExperimentExecutor.verify(context, entities);
+        verifyExperimentExecutor.verify(context, batch);
     }
 
     @Override
-    protected void updateBatch(IOperationContext context, Map<ExperimentUpdate, ExperimentPE> entitiesMap)
+    protected void updateBatch(IOperationContext context, MapBatch<ExperimentUpdate, ExperimentPE> batch)
     {
-        updateExperimentProjectExecutor.update(context, entitiesMap);
+        updateExperimentProjectExecutor.update(context, batch);
 
         Map<IEntityPropertiesHolder, Map<String, String>> propertyMap = new HashMap<IEntityPropertiesHolder, Map<String, String>>();
         PersonPE person = context.getSession().tryGetPerson();
         Date timeStamp = daoFactory.getTransactionTimestamp();
-        for (Map.Entry<ExperimentUpdate, ExperimentPE> entry : entitiesMap.entrySet())
+        for (Map.Entry<ExperimentUpdate, ExperimentPE> entry : batch.getObjects().entrySet())
         {
             ExperimentUpdate update = entry.getKey();
             ExperimentPE entity = entry.getValue();
@@ -135,7 +137,7 @@ public class UpdateExperimentExecutor extends AbstractUpdateEntityExecutor<Exper
     }
 
     @Override
-    protected void updateAll(IOperationContext context, Map<ExperimentUpdate, ExperimentPE> entitiesMap)
+    protected void updateAll(IOperationContext context, MapBatch<ExperimentUpdate, ExperimentPE> batch)
     {
     }
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/UpdateExperimentProjectExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/UpdateExperimentProjectExecutor.java
index 461984f591f6b618543aa9660fd731f2c38626a2..c4417bb0971daaa739210da874fc73c7847d6b03 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/UpdateExperimentProjectExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/UpdateExperimentProjectExecutor.java
@@ -46,6 +46,12 @@ public class UpdateExperimentProjectExecutor extends AbstractUpdateEntityToOneRe
     @Autowired
     private IMapProjectByIdExecutor mapProjectByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "experiment-project";
+    }
+
     @Override
     protected IProjectId getRelatedId(ProjectPE related)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/VerifyExperimentExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/VerifyExperimentExecutor.java
index 89edab7a8ac78fc5e278ac83ac1dddc138a368f4..fda41f8e332f7054bc2c82d6e3c869445fd2ca81 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/VerifyExperimentExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/VerifyExperimentExecutor.java
@@ -16,13 +16,12 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.experiment;
 
-import java.util.Collection;
-
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property.IVerifyEntityPropertyExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 
 /**
@@ -46,12 +45,9 @@ public class VerifyExperimentExecutor implements IVerifyExperimentExecutor
     }
 
     @Override
-    public void verify(IOperationContext context, Collection<ExperimentPE> experiments)
+    public void verify(IOperationContext context, CollectionBatch<ExperimentPE> batch)
     {
-        if (experiments != null && false == experiments.isEmpty())
-        {
-            verifyEntityPropertyExecutor.verify(context, experiments);
-        }
+        verifyEntityPropertyExecutor.verify(context, batch);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/CreateMaterialExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/CreateMaterialExecutor.java
index f322cf32818ef70861e7d7615b3da17d4a2fd0fd..8080b9bc0f40ed70b9e7602f4092bb87770c6950 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/CreateMaterialExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/CreateMaterialExecutor.java
@@ -32,10 +32,15 @@ import org.springframework.stereotype.Component;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.create.MaterialCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.id.MaterialPermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractCreateEntityExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property.IUpdateEntityPropertyExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.tag.IAddTagToEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.CreateEntityProgress;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.ComponentNames;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.DataAccessExceptionTranslator;
@@ -74,16 +79,28 @@ public class CreateMaterialExecutor extends AbstractCreateEntityExecutor<Materia
     private IVerifyMaterialExecutor verifyMaterialExecutor;
 
     @Override
-    protected List<MaterialPE> createEntities(IOperationContext context, Collection<MaterialCreation> creations)
+    protected List<MaterialPE> createEntities(final IOperationContext context, CollectionBatch<MaterialCreation> batch)
     {
-        List<MaterialPE> materials = new LinkedList<MaterialPE>();
-        for (MaterialCreation creation : creations)
-        {
-            MaterialPE material = new MaterialPE();
-            material.setCode(creation.getCode());
-            material.setRegistrator(context.getSession().tryGetPerson());
-            materials.add(material);
-        }
+        final List<MaterialPE> materials = new LinkedList<MaterialPE>();
+
+        new CollectionBatchProcessor<MaterialCreation>(context, batch)
+            {
+                @Override
+                public void process(MaterialCreation object)
+                {
+                    MaterialPE material = new MaterialPE();
+                    material.setCode(object.getCode());
+                    material.setRegistrator(context.getSession().tryGetPerson());
+                    materials.add(material);
+                }
+
+                @Override
+                public IProgress createProgress(MaterialCreation object, int objectIndex, int totalObjectCount)
+                {
+                    return new CreateEntityProgress(object, objectIndex, totalObjectCount);
+                }
+            };
+
         return materials;
     }
 
@@ -111,18 +128,18 @@ public class CreateMaterialExecutor extends AbstractCreateEntityExecutor<Materia
     }
 
     @Override
-    protected void checkBusinessRules(IOperationContext context, Collection<MaterialPE> entities)
+    protected void checkBusinessRules(IOperationContext context, CollectionBatch<MaterialPE> batch)
     {
-        verifyMaterialExecutor.verify(context, entities);
+        verifyMaterialExecutor.verify(context, batch);
     }
 
     @Override
-    protected void updateBatch(IOperationContext context, Map<MaterialCreation, MaterialPE> entitiesMap)
+    protected void updateBatch(IOperationContext context, MapBatch<MaterialCreation, MaterialPE> batch)
     {
-        setMaterialTypeExecutor.set(context, entitiesMap);
+        setMaterialTypeExecutor.set(context, batch);
 
         Map<IEntityPropertiesHolder, Map<String, String>> propertyMap = new HashMap<IEntityPropertiesHolder, Map<String, String>>();
-        for (Map.Entry<MaterialCreation, MaterialPE> entry : entitiesMap.entrySet())
+        for (Map.Entry<MaterialCreation, MaterialPE> entry : batch.getObjects().entrySet())
         {
             propertyMap.put(entry.getValue(), entry.getKey().getProperties());
         }
@@ -130,11 +147,11 @@ public class CreateMaterialExecutor extends AbstractCreateEntityExecutor<Materia
     }
 
     @Override
-    protected void updateAll(IOperationContext context, Map<MaterialCreation, MaterialPE> entitiesMap)
+    protected void updateAll(IOperationContext context, MapBatch<MaterialCreation, MaterialPE> batch)
     {
         Map<IEntityWithMetaprojects, Collection<? extends ITagId>> tagMap = new HashMap<IEntityWithMetaprojects, Collection<? extends ITagId>>();
 
-        for (Map.Entry<MaterialCreation, MaterialPE> entry : entitiesMap.entrySet())
+        for (Map.Entry<MaterialCreation, MaterialPE> entry : batch.getObjects().entrySet())
         {
             MaterialCreation creation = entry.getKey();
             MaterialPE entity = entry.getValue();
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/IVerifyMaterialExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/IVerifyMaterialExecutor.java
index 56bb53ae93980ecd6b21e9f2e9c1ad8cfb7a5665..b5b43cc12047c0cc5f5d3e43fe914e17b2608344 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/IVerifyMaterialExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/IVerifyMaterialExecutor.java
@@ -16,9 +16,8 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.material;
 
-import java.util.Collection;
-
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialPE;
 
 /**
@@ -27,6 +26,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialPE;
 public interface IVerifyMaterialExecutor
 {
 
-    public void verify(IOperationContext context, Collection<MaterialPE> materials);
+    public void verify(IOperationContext context, CollectionBatch<MaterialPE> batch);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/SetMaterialTypeExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/SetMaterialTypeExecutor.java
index 04ca9be036a66e6dbed21916d6fb6b1d5eb5e599..04b93bfa63f7ebf3662fae741f90f35c4692ea5b 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/SetMaterialTypeExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/SetMaterialTypeExecutor.java
@@ -45,6 +45,12 @@ public class SetMaterialTypeExecutor extends AbstractSetEntityToOneRelationExecu
     @Autowired
     private IMapEntityTypeByIdExecutor mapEntityTypeByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "material-type";
+    }
+
     @Override
     protected IEntityTypeId getRelatedId(MaterialCreation creation)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/UpdateMaterialExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/UpdateMaterialExecutor.java
index c2e5221fe253a4df88a787e913d0dc52d389acdd..57e0ad398d0dc41d7ba4b0e0114d85a2993eb9b4 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/UpdateMaterialExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/UpdateMaterialExecutor.java
@@ -31,6 +31,8 @@ import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessE
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractUpdateEntityExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.tag.IUpdateTagForEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.authorization.validator.SampleByIdentiferValidator;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.DataAccessExceptionTranslator;
@@ -86,16 +88,16 @@ public class UpdateMaterialExecutor extends AbstractUpdateEntityExecutor<Materia
     }
 
     @Override
-    protected void checkBusinessRules(IOperationContext context, Collection<MaterialPE> entities)
+    protected void checkBusinessRules(IOperationContext context, CollectionBatch<MaterialPE> batch)
     {
-        verifyMaterialExecutor.verify(context, entities);
+        verifyMaterialExecutor.verify(context, batch);
     }
 
     @Override
-    protected void updateBatch(IOperationContext context, Map<MaterialUpdate, MaterialPE> entitiesMap)
+    protected void updateBatch(IOperationContext context, MapBatch<MaterialUpdate, MaterialPE> batch)
     {
         Map<IEntityPropertiesHolder, Map<String, String>> propertyMap = new HashMap<IEntityPropertiesHolder, Map<String, String>>();
-        for (Map.Entry<MaterialUpdate, MaterialPE> entry : entitiesMap.entrySet())
+        for (Map.Entry<MaterialUpdate, MaterialPE> entry : batch.getObjects().entrySet())
         {
             MaterialUpdate update = entry.getKey();
             MaterialPE entity = entry.getValue();
@@ -114,7 +116,7 @@ public class UpdateMaterialExecutor extends AbstractUpdateEntityExecutor<Materia
     }
 
     @Override
-    protected void updateAll(IOperationContext context, Map<MaterialUpdate, MaterialPE> entitiesMap)
+    protected void updateAll(IOperationContext context, MapBatch<MaterialUpdate, MaterialPE> batch)
     {
     }
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/VerifyMaterialExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/VerifyMaterialExecutor.java
index 2d9d4fd992a960d575b74b271f6448709c199bc4..b1251882077c297d27f51c16f00d21fa8a7c9691 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/VerifyMaterialExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/material/VerifyMaterialExecutor.java
@@ -16,13 +16,12 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.material;
 
-import java.util.Collection;
-
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property.IVerifyEntityPropertyExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialPE;
 
 /**
@@ -46,9 +45,9 @@ public class VerifyMaterialExecutor implements IVerifyMaterialExecutor
     }
 
     @Override
-    public void verify(IOperationContext context, Collection<MaterialPE> materials)
+    public void verify(IOperationContext context, CollectionBatch<MaterialPE> batch)
     {
-        verifyEntityPropertyExecutor.verify(context, materials);
+        verifyEntityPropertyExecutor.verify(context, batch);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/CreateProjectExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/CreateProjectExecutor.java
index ff3b1aaab43520648af43b47a1c84cc59d564d1e..881258ad96a58d7299dfab1781cc271902c2d42a 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/CreateProjectExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/CreateProjectExecutor.java
@@ -33,9 +33,14 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.create.ProjectCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.id.ProjectIdentifier;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.id.ProjectPermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessException;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.attachment.ICreateAttachmentExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractCreateEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.CreateEntityProgress;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.authorization.validator.ProjectByIdentiferValidator;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.DataAccessExceptionTranslator;
@@ -67,23 +72,33 @@ public class CreateProjectExecutor extends AbstractCreateEntityExecutor<ProjectC
     private ICreateAttachmentExecutor createAttachmentExecutor;
 
     @Override
-    protected List<ProjectPE> createEntities(IOperationContext context, Collection<ProjectCreation> creations)
+    protected List<ProjectPE> createEntities(IOperationContext context, CollectionBatch<ProjectCreation> batch)
     {
-        List<ProjectPE> projects = new LinkedList<ProjectPE>();
-
-        PersonPE person = context.getSession().tryGetPerson();
-        Date timeStamp = daoFactory.getTransactionTimestamp();
-        for (ProjectCreation creation : creations)
-        {
-            ProjectPE project = new ProjectPE();
-            project.setCode(creation.getCode());
-            String createdPermId = daoFactory.getPermIdDAO().createPermId();
-            project.setPermId(createdPermId);
-            project.setDescription(creation.getDescription());
-            project.setRegistrator(person);
-            RelationshipUtils.updateModificationDateAndModifier(project, person, timeStamp);
-            projects.add(project);
-        }
+        final List<ProjectPE> projects = new LinkedList<ProjectPE>();
+        final PersonPE person = context.getSession().tryGetPerson();
+        final Date timeStamp = daoFactory.getTransactionTimestamp();
+
+        new CollectionBatchProcessor<ProjectCreation>(context, batch)
+            {
+                @Override
+                public void process(ProjectCreation object)
+                {
+                    ProjectPE project = new ProjectPE();
+                    project.setCode(object.getCode());
+                    String createdPermId = daoFactory.getPermIdDAO().createPermId();
+                    project.setPermId(createdPermId);
+                    project.setDescription(object.getDescription());
+                    project.setRegistrator(person);
+                    RelationshipUtils.updateModificationDateAndModifier(project, person, timeStamp);
+                    projects.add(project);
+                }
+
+                @Override
+                public IProgress createProgress(ProjectCreation object, int objectIndex, int totalObjectCount)
+                {
+                    return new CreateEntityProgress(object, objectIndex, totalObjectCount);
+                }
+            };
 
         return projects;
     }
@@ -115,25 +130,25 @@ public class CreateProjectExecutor extends AbstractCreateEntityExecutor<ProjectC
     }
 
     @Override
-    protected void checkBusinessRules(IOperationContext context, Collection<ProjectPE> entities)
+    protected void checkBusinessRules(IOperationContext context, CollectionBatch<ProjectPE> batch)
     {
         // nothing to do
     }
 
     @Override
-    protected void updateBatch(IOperationContext context, Map<ProjectCreation, ProjectPE> entitiesMap)
+    protected void updateBatch(IOperationContext context, MapBatch<ProjectCreation, ProjectPE> batch)
     {
-        setProjectSpaceExecutor.set(context, entitiesMap);
-        setProjectLeaderExecutor.set(context, entitiesMap);
+        setProjectSpaceExecutor.set(context, batch);
+        setProjectLeaderExecutor.set(context, batch);
     }
 
     @Override
-    protected void updateAll(IOperationContext context, Map<ProjectCreation, ProjectPE> entitiesMap)
+    protected void updateAll(IOperationContext context, MapBatch<ProjectCreation, ProjectPE> batch)
     {
         Map<AttachmentHolderPE, Collection<? extends AttachmentCreation>> attachmentMap =
                 new HashMap<AttachmentHolderPE, Collection<? extends AttachmentCreation>>();
 
-        for (Map.Entry<ProjectCreation, ProjectPE> entry : entitiesMap.entrySet())
+        for (Map.Entry<ProjectCreation, ProjectPE> entry : batch.getObjects().entrySet())
         {
             ProjectCreation creation = entry.getKey();
             ProjectPE entity = entry.getValue();
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/SetProjectLeaderExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/SetProjectLeaderExecutor.java
index 252d50806ba46cbdc98622288385fe7338a30a00..eeed29e5db89bc352899e31a0e0ec25e5f097bd3 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/SetProjectLeaderExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/SetProjectLeaderExecutor.java
@@ -41,6 +41,12 @@ public class SetProjectLeaderExecutor extends AbstractSetEntityToOneRelationExec
     @Autowired
     private IMapPersonByIdExecutor mapPersonByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "project-leader";
+    }
+
     @Override
     protected IPersonId getRelatedId(ProjectCreation creation)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/SetProjectSpaceExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/SetProjectSpaceExecutor.java
index d8fef21f38952a8b98adb8dc47908a619dd011cc..7fd3ec125e02d82653e2cf4269fe1d559e8b844c 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/SetProjectSpaceExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/SetProjectSpaceExecutor.java
@@ -44,6 +44,12 @@ public class SetProjectSpaceExecutor extends AbstractSetEntityToOneRelationExecu
     @Autowired
     private IMapSpaceByIdExecutor mapSpaceByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "project-space";
+    }
+
     @Override
     protected ISpaceId getRelatedId(ProjectCreation creation)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/UpdateProjectExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/UpdateProjectExecutor.java
index 593ee016d0389fca7de1d35efd508b6a959c6e83..c96e5d25f6de12a16f679c48452f2f0490732c4f 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/UpdateProjectExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/UpdateProjectExecutor.java
@@ -30,6 +30,8 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.update.ProjectUpdate;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessException;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractUpdateEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.authorization.validator.ProjectByIdentiferValidator;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.DataAccessExceptionTranslator;
@@ -83,19 +85,19 @@ public class UpdateProjectExecutor extends AbstractUpdateEntityExecutor<ProjectU
     }
 
     @Override
-    protected void checkBusinessRules(IOperationContext context, Collection<ProjectPE> entities)
+    protected void checkBusinessRules(IOperationContext context, CollectionBatch<ProjectPE> batch)
     {
         // nothing to do
     }
 
     @Override
-    protected void updateBatch(IOperationContext context, Map<ProjectUpdate, ProjectPE> entitiesMap)
+    protected void updateBatch(IOperationContext context, MapBatch<ProjectUpdate, ProjectPE> batch)
     {
-        updateProjectSpaceExecutor.update(context, entitiesMap);
+        updateProjectSpaceExecutor.update(context, batch);
 
         PersonPE person = context.getSession().tryGetPerson();
         Date timeStamp = daoFactory.getTransactionTimestamp();
-        for (Map.Entry<ProjectUpdate, ProjectPE> entry : entitiesMap.entrySet())
+        for (Map.Entry<ProjectUpdate, ProjectPE> entry : batch.getObjects().entrySet())
         {
             ProjectUpdate update = entry.getKey();
             ProjectPE project = entry.getValue();
@@ -115,7 +117,7 @@ public class UpdateProjectExecutor extends AbstractUpdateEntityExecutor<ProjectU
     }
 
     @Override
-    protected void updateAll(IOperationContext context, Map<ProjectUpdate, ProjectPE> entitiesMap)
+    protected void updateAll(IOperationContext context, MapBatch<ProjectUpdate, ProjectPE> batch)
     {
         // nothing to do
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/UpdateProjectSpaceExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/UpdateProjectSpaceExecutor.java
index e34c693c482fb061b814206dfd7d8b58c1690e74..be2a03a8c8688e409f51d160bdce25467edb372f 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/UpdateProjectSpaceExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/project/UpdateProjectSpaceExecutor.java
@@ -34,6 +34,7 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractUpdateEntityToOneRelationExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IReindexEntityExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.space.IMapSpaceByIdExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.authorization.validator.SimpleSpaceValidator;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
@@ -55,19 +56,25 @@ public class UpdateProjectSpaceExecutor extends AbstractUpdateEntityToOneRelatio
     private IReindexEntityExecutor reindexObjectExecutor;
 
     @Override
-    public void update(IOperationContext context, Map<ProjectUpdate, ProjectPE> entitiesMap)
+    protected String getRelationName()
     {
-        super.update(context, entitiesMap);
+        return "project-space";
+    }
+
+    @Override
+    public void update(IOperationContext context, MapBatch<ProjectUpdate, ProjectPE> batch)
+    {
+        super.update(context, batch);
 
-        reindex(context, entitiesMap.values());
+        reindex(context, batch.getObjects().values());
     }
 
     @Override
-    public void update(IOperationContext context, Map<ProjectUpdate, ProjectPE> entitiesMap, Map<ISpaceId, SpacePE> relatedMap)
+    public void update(IOperationContext context, MapBatch<ProjectUpdate, ProjectPE> batch, Map<ISpaceId, SpacePE> relatedMap)
     {
-        super.update(context, entitiesMap, relatedMap);
+        super.update(context, batch, relatedMap);
 
-        reindex(context, entitiesMap.values());
+        reindex(context, batch.getObjects().values());
     }
 
     @Override
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/IVerifyEntityPropertyExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/IVerifyEntityPropertyExecutor.java
index 5a7a0d7dcf0358135c73f8c21ea47ec9b2f9203b..9848572024b266b76b5eb3c754432872d5f177dc 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/IVerifyEntityPropertyExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/IVerifyEntityPropertyExecutor.java
@@ -16,9 +16,8 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property;
 
-import java.util.Collection;
-
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.IEntityInformationWithPropertiesHolder;
 
 /**
@@ -27,6 +26,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.IEntityInformationWithPropert
 public interface IVerifyEntityPropertyExecutor
 {
 
-    public void verify(IOperationContext context, Collection<? extends IEntityInformationWithPropertiesHolder> entities);
+    public void verify(IOperationContext context, CollectionBatch<? extends IEntityInformationWithPropertiesHolder> batch);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/VerifyEntityPropertyExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/VerifyEntityPropertyExecutor.java
index b857a8749fd20979841e2743ba1d089e0d516a65..1263e5aa4e476bcdf699b0ad6e2d0e2360463e23 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/VerifyEntityPropertyExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/VerifyEntityPropertyExecutor.java
@@ -16,7 +16,6 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property;
 
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -24,7 +23,11 @@ import java.util.Map;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.VerifyEntityProgress;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.EntityPropertiesConverter;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePE;
@@ -58,16 +61,26 @@ public class VerifyEntityPropertyExecutor implements IVerifyEntityPropertyExecut
     }
 
     @Override
-    public void verify(IOperationContext context, Collection<? extends IEntityInformationWithPropertiesHolder> entities)
+    public void verify(IOperationContext context, CollectionBatch<? extends IEntityInformationWithPropertiesHolder> batch)
     {
         final Map<EntityTypePE, List<EntityTypePropertyTypePE>> cache =
                 new HashMap<EntityTypePE, List<EntityTypePropertyTypePE>>();
 
-        for (IEntityInformationWithPropertiesHolder entity : entities)
-        {
-            EntityPropertiesConverter converter = getEntityPropertiesConverter(entity.getEntityKind());
-            converter.checkMandatoryProperties(entity.getProperties(), entity.getEntityType(), cache);
-        }
+        new CollectionBatchProcessor<IEntityInformationWithPropertiesHolder>(context, batch)
+            {
+                @Override
+                public void process(IEntityInformationWithPropertiesHolder entity)
+                {
+                    EntityPropertiesConverter converter = getEntityPropertiesConverter(entity.getEntityKind());
+                    converter.checkMandatoryProperties(entity.getProperties(), entity.getEntityType(), cache);
+                }
+
+                @Override
+                public IProgress createProgress(IEntityInformationWithPropertiesHolder object, int objectIndex, int totalObjectCount)
+                {
+                    return new VerifyEntityProgress(objectIndex, totalObjectCount);
+                }
+            };
     }
 
     private EntityPropertiesConverter getEntityPropertiesConverter(EntityKind entityKindOrNull)
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/CreateSampleExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/CreateSampleExecutor.java
index 6aab3193741965de4e68d1f3f3efddc85599879a..1c5a49d79fec5597133b877e96792ea7e1dc92de 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/CreateSampleExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/CreateSampleExecutor.java
@@ -47,6 +47,8 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractCreat
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IMapEntityTypeByIdExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property.IUpdateEntityPropertyExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.tag.IAddTagToEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.common.properties.PropertyUtils;
 import ch.systemsx.cisd.common.spring.ExposablePropertyPlaceholderConfigurer;
@@ -111,12 +113,12 @@ public class CreateSampleExecutor extends AbstractCreateEntityExecutor<SampleCre
     private ExposablePropertyPlaceholderConfigurer configurer;
 
     @Override
-    protected List<SamplePE> createEntities(IOperationContext context, Collection<SampleCreation> creations)
+    protected List<SamplePE> createEntities(IOperationContext context, CollectionBatch<SampleCreation> batch)
     {
         // Get Types
         Collection<IEntityTypeId> typeIds = new HashSet<IEntityTypeId>();
 
-        for (SampleCreation creation : creations)
+        for (SampleCreation creation : batch.getObjects())
         {
             typeIds.add(creation.getTypeId());
         }
@@ -137,7 +139,7 @@ public class CreateSampleExecutor extends AbstractCreateEntityExecutor<SampleCre
 
         // Validate Sample creations using types
         Map<String, Integer> numCodesByPrefix = new HashMap<String, Integer>();
-        for (SampleCreation creation : creations)
+        for (SampleCreation creation : batch.getObjects())
         {
             SampleTypePE type = (SampleTypePE) types.get(creation.getTypeId());
             checkData(context, creation, type);
@@ -171,7 +173,7 @@ public class CreateSampleExecutor extends AbstractCreateEntityExecutor<SampleCre
         List<SamplePE> samples = new LinkedList<SamplePE>();
         PersonPE person = context.getSession().tryGetPerson();
         Date timeStamp = daoFactory.getTransactionTimestamp();
-        for (SampleCreation creation : creations)
+        for (SampleCreation creation : batch.getObjects())
         {
             SamplePE sample = new SamplePE();
             // Create code if is not present
@@ -233,21 +235,21 @@ public class CreateSampleExecutor extends AbstractCreateEntityExecutor<SampleCre
     }
 
     @Override
-    protected void checkBusinessRules(IOperationContext context, Collection<SamplePE> entities)
+    protected void checkBusinessRules(IOperationContext context, CollectionBatch<SamplePE> batch)
     {
-        verifySampleExecutor.verify(context, entities);
+        verifySampleExecutor.verify(context, batch);
     }
 
     @Override
-    protected void updateBatch(IOperationContext context, Map<SampleCreation, SamplePE> entitiesMap)
+    protected void updateBatch(IOperationContext context, MapBatch<SampleCreation, SamplePE> batch)
     {
-        setSampleSpaceExecutor.set(context, entitiesMap);
-        setSampleProjectExecutor.set(context, entitiesMap);
-        setSampleExperimentExecutor.set(context, entitiesMap);
-        setSampleTypeExecutor.set(context, entitiesMap);
+        setSampleSpaceExecutor.set(context, batch);
+        setSampleProjectExecutor.set(context, batch);
+        setSampleExperimentExecutor.set(context, batch);
+        setSampleTypeExecutor.set(context, batch);
 
         Map<IEntityPropertiesHolder, Map<String, String>> propertyMap = new HashMap<IEntityPropertiesHolder, Map<String, String>>();
-        for (Map.Entry<SampleCreation, SamplePE> entry : entitiesMap.entrySet())
+        for (Map.Entry<SampleCreation, SamplePE> entry : batch.getObjects().entrySet())
         {
             propertyMap.put(entry.getValue(), entry.getKey().getProperties());
         }
@@ -255,13 +257,13 @@ public class CreateSampleExecutor extends AbstractCreateEntityExecutor<SampleCre
     }
 
     @Override
-    protected void updateAll(IOperationContext context, Map<SampleCreation, SamplePE> entitiesMap)
+    protected void updateAll(IOperationContext context, MapBatch<SampleCreation, SamplePE> batch)
     {
         Map<AttachmentHolderPE, Collection<? extends AttachmentCreation>> attachmentMap =
                 new HashMap<AttachmentHolderPE, Collection<? extends AttachmentCreation>>();
         Map<IEntityWithMetaprojects, Collection<? extends ITagId>> tagMap = new HashMap<IEntityWithMetaprojects, Collection<? extends ITagId>>();
 
-        for (Map.Entry<SampleCreation, SamplePE> entry : entitiesMap.entrySet())
+        for (Map.Entry<SampleCreation, SamplePE> entry : batch.getObjects().entrySet())
         {
             SampleCreation creation = entry.getKey();
             SamplePE entity = entry.getValue();
@@ -271,7 +273,7 @@ public class CreateSampleExecutor extends AbstractCreateEntityExecutor<SampleCre
 
         createAttachmentExecutor.create(context, attachmentMap);
         addTagToEntityExecutor.add(context, tagMap);
-        setSampleRelatedSamplesExecutor.set(context, entitiesMap);
+        setSampleRelatedSamplesExecutor.set(context, batch);
     }
 
     @Override
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/ISetSampleContainerExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/ISetSampleContainerExecutor.java
index 3a397e3399dc677db009ca0fea581fdf4ca9256d..750e9ad1c42965f96536e8d7cfe11f28694d64f6 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/ISetSampleContainerExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/ISetSampleContainerExecutor.java
@@ -21,6 +21,7 @@ import java.util.Map;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.create.SampleCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.ISampleId;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
 
 /**
@@ -29,6 +30,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
 public interface ISetSampleContainerExecutor
 {
 
-    public void set(IOperationContext context, Map<SampleCreation, SamplePE> creationsMap, Map<ISampleId, SamplePE> sampleMap);
+    public void set(IOperationContext context, MapBatch<SampleCreation, SamplePE> batch, Map<ISampleId, SamplePE> sampleMap);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/IVerifySampleContainerExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/IVerifySampleContainerExecutor.java
index 158fe3c55e186b9b53d89f234d30a23bef9bb468..16f593259c204d9f3eee59ac3b915a17a34bea4b 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/IVerifySampleContainerExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/IVerifySampleContainerExecutor.java
@@ -16,9 +16,8 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.sample;
 
-import java.util.Collection;
-
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
 
 /**
@@ -27,6 +26,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
 public interface IVerifySampleContainerExecutor
 {
 
-    public void verify(IOperationContext context, Collection<SamplePE> samples);
+    public void verify(IOperationContext context, CollectionBatch<SamplePE> batch);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/IVerifySampleExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/IVerifySampleExecutor.java
index edc40584a0c4e161bc480c79de190d6f6b30b43a..245c5f90bba243329a60e79386d862de9424f1cb 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/IVerifySampleExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/IVerifySampleExecutor.java
@@ -16,9 +16,8 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.sample;
 
-import java.util.Collection;
-
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
 
 /**
@@ -27,6 +26,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
 public interface IVerifySampleExecutor
 {
 
-    public void verify(IOperationContext context, Collection<SamplePE> samples);
+    public void verify(IOperationContext context, CollectionBatch<SamplePE> batch);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/IVerifySampleExperimentExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/IVerifySampleExperimentExecutor.java
index 4b97f05c07a885bff53d46453018c366f5e447a3..84d7baf51ec1a004739ddc5dbb4deef3a16e8dce 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/IVerifySampleExperimentExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/IVerifySampleExperimentExecutor.java
@@ -16,9 +16,8 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.sample;
 
-import java.util.Collection;
-
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
 
 /**
@@ -27,6 +26,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
 public interface IVerifySampleExperimentExecutor
 {
 
-    public void verify(IOperationContext context, Collection<SamplePE> samples);
+    public void verify(IOperationContext context, CollectionBatch<SamplePE> batch);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/IVerifySampleProjectExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/IVerifySampleProjectExecutor.java
index e65a4078953f25b2213efe95408970429b180852..0be4cdaf664f1f35868a393bbb1d4831de12f7b9 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/IVerifySampleProjectExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/IVerifySampleProjectExecutor.java
@@ -16,9 +16,8 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.sample;
 
-import java.util.Collection;
-
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
 
 /**
@@ -26,5 +25,5 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
  */
 public interface IVerifySampleProjectExecutor
 {
-    public void verify(IOperationContext context, Collection<SamplePE> samples);
+    public void verify(IOperationContext context, CollectionBatch<SamplePE> batch);
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleChildrenExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleChildrenExecutor.java
index df144c9fb1346a4eb952f0e4f90d9a08d094cfba..b4dd9b1d8e2c43ad9556a3761dde2dd0e5d00159 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleChildrenExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleChildrenExecutor.java
@@ -24,7 +24,6 @@ import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.create.SampleCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.ISampleId;
-import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractSetEntityToManyRelationExecutor;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
@@ -37,6 +36,12 @@ public class SetSampleChildrenExecutor extends AbstractSetEntityToManyRelationEx
         ISetSampleChildrenExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "sample-children";
+    }
+
     @Override
     protected Collection<? extends ISampleId> getRelatedIds(IOperationContext context, SampleCreation creation)
     {
@@ -48,8 +53,6 @@ public class SetSampleChildrenExecutor extends AbstractSetEntityToManyRelationEx
     {
         Set<SamplePE> existingChildren = new HashSet<SamplePE>(parent.getChildren());
 
-        context.pushProgress(new Progress("set children for sample " + parent.getCode()));
-
         for (SamplePE child : children)
         {
             if (false == existingChildren.contains(child))
@@ -58,8 +61,6 @@ public class SetSampleChildrenExecutor extends AbstractSetEntityToManyRelationEx
                 existingChildren.add(child);
             }
         }
-
-        context.popProgress();
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleComponentsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleComponentsExecutor.java
index 558461a4fea2cd6d8285b52f1b3227ecbc61f8e5..0d2354dd2a880aa09ecf6ec9b8ef136b4f118eb6 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleComponentsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleComponentsExecutor.java
@@ -23,7 +23,6 @@ import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.create.SampleCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.ISampleId;
-import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractSetEntityToManyRelationExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IReindexEntityExecutor;
@@ -40,6 +39,12 @@ public class SetSampleComponentsExecutor extends AbstractSetEntityToManyRelation
     @Autowired
     private IReindexEntityExecutor reindexObjectExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "sample-components";
+    }
+
     @Override
     protected Collection<? extends ISampleId> getRelatedIds(IOperationContext context, SampleCreation creation)
     {
@@ -55,14 +60,10 @@ public class SetSampleComponentsExecutor extends AbstractSetEntityToManyRelation
     @Override
     protected void setRelated(IOperationContext context, SamplePE container, Collection<SamplePE> components)
     {
-        context.pushProgress(new Progress("set components for sample " + container.getCode()));
-
         for (SamplePE component : components)
         {
             relationshipService.assignSampleToContainer(context.getSession(), component, container);
         }
-
-        context.popProgress();
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleContainerExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleContainerExecutor.java
index 518284ac13bd1f33195baa0281523fc3fb6170de..48043d0d16cde6a36debdc8143a7dbf2a7781b00 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleContainerExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleContainerExecutor.java
@@ -24,8 +24,11 @@ import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.create.SampleCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.ISampleId;
-import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.SetEntityRelationProgress;
 import ch.systemsx.cisd.openbis.generic.server.ComponentNames;
 import ch.systemsx.cisd.openbis.generic.server.business.IRelationshipService;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
@@ -41,22 +44,27 @@ public class SetSampleContainerExecutor implements ISetSampleContainerExecutor
     private IRelationshipService relationshipService;
 
     @Override
-    public void set(IOperationContext context, Map<SampleCreation, SamplePE> creationsMap, Map<ISampleId, SamplePE> sampleMap)
+    public void set(final IOperationContext context, final MapBatch<SampleCreation, SamplePE> batch, final Map<ISampleId, SamplePE> sampleMap)
     {
-        for (SampleCreation creation : creationsMap.keySet())
-        {
-            context.pushProgress(new Progress("set container for sample " + creation.getCode()));
-
-            SamplePE sample = creationsMap.get(creation);
-            ISampleId containerId = creation.getContainerId();
-            if (containerId != null)
+        new MapBatchProcessor<SampleCreation, SamplePE>(context, batch)
             {
-                SamplePE container = sampleMap.get(containerId);
-                relationshipService.assignSampleToContainer(context.getSession(), sample, container);
-            }
+                @Override
+                public void process(SampleCreation creation, SamplePE sample)
+                {
+                    ISampleId containerId = creation.getContainerId();
+                    if (containerId != null)
+                    {
+                        SamplePE container = sampleMap.get(containerId);
+                        relationshipService.assignSampleToContainer(context.getSession(), sample, container);
+                    }
+                }
 
-            context.popProgress();
-        }
+                @Override
+                public IProgress createProgress(SampleCreation key, SamplePE value, int objectIndex, int totalObjectCount)
+                {
+                    return new SetEntityRelationProgress(key, "sample-container", objectIndex, totalObjectCount);
+                }
+            };
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleExperimentExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleExperimentExecutor.java
index 868a82e7ec3763dd360093c7267417e41d4da0a0..11849e40aadbb32240db9c28f810a4b3e4db102c 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleExperimentExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleExperimentExecutor.java
@@ -33,6 +33,12 @@ public class SetSampleExperimentExecutor extends AbstractSetEntityExperimentRela
         ISetSampleExperimentExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "sample-experiment";
+    }
+
     @Override
     protected IExperimentId getRelatedId(SampleCreation creation)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleParentsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleParentsExecutor.java
index 1a656f9d3e69697322efd3f6f152167e78fb5e7c..e2d1cfe5aa9137b6e15d61fdab75cbd54853a590 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleParentsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleParentsExecutor.java
@@ -24,7 +24,6 @@ import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.create.SampleCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.ISampleId;
-import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractSetEntityToManyRelationExecutor;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
@@ -37,6 +36,12 @@ public class SetSampleParentsExecutor extends AbstractSetEntityToManyRelationExe
         ISetSampleParentsExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "sample-parents";
+    }
+
     @Override
     protected Collection<? extends ISampleId> getRelatedIds(IOperationContext context, SampleCreation creation)
     {
@@ -48,8 +53,6 @@ public class SetSampleParentsExecutor extends AbstractSetEntityToManyRelationExe
     {
         Set<SamplePE> existingParents = new HashSet<SamplePE>(child.getParents());
 
-        context.pushProgress(new Progress("set parents for sample " + child.getCode()));
-
         for (SamplePE parent : parents)
         {
             if (false == existingParents.contains(parent))
@@ -58,7 +61,5 @@ public class SetSampleParentsExecutor extends AbstractSetEntityToManyRelationExe
                 existingParents.add(parent);
             }
         }
-
-        context.popProgress();
     }
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleProjectExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleProjectExecutor.java
index ab0301fd3a6b2306aaa12b96d7c5727896f76d7d..2422a75ad26a5a649041ae957df4848cc9480d91 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleProjectExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleProjectExecutor.java
@@ -42,6 +42,12 @@ public class SetSampleProjectExecutor extends AbstractSetEntityToOneRelationExec
     @Autowired
     private IMapProjectByIdExecutor mapProjectByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "sample-project";
+    }
+
     @Override
     protected IProjectId getRelatedId(SampleCreation creation)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleRelatedSamplesExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleRelatedSamplesExecutor.java
index 3462a9e381603c50daea772bb13439476f7448e5..d286e1bc8e0ea74a970433ae455df7989dc0d8bb 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleRelatedSamplesExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleRelatedSamplesExecutor.java
@@ -28,6 +28,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.ISampleId;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessException;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractSetEntityMultipleRelationsExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.server.authorization.validator.SampleByIdentiferValidator;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
 
@@ -86,12 +87,12 @@ public class SetSampleRelatedSamplesExecutor extends AbstractSetEntityMultipleRe
     }
 
     @Override
-    protected void set(IOperationContext context, Map<SampleCreation, SamplePE> creationsMap, Map<ISampleId, SamplePE> relatedMap)
+    protected void set(IOperationContext context, MapBatch<SampleCreation, SamplePE> batch, Map<ISampleId, SamplePE> relatedMap)
     {
-        setSampleContainerExecutor.set(context, creationsMap, relatedMap);
-        setSampleComponentsExecutor.set(context, creationsMap, relatedMap);
-        setSampleParentsExecutor.set(context, creationsMap, relatedMap);
-        setSampleChildrenExecutor.set(context, creationsMap, relatedMap);
+        setSampleContainerExecutor.set(context, batch, relatedMap);
+        setSampleComponentsExecutor.set(context, batch, relatedMap);
+        setSampleParentsExecutor.set(context, batch, relatedMap);
+        setSampleChildrenExecutor.set(context, batch, relatedMap);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleSpaceExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleSpaceExecutor.java
index 982dadccb77e463739e1decb9a2b903230a12afc..b5a9450ad35869ce572eaf3f23d829f88773f32f 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleSpaceExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleSpaceExecutor.java
@@ -43,6 +43,12 @@ public class SetSampleSpaceExecutor extends AbstractSetEntityToOneRelationExecut
     @Autowired
     private IMapSpaceByIdExecutor mapSpaceByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "sample-space";
+    }
+
     @Override
     protected ISpaceId getRelatedId(SampleCreation creation)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleTypeExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleTypeExecutor.java
index 7f93cd44220d221293414ff398f3bbe998450fc7..c5242aae31523022e19fd0567a6daeb6eee8074c 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleTypeExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/SetSampleTypeExecutor.java
@@ -44,6 +44,12 @@ public class SetSampleTypeExecutor extends AbstractSetEntityToOneRelationExecuto
     @Autowired
     private IMapEntityTypeByIdExecutor mapEntityTypeByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "sample-type";
+    }
+
     @Override
     protected IEntityTypeId getRelatedId(SampleCreation creation)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleChildrenExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleChildrenExecutor.java
index 7947193d1f4772718b65ec6705081bd897b5e702..26440902a82c0b22e4a75cf917d40e09bd86e7f7 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleChildrenExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleChildrenExecutor.java
@@ -37,6 +37,12 @@ public class UpdateSampleChildrenExecutor extends AbstractUpdateEntityToManyRela
         implements IUpdateSampleChildrenExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "sample-children";
+    }
+
     @Override
     protected Collection<SamplePE> getCurrentlyRelated(SamplePE entity)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleComponentsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleComponentsExecutor.java
index c63ece48da4693819d8c185e374c78ed4930c79a..300d38697177830c7d5871a12d52fc3a28b86333 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleComponentsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleComponentsExecutor.java
@@ -46,6 +46,12 @@ public class UpdateSampleComponentsExecutor extends AbstractUpdateEntityToManyRe
     @Autowired
     private IReindexEntityExecutor reindexObjectExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "sample-components";
+    }
+
     @Override
     protected Collection<SamplePE> getCurrentlyRelated(SamplePE entity)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleContainerExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleContainerExecutor.java
index d413c389e02e224ac09e99af8db35c0b51be577f..0ebd71850c81f8db6f459dc06fd65f899125514b 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleContainerExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleContainerExecutor.java
@@ -39,6 +39,12 @@ public class UpdateSampleContainerExecutor extends AbstractUpdateEntityToOneRela
         implements IUpdateSampleContainerExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "sample-containers";
+    }
+
     @Override
     protected ISampleId getRelatedId(SamplePE related)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleExecutor.java
index b936fbaa957855a426aaea970dd9c9373be10c56..3ed334058f7e5de66531645fe0d761a3c41d3d24 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleExecutor.java
@@ -32,6 +32,8 @@ import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessE
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractUpdateEntityExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.tag.IUpdateTagForEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.authorization.validator.SampleByIdentiferValidator;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.DataAccessExceptionTranslator;
@@ -104,22 +106,22 @@ public class UpdateSampleExecutor extends AbstractUpdateEntityExecutor<SampleUpd
     }
 
     @Override
-    protected void checkBusinessRules(IOperationContext context, Collection<SamplePE> entities)
+    protected void checkBusinessRules(IOperationContext context, CollectionBatch<SamplePE> batch)
     {
-        verifySampleExecutor.verify(context, entities);
+        verifySampleExecutor.verify(context, batch);
     }
 
     @Override
-    protected void updateBatch(IOperationContext context, Map<SampleUpdate, SamplePE> entitiesMap)
+    protected void updateBatch(IOperationContext context, MapBatch<SampleUpdate, SamplePE> batch)
     {
-        updateSampleSpaceExecutor.update(context, entitiesMap);
-        updateSampleProjectExecutor.update(context, entitiesMap);
-        updateSampleExperimentExecutor.update(context, entitiesMap);
+        updateSampleSpaceExecutor.update(context, batch);
+        updateSampleProjectExecutor.update(context, batch);
+        updateSampleExperimentExecutor.update(context, batch);
 
         Map<IEntityPropertiesHolder, Map<String, String>> propertyMap = new HashMap<IEntityPropertiesHolder, Map<String, String>>();
         PersonPE person = context.getSession().tryGetPerson();
         Date timeStamp = daoFactory.getTransactionTimestamp();
-        for (Map.Entry<SampleUpdate, SamplePE> entry : entitiesMap.entrySet())
+        for (Map.Entry<SampleUpdate, SamplePE> entry : batch.getObjects().entrySet())
         {
             SampleUpdate update = entry.getKey();
             SamplePE entity = entry.getValue();
@@ -145,9 +147,9 @@ public class UpdateSampleExecutor extends AbstractUpdateEntityExecutor<SampleUpd
     }
 
     @Override
-    protected void updateAll(IOperationContext context, Map<SampleUpdate, SamplePE> entitiesMap)
+    protected void updateAll(IOperationContext context, MapBatch<SampleUpdate, SamplePE> batch)
     {
-        updateSampleRelatedSamplesExecutor.update(context, entitiesMap);
+        updateSampleRelatedSamplesExecutor.update(context, batch);
     }
 
     @Override
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleExperimentExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleExperimentExecutor.java
index 266300706c972830398acec082f1b083731b938b..b8f918ddc15d40020a38d346e1986acd7f5f5368 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleExperimentExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleExperimentExecutor.java
@@ -51,6 +51,12 @@ public class UpdateSampleExperimentExecutor extends
     @Autowired
     private IVerifySampleDataSetsExecutor verifySampleDataSetsExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "sample-experiment";
+    }
+
     @Override
     protected IExperimentId getRelatedId(ExperimentPE related)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleParentsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleParentsExecutor.java
index 694558462c8cf7f127ca417dcb3845266015e770..2af144bdaaa3fbdb5310e58cfa42390613cfd075 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleParentsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleParentsExecutor.java
@@ -37,6 +37,12 @@ public class UpdateSampleParentsExecutor extends AbstractUpdateEntityToManyRelat
         implements IUpdateSampleParentsExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "sample-parents";
+    }
+
     @Override
     protected Collection<SamplePE> getCurrentlyRelated(SamplePE entity)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleProjectExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleProjectExecutor.java
index f30227d07ba3b3ca71a1e9192b6130209df610c9..afa66871e107800f46e9569d05a759416756b0b0 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleProjectExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleProjectExecutor.java
@@ -44,6 +44,12 @@ public class UpdateSampleProjectExecutor extends AbstractUpdateEntityToOneRelati
     @Autowired
     private IMapProjectByIdExecutor mapProjectByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "sample-project";
+    }
+
     @Override
     protected IProjectId getRelatedId(ProjectPE related)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleRelatedSamplesExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleRelatedSamplesExecutor.java
index 3f5a4f71502c30a7936b92489222229721e32645..b06f0a2d45ac977d4ccd78519d8c1ca8cdabb06c 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleRelatedSamplesExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleRelatedSamplesExecutor.java
@@ -27,6 +27,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.ISampleId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.update.SampleUpdate;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractUpdateEntityMultipleRelationsExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
 
 /**
@@ -68,12 +69,12 @@ public class UpdateSampleRelatedSamplesExecutor extends AbstractUpdateEntityMult
     }
 
     @Override
-    protected void update(IOperationContext context, Map<SampleUpdate, SamplePE> entitiesMap, Map<ISampleId, SamplePE> relatedMap)
+    protected void update(IOperationContext context, MapBatch<SampleUpdate, SamplePE> batch, Map<ISampleId, SamplePE> relatedMap)
     {
-        updateSampleContainerExecutor.update(context, entitiesMap, relatedMap);
-        updateSampleComponentsExecutor.update(context, entitiesMap, relatedMap);
-        updateSampleParentsExecutor.update(context, entitiesMap, relatedMap);
-        updateSampleChildrenExecutor.update(context, entitiesMap, relatedMap);
+        updateSampleContainerExecutor.update(context, batch, relatedMap);
+        updateSampleComponentsExecutor.update(context, batch, relatedMap);
+        updateSampleParentsExecutor.update(context, batch, relatedMap);
+        updateSampleChildrenExecutor.update(context, batch, relatedMap);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleSpaceExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleSpaceExecutor.java
index 5cc3ca932dd91956f3e97d002e545eacbb47e9a7..d810209193190641c424b657834b83505804f6b6 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleSpaceExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleSpaceExecutor.java
@@ -45,6 +45,12 @@ public class UpdateSampleSpaceExecutor extends AbstractUpdateEntityToOneRelation
     @Autowired
     private IMapSpaceByIdExecutor mapSpaceByIdExecutor;
 
+    @Override
+    protected String getRelationName()
+    {
+        return "sample-space";
+    }
+
     @Override
     protected ISpaceId getRelatedId(SpacePE related)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleContainerExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleContainerExecutor.java
index a4ff03757dc1d33352b3d8f4d5b9ab8d76ffa125..fc61273af9c62a258cd74677179d928c13afbfb2 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleContainerExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleContainerExecutor.java
@@ -16,11 +16,10 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.sample;
 
-import java.util.Collection;
-
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.SampleGenericBusinessRules;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
@@ -33,9 +32,9 @@ public class VerifySampleContainerExecutor implements IVerifySampleContainerExec
 {
 
     @Override
-    public void verify(IOperationContext context, Collection<SamplePE> samples)
+    public void verify(IOperationContext context, CollectionBatch<SamplePE> batch)
     {
-        for (SamplePE sample : samples)
+        for (SamplePE sample : batch.getObjects())
         {
             SamplePE containerCandidate = sample.getContainer();
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleExecutor.java
index 82ebd97c8c374cbcbd71535d09143724192516de..69b73ad5c77a218bc4b19187134aa76334b4419b 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleExecutor.java
@@ -16,13 +16,12 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.sample;
 
-import java.util.Collection;
-
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property.IVerifyEntityPropertyExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
 
 /**
@@ -35,8 +34,9 @@ public class VerifySampleExecutor implements IVerifySampleExecutor
     @Autowired
     private IVerifyEntityPropertyExecutor verifyEntityPropertyExecutor;
 
-    @Autowired
-    private IVerifySampleProjectExecutor verifySampleProjectExecutor;
+    // TODO: project samples
+    // @Autowired
+    // private IVerifySampleProjectExecutor verifySampleProjectExecutor;
 
     @Autowired
     private IVerifySampleExperimentExecutor verifySampleExperimentExecutor;
@@ -48,14 +48,14 @@ public class VerifySampleExecutor implements IVerifySampleExecutor
     private IVerifySampleParentsExecutor verifySampleParentsExecutor;
 
     @Override
-    public void verify(IOperationContext context, Collection<SamplePE> samples)
+    public void verify(IOperationContext context, CollectionBatch<SamplePE> batch)
     {
-        verifyEntityPropertyExecutor.verify(context, samples);
-        verifySampleExperimentExecutor.verify(context, samples);
+        verifyEntityPropertyExecutor.verify(context, batch);
+        verifySampleExperimentExecutor.verify(context, batch);
         // TODO: project samples
         // verifySampleProjectExecutor.verify(context, samples);
-        verifySampleContainerExecutor.verify(context, samples);
-        verifySampleParentsExecutor.verify(context, samples);
+        verifySampleContainerExecutor.verify(context, batch);
+        verifySampleParentsExecutor.verify(context, batch);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleExperimentExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleExperimentExecutor.java
index a7b98f2c03c6fb804143336c2d5277ac0f007d64..4a78806e68e4788611752f9ed94075f88092e75c 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleExperimentExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleExperimentExecutor.java
@@ -16,14 +16,16 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.sample;
 
-import java.util.Collection;
 import java.util.Map;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
-import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.VerifyEntityProgress;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDataDAO;
@@ -54,52 +56,58 @@ public class VerifySampleExperimentExecutor implements IVerifySampleExperimentEx
     }
 
     @Override
-    public void verify(IOperationContext context, Collection<SamplePE> samples)
+    public void verify(final IOperationContext context, final CollectionBatch<SamplePE> batch)
     {
         IDataDAO dataDAO = daoFactory.getDataDAO();
 
-        Map<SamplePE, Boolean> haveDatasetsMap = dataDAO.haveDataSets(samples);
+        final Map<SamplePE, Boolean> haveDatasetsMap = dataDAO.haveDataSets(batch.getObjects());
 
-        for (SamplePE sample : samples)
-        {
-            context.pushProgress(new Progress("verify experiment for sample " + sample.getCode()));
-
-            boolean hasDatasets = haveDatasetsMap.get(sample);
-            ExperimentPE experiment = sample.getExperiment();
-
-            if (experiment == null)
-            {
-                verifySampleDataSetsExecutor.checkDataSetsDoNotNeedAnExperiment(context, sample);
-            }
-
-            if (hasDatasets && sample.getSpace() == null)
-            {
-                throw UserFailureException.fromTemplate("Cannot detach the sample '%s' from the space "
-                        + "because there are already datasets attached to the sample.",
-                        sample.getIdentifier());
-            }
-
-            if (experiment != null && sample.getSpace() == null)
-            {
-                throw new UserFailureException("Shared samples cannot be attached to experiments. Sample: "
-                        + sample.getIdentifier() + ", Experiment: " + experiment.getIdentifier());
-            }
-
-            if (experiment != null && experiment.getProject().getSpace().equals(sample.getSpace()) == false)
+        new CollectionBatchProcessor<SamplePE>(context, batch)
             {
-                throw new UserFailureException("Sample space must be the same as experiment space. "
-                        + "Sample: " + sample.getIdentifier() + ", Experiment: " + experiment.getIdentifier());
-            }
-            if (experiment != null && sample.getProject() != null
-                    && experiment.getProject().equals(sample.getProject()) == false)
-            {
-                throw new UserFailureException("Sample project must be the same as experiment project. "
-                        + "Sample: " + sample.getIdentifier() + ", Project: " + sample.getProject().getIdentifier()
-                        + ", Experiment: " + experiment.getIdentifier());
-            }
-
-            context.popProgress();
-        }
+                @Override
+                public void process(SamplePE sample)
+                {
+                    boolean hasDatasets = haveDatasetsMap.get(sample);
+                    ExperimentPE experiment = sample.getExperiment();
+
+                    if (experiment == null)
+                    {
+                        verifySampleDataSetsExecutor.checkDataSetsDoNotNeedAnExperiment(context, sample);
+                    }
+
+                    if (hasDatasets && sample.getSpace() == null)
+                    {
+                        throw UserFailureException.fromTemplate("Cannot detach the sample '%s' from the space "
+                                + "because there are already datasets attached to the sample.",
+                                sample.getIdentifier());
+                    }
+
+                    if (experiment != null && sample.getSpace() == null)
+                    {
+                        throw new UserFailureException("Shared samples cannot be attached to experiments. Sample: "
+                                + sample.getIdentifier() + ", Experiment: " + experiment.getIdentifier());
+                    }
+
+                    if (experiment != null && experiment.getProject().getSpace().equals(sample.getSpace()) == false)
+                    {
+                        throw new UserFailureException("Sample space must be the same as experiment space. "
+                                + "Sample: " + sample.getIdentifier() + ", Experiment: " + experiment.getIdentifier());
+                    }
+                    if (experiment != null && sample.getProject() != null
+                            && experiment.getProject().equals(sample.getProject()) == false)
+                    {
+                        throw new UserFailureException("Sample project must be the same as experiment project. "
+                                + "Sample: " + sample.getIdentifier() + ", Project: " + sample.getProject().getIdentifier()
+                                + ", Experiment: " + experiment.getIdentifier());
+                    }
+                }
+
+                @Override
+                public IProgress createProgress(SamplePE object, int objectIndex, int totalObjectCount)
+                {
+                    return new VerifyEntityProgress(objectIndex, totalObjectCount);
+                }
+            };
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleParentsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleParentsExecutor.java
index 1fda953d4952fe77cd2a497a7479a1d14e5227c1..7cfaeb38289eed531a7d1876279c8b1145bd44ab 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleParentsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleParentsExecutor.java
@@ -16,7 +16,6 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.sample;
 
-import java.util.Collection;
 import java.util.Map;
 import java.util.Set;
 
@@ -27,6 +26,7 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractVerifyEntityCyclesExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.relationship.IGetRelationshipIdExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.relationship.IGetRelationshipIdExecutor.RelationshipType;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.SampleGenericBusinessRules;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
@@ -42,11 +42,11 @@ public class VerifySampleParentsExecutor extends AbstractVerifyEntityCyclesExecu
     private IGetRelationshipIdExecutor getRelationshipIdExecutor;
 
     @Override
-    public void verify(IOperationContext context, Collection<SamplePE> entities)
+    public void verify(IOperationContext context, CollectionBatch<SamplePE> batch)
     {
-        super.verify(context, entities);
+        super.verify(context, batch);
 
-        for (SamplePE sample : entities)
+        for (SamplePE sample : batch.getObjects())
         {
             SampleGenericBusinessRules.assertValidParents(sample);
             SampleGenericBusinessRules.assertValidChildren(sample);
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleProjectExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleProjectExecutor.java
index c6467e2d9da37851d5c49f9c441518aed2bc274e..5b7063d1aa3fe11da80a122a460257005629d259 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleProjectExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/VerifySampleProjectExecutor.java
@@ -16,12 +16,13 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.sample;
 
-import java.util.Collection;
-
 import org.springframework.stereotype.Component;
 
-import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.VerifyEntityProgress;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ProjectPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
@@ -34,24 +35,32 @@ public class VerifySampleProjectExecutor implements IVerifySampleProjectExecutor
 {
 
     @Override
-    public void verify(IOperationContext context, Collection<SamplePE> samples)
+    public void verify(final IOperationContext context, final CollectionBatch<SamplePE> batch)
     {
-        for (SamplePE sample : samples)
-        {
-            context.pushProgress(new Progress("verify project for sample " + sample.getCode()));
-            ProjectPE project = sample.getProject();
-            if (project != null && sample.getSpace() == null)
-            {
-                throw new UserFailureException("Shared samples cannot be attached to projects. Sample: "
-                        + sample.getIdentifier() + ", Project: " + project.getIdentifier());
-            }
-            if (project != null && project.getSpace().equals(sample.getSpace()) == false)
+        new CollectionBatchProcessor<SamplePE>(context, batch)
             {
-                throw new UserFailureException("Sample space must be the same as project space. Sample: "
-                        + sample.getIdentifier() + ", Project: " + project.getIdentifier());
-            }
-            context.popProgress();
-        }
+                @Override
+                public void process(SamplePE sample)
+                {
+                    ProjectPE project = sample.getProject();
+                    if (project != null && sample.getSpace() == null)
+                    {
+                        throw new UserFailureException("Shared samples cannot be attached to projects. Sample: "
+                                + sample.getIdentifier() + ", Project: " + project.getIdentifier());
+                    }
+                    if (project != null && project.getSpace().equals(sample.getSpace()) == false)
+                    {
+                        throw new UserFailureException("Sample space must be the same as project space. Sample: "
+                                + sample.getIdentifier() + ", Project: " + project.getIdentifier());
+                    }
+                }
+
+                @Override
+                public IProgress createProgress(SamplePE object, int objectIndex, int totalObjectCount)
+                {
+                    return new VerifyEntityProgress(objectIndex, totalObjectCount);
+                }
+            };
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/space/CreateSpaceExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/space/CreateSpaceExecutor.java
index d7e12cb601477d6cac1e691862c5af83a72cf89d..4e908e62a63f48d8485dc5952bbc669f3b3747a0 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/space/CreateSpaceExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/space/CreateSpaceExecutor.java
@@ -19,7 +19,6 @@ package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.space;
 import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 
 import javax.annotation.Resource;
 
@@ -30,8 +29,13 @@ import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.create.SpaceCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.id.SpacePermId;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractCreateEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.CreateEntityProgress;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.ComponentNames;
 import ch.systemsx.cisd.openbis.generic.server.authorization.AuthorizationServiceUtils;
@@ -63,18 +67,28 @@ public class CreateSpaceExecutor extends AbstractCreateEntityExecutor<SpaceCreat
     private IDAOFactory daoFactory;
 
     @Override
-    protected List<SpacePE> createEntities(IOperationContext context, Collection<SpaceCreation> creations)
+    protected List<SpacePE> createEntities(final IOperationContext context, CollectionBatch<SpaceCreation> batch)
     {
-        List<SpacePE> spaces = new LinkedList<SpacePE>();
+        final List<SpacePE> spaces = new LinkedList<SpacePE>();
 
-        for (SpaceCreation creation : creations)
-        {
-            SpacePE space = new SpacePE();
-            space.setCode(creation.getCode());
-            space.setDescription(creation.getDescription());
-            space.setRegistrator(context.getSession().tryGetPerson());
-            spaces.add(space);
-        }
+        new CollectionBatchProcessor<SpaceCreation>(context, batch)
+            {
+                @Override
+                public void process(SpaceCreation object)
+                {
+                    SpacePE space = new SpacePE();
+                    space.setCode(object.getCode());
+                    space.setDescription(object.getDescription());
+                    space.setRegistrator(context.getSession().tryGetPerson());
+                    spaces.add(space);
+                }
+
+                @Override
+                public IProgress createProgress(SpaceCreation object, int objectIndex, int totalObjectCount)
+                {
+                    return new CreateEntityProgress(object, objectIndex, totalObjectCount);
+                }
+            };
 
         return spaces;
     }
@@ -103,19 +117,19 @@ public class CreateSpaceExecutor extends AbstractCreateEntityExecutor<SpaceCreat
     }
 
     @Override
-    protected void checkBusinessRules(IOperationContext context, Collection<SpacePE> entities)
+    protected void checkBusinessRules(IOperationContext context, CollectionBatch<SpacePE> batch)
     {
         // nothing to do
     }
 
     @Override
-    protected void updateBatch(IOperationContext context, Map<SpaceCreation, SpacePE> entitiesMap)
+    protected void updateBatch(IOperationContext context, MapBatch<SpaceCreation, SpacePE> batch)
     {
         // nothing to do
     }
 
     @Override
-    protected void updateAll(IOperationContext context, Map<SpaceCreation, SpacePE> entitiesMap)
+    protected void updateAll(IOperationContext context, MapBatch<SpaceCreation, SpacePE> batch)
     {
         // nothing to do
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/space/UpdateSpaceExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/space/UpdateSpaceExecutor.java
index 1255b1dd52ee0a1d33bb3e0a44044233c77f32c0..a36de84917867ad705fd248fb6ffebb5cffca047 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/space/UpdateSpaceExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/space/UpdateSpaceExecutor.java
@@ -29,6 +29,8 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.update.SpaceUpdate;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessException;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractUpdateEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.authorization.validator.SimpleSpaceValidator;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.DataAccessExceptionTranslator;
@@ -74,15 +76,15 @@ public class UpdateSpaceExecutor extends AbstractUpdateEntityExecutor<SpaceUpdat
     }
 
     @Override
-    protected void checkBusinessRules(IOperationContext context, Collection<SpacePE> entities)
+    protected void checkBusinessRules(IOperationContext context, CollectionBatch<SpacePE> batch)
     {
         // nothing to do
     }
 
     @Override
-    protected void updateBatch(IOperationContext context, Map<SpaceUpdate, SpacePE> entitiesMap)
+    protected void updateBatch(IOperationContext context, MapBatch<SpaceUpdate, SpacePE> batch)
     {
-        for (Map.Entry<SpaceUpdate, SpacePE> entry : entitiesMap.entrySet())
+        for (Map.Entry<SpaceUpdate, SpacePE> entry : batch.getObjects().entrySet())
         {
             SpaceUpdate update = entry.getKey();
             SpacePE space = entry.getValue();
@@ -95,7 +97,7 @@ public class UpdateSpaceExecutor extends AbstractUpdateEntityExecutor<SpaceUpdat
     }
 
     @Override
-    protected void updateAll(IOperationContext context, Map<SpaceUpdate, SpacePE> entitiesMap)
+    protected void updateAll(IOperationContext context, MapBatch<SpaceUpdate, SpacePE> batch)
     {
         // nothing to do
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/CreateTagExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/CreateTagExecutor.java
index 593d39c122684556bbfaa98d5d88e0b20c61dde5..686070470c4968a699c65f08d06e49cd2a51772e 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/CreateTagExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/CreateTagExecutor.java
@@ -19,7 +19,6 @@ package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.tag;
 import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 
 import org.apache.commons.lang.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -28,8 +27,13 @@ import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.create.TagCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.TagPermId;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractCreateEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatchProcessor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress.CreateEntityProgress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.tag.TagAuthorization;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.DataAccessExceptionTranslator;
@@ -59,19 +63,29 @@ public class CreateTagExecutor extends AbstractCreateEntityExecutor<TagCreation,
     private ISetTagMaterialsExecutor setTagMaterialsExecutor;
 
     @Override
-    protected List<MetaprojectPE> createEntities(IOperationContext context, Collection<TagCreation> creations)
+    protected List<MetaprojectPE> createEntities(final IOperationContext context, CollectionBatch<TagCreation> batch)
     {
-        List<MetaprojectPE> tags = new LinkedList<MetaprojectPE>();
-
-        for (TagCreation creation : creations)
-        {
-            MetaprojectPE tag = new MetaprojectPE();
-            tag.setName(creation.getCode());
-            tag.setDescription(creation.getDescription());
-            tag.setOwner(context.getSession().tryGetPerson());
-            tag.setPrivate(true);
-            tags.add(tag);
-        }
+        final List<MetaprojectPE> tags = new LinkedList<MetaprojectPE>();
+
+        new CollectionBatchProcessor<TagCreation>(context, batch)
+            {
+                @Override
+                public void process(TagCreation object)
+                {
+                    MetaprojectPE tag = new MetaprojectPE();
+                    tag.setName(object.getCode());
+                    tag.setDescription(object.getDescription());
+                    tag.setOwner(context.getSession().tryGetPerson());
+                    tag.setPrivate(true);
+                    tags.add(tag);
+                }
+
+                @Override
+                public IProgress createProgress(TagCreation object, int objectIndex, int totalObjectCount)
+                {
+                    return new CreateEntityProgress(object, objectIndex, totalObjectCount);
+                }
+            };
 
         return tags;
     }
@@ -98,24 +112,24 @@ public class CreateTagExecutor extends AbstractCreateEntityExecutor<TagCreation,
     }
 
     @Override
-    protected void checkBusinessRules(IOperationContext context, Collection<MetaprojectPE> entities)
+    protected void checkBusinessRules(IOperationContext context, CollectionBatch<MetaprojectPE> batch)
     {
         // nothing to do
     }
 
     @Override
-    protected void updateBatch(IOperationContext context, Map<TagCreation, MetaprojectPE> entitiesMap)
+    protected void updateBatch(IOperationContext context, MapBatch<TagCreation, MetaprojectPE> batch)
     {
         // nothing to do
     }
 
     @Override
-    protected void updateAll(IOperationContext context, Map<TagCreation, MetaprojectPE> entitiesMap)
+    protected void updateAll(IOperationContext context, MapBatch<TagCreation, MetaprojectPE> batch)
     {
-        setTagExperimentsExecutor.set(context, entitiesMap);
-        setTagSamplesExecutor.set(context, entitiesMap);
-        setTagDataSetsExecutor.set(context, entitiesMap);
-        setTagMaterialsExecutor.set(context, entitiesMap);
+        setTagExperimentsExecutor.set(context, batch);
+        setTagSamplesExecutor.set(context, batch);
+        setTagDataSetsExecutor.set(context, batch);
+        setTagMaterialsExecutor.set(context, batch);
     }
 
     @Override
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagDataSetsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagDataSetsExecutor.java
index 8ad7961affd420f77f447290a4c894d37f8d8a35..e84fdca3943ce610c36b393fcdc0f166178e077a 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagDataSetsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagDataSetsExecutor.java
@@ -29,6 +29,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessE
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.dataset.IMapDataSetByIdExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractSetEntityMultipleRelationsExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.server.authorization.validator.DataSetPEByExperimentOrSampleIdentifierValidator;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
@@ -76,9 +77,9 @@ public class SetTagDataSetsExecutor extends AbstractSetEntityMultipleRelationsEx
     }
 
     @Override
-    protected void set(IOperationContext context, Map<TagCreation, MetaprojectPE> creationsMap, Map<IDataSetId, DataPE> relatedMap)
+    protected void set(IOperationContext context, MapBatch<TagCreation, MetaprojectPE> batch, Map<IDataSetId, DataPE> relatedMap)
     {
-        setTagDataSetsWithCacheExecutor.set(context, creationsMap, relatedMap);
+        setTagDataSetsWithCacheExecutor.set(context, batch, relatedMap);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagDataSetsWithCacheExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagDataSetsWithCacheExecutor.java
index 1a431e556f3cbc751c519add35efae7633e9fb7c..9349f2b47f285178e056a22268d1b9140ef3a022 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagDataSetsWithCacheExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagDataSetsWithCacheExecutor.java
@@ -33,6 +33,12 @@ public class SetTagDataSetsWithCacheExecutor extends SetTagEntitiesWithCacheExec
         implements ISetTagDataSetsWithCacheExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "tag-datasets";
+    }
+
     @Override
     protected Class<DataPE> getRelatedClass()
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagExperimentsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagExperimentsExecutor.java
index de756c9498013ce9c3028ff8c64adb151f7e22ba..c02e9e9b3b913203f730d3e78696e3afb329638f 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagExperimentsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagExperimentsExecutor.java
@@ -29,6 +29,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessE
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractSetEntityMultipleRelationsExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.experiment.IMapExperimentByIdExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.server.authorization.validator.ExperimentByIdentiferValidator;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
@@ -75,9 +76,9 @@ public class SetTagExperimentsExecutor extends AbstractSetEntityMultipleRelation
     }
 
     @Override
-    protected void set(IOperationContext context, Map<TagCreation, MetaprojectPE> creationsMap, Map<IExperimentId, ExperimentPE> relatedMap)
+    protected void set(IOperationContext context, MapBatch<TagCreation, MetaprojectPE> batch, Map<IExperimentId, ExperimentPE> relatedMap)
     {
-        setTagExperimentsWithCacheExecutor.set(context, creationsMap, relatedMap);
+        setTagExperimentsWithCacheExecutor.set(context, batch, relatedMap);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagExperimentsWithCacheExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagExperimentsWithCacheExecutor.java
index 20a763b243b7b430baa958ad005f18d4b3f45bbf..4629251c81ba49183ea0272f31d7f38d78ef0de8 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagExperimentsWithCacheExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagExperimentsWithCacheExecutor.java
@@ -33,6 +33,12 @@ public class SetTagExperimentsWithCacheExecutor extends SetTagEntitiesWithCacheE
         implements ISetTagExperimentsWithCacheExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "tag-experiments";
+    }
+
     @Override
     protected Class<ExperimentPE> getRelatedClass()
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagMaterialsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagMaterialsExecutor.java
index 8ed55514c1b3d5a3d1be9de10428550e0b2396a6..3804f93b16e1b5965cc4df038ca5ecfbdfc1ade9 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagMaterialsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagMaterialsExecutor.java
@@ -28,6 +28,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.create.TagCreation;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractSetEntityMultipleRelationsExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.material.IMapMaterialByIdExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 
@@ -70,9 +71,9 @@ public class SetTagMaterialsExecutor extends AbstractSetEntityMultipleRelationsE
     }
 
     @Override
-    protected void set(IOperationContext context, Map<TagCreation, MetaprojectPE> creationsMap, Map<IMaterialId, MaterialPE> relatedMap)
+    protected void set(IOperationContext context, MapBatch<TagCreation, MetaprojectPE> batch, Map<IMaterialId, MaterialPE> relatedMap)
     {
-        setTagMaterialsWithCacheExecutor.set(context, creationsMap, relatedMap);
+        setTagMaterialsWithCacheExecutor.set(context, batch, relatedMap);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagMaterialsWithCacheExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagMaterialsWithCacheExecutor.java
index 06a6db69d02d1862d87f1744f9eecfb174e35aa4..46d0f54d961cab1bef3d3cc85929a244b2134938 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagMaterialsWithCacheExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagMaterialsWithCacheExecutor.java
@@ -33,6 +33,12 @@ public class SetTagMaterialsWithCacheExecutor extends SetTagEntitiesWithCacheExe
         implements ISetTagMaterialsWithCacheExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "tag-materials";
+    }
+
     @Override
     protected Class<MaterialPE> getRelatedClass()
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagSamplesExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagSamplesExecutor.java
index f4533130cef12d80fc8d470268a35edb5f2bddf0..87b5dbcfb99821587ae086e74b98df13a227edfe 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagSamplesExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagSamplesExecutor.java
@@ -29,6 +29,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessE
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractSetEntityMultipleRelationsExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.sample.IMapSampleByIdExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.server.authorization.validator.SampleByIdentiferValidator;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
@@ -75,9 +76,9 @@ public class SetTagSamplesExecutor extends AbstractSetEntityMultipleRelationsExe
     }
 
     @Override
-    protected void set(IOperationContext context, Map<TagCreation, MetaprojectPE> creationsMap, Map<ISampleId, SamplePE> relatedMap)
+    protected void set(IOperationContext context, MapBatch<TagCreation, MetaprojectPE> batch, Map<ISampleId, SamplePE> relatedMap)
     {
-        setTagSamplesWithCacheExecutor.set(context, creationsMap, relatedMap);
+        setTagSamplesWithCacheExecutor.set(context, batch, relatedMap);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagSamplesWithCacheExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagSamplesWithCacheExecutor.java
index ffea2d97df178bc1efa56c62501f2d7e1d732775..b5ccabdc73b25613c9e43729b98934900db45cf0 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagSamplesWithCacheExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagSamplesWithCacheExecutor.java
@@ -33,6 +33,12 @@ public class SetTagSamplesWithCacheExecutor extends SetTagEntitiesWithCacheExecu
         implements ISetTagSamplesWithCacheExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "tag-samples";
+    }
+
     @Override
     protected Class<SamplePE> getRelatedClass()
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagDataSetsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagDataSetsExecutor.java
index 3c7b8919a3fe76594b7587a1e94ac7493c4543b3..2fa2b13bf9bed151db5afcd0f7da7171643ec518 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagDataSetsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagDataSetsExecutor.java
@@ -28,6 +28,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.dataset.IMapDataSetByIdExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractUpdateEntityMultipleRelationsExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 
@@ -58,9 +59,9 @@ public class UpdateTagDataSetsExecutor extends AbstractUpdateEntityMultipleRelat
     }
 
     @Override
-    protected void update(IOperationContext context, Map<TagUpdate, MetaprojectPE> entitiesMap, Map<IDataSetId, DataPE> relatedMap)
+    protected void update(IOperationContext context, MapBatch<TagUpdate, MetaprojectPE> batch, Map<IDataSetId, DataPE> relatedMap)
     {
-        updateTagDataSetsWithCacheExecutor.update(context, entitiesMap, relatedMap);
+        updateTagDataSetsWithCacheExecutor.update(context, batch, relatedMap);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagDataSetsWithCacheExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagDataSetsWithCacheExecutor.java
index a9f8ce597d4500f1b3d6d56f12b59821035e46ed..e8ff4465332de55493dbb069e78a5056a1afc6fd 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagDataSetsWithCacheExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagDataSetsWithCacheExecutor.java
@@ -36,6 +36,12 @@ public class UpdateTagDataSetsWithCacheExecutor extends UpdateTagEntitiesWithCac
         IUpdateTagDataSetsWithCacheExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "tag-datasets";
+    }
+
     @Override
     protected Class<DataPE> getRelatedClass()
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExecutor.java
index 586cb6d03bd3ff30f11a83296d0cece5e51793d4..a644633deb8dc7c62d6b70e6b3f14c9f2c5f524e 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExecutor.java
@@ -29,6 +29,8 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessException;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractUpdateEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.tag.TagAuthorization;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.DataAccessExceptionTranslator;
@@ -86,15 +88,15 @@ public class UpdateTagExecutor extends AbstractUpdateEntityExecutor<TagUpdate, M
     }
 
     @Override
-    protected void checkBusinessRules(IOperationContext context, Collection<MetaprojectPE> entities)
+    protected void checkBusinessRules(IOperationContext context, CollectionBatch<MetaprojectPE> batch)
     {
         // nothing to do
     }
 
     @Override
-    protected void updateBatch(IOperationContext context, Map<TagUpdate, MetaprojectPE> entitiesMap)
+    protected void updateBatch(IOperationContext context, MapBatch<TagUpdate, MetaprojectPE> batch)
     {
-        for (Map.Entry<TagUpdate, MetaprojectPE> entry : entitiesMap.entrySet())
+        for (Map.Entry<TagUpdate, MetaprojectPE> entry : batch.getObjects().entrySet())
         {
             TagUpdate update = entry.getKey();
             MetaprojectPE tag = entry.getValue();
@@ -107,12 +109,12 @@ public class UpdateTagExecutor extends AbstractUpdateEntityExecutor<TagUpdate, M
     }
 
     @Override
-    protected void updateAll(IOperationContext context, Map<TagUpdate, MetaprojectPE> entitiesMap)
+    protected void updateAll(IOperationContext context, MapBatch<TagUpdate, MetaprojectPE> batch)
     {
-        updateTagExperimentsExecutor.update(context, entitiesMap);
-        updateTagSamplesExecutor.update(context, entitiesMap);
-        updateTagDataSetsExecutor.update(context, entitiesMap);
-        updateTagMaterialsExecutor.update(context, entitiesMap);
+        updateTagExperimentsExecutor.update(context, batch);
+        updateTagSamplesExecutor.update(context, batch);
+        updateTagDataSetsExecutor.update(context, batch);
+        updateTagMaterialsExecutor.update(context, batch);
     }
 
     @Override
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExperimentsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExperimentsExecutor.java
index 7c58a8de90447eb26f3059550bd7a50495f883c7..32d57941642cc6b49ea8d91ed60ad94ca10aa156 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExperimentsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExperimentsExecutor.java
@@ -28,6 +28,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractUpdateEntityMultipleRelationsExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.experiment.IMapExperimentByIdExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 
@@ -59,9 +60,9 @@ public class UpdateTagExperimentsExecutor extends
     }
 
     @Override
-    protected void update(IOperationContext context, Map<TagUpdate, MetaprojectPE> entitiesMap, Map<IExperimentId, ExperimentPE> relatedMap)
+    protected void update(IOperationContext context, MapBatch<TagUpdate, MetaprojectPE> batch, Map<IExperimentId, ExperimentPE> relatedMap)
     {
-        updateTagExperimentsWithCacheExecutor.update(context, entitiesMap, relatedMap);
+        updateTagExperimentsWithCacheExecutor.update(context, batch, relatedMap);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExperimentsWithCacheExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExperimentsWithCacheExecutor.java
index ab7bb302cf442b76a63c0e555fd76de3f55ba082..ec996590f7f910b66b103ab4fb5868513a5c1025 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExperimentsWithCacheExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExperimentsWithCacheExecutor.java
@@ -36,6 +36,12 @@ public class UpdateTagExperimentsWithCacheExecutor extends UpdateTagEntitiesWith
         implements IUpdateTagExperimentsWithCacheExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "tag-experiments";
+    }
+
     @Override
     protected Class<ExperimentPE> getRelatedClass()
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagMaterialsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagMaterialsExecutor.java
index 1c570ec5c892153f4d8c0801fbfe494be956dfca..f79e17c288744b20c53d2421eac638153ab3c037 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagMaterialsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagMaterialsExecutor.java
@@ -28,6 +28,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractUpdateEntityMultipleRelationsExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.material.IMapMaterialByIdExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 
@@ -58,9 +59,9 @@ public class UpdateTagMaterialsExecutor extends AbstractUpdateEntityMultipleRela
     }
 
     @Override
-    protected void update(IOperationContext context, Map<TagUpdate, MetaprojectPE> entitiesMap, Map<IMaterialId, MaterialPE> relatedMap)
+    protected void update(IOperationContext context, MapBatch<TagUpdate, MetaprojectPE> batch, Map<IMaterialId, MaterialPE> relatedMap)
     {
-        updateTagMaterialsWithCacheExecutor.update(context, entitiesMap, relatedMap);
+        updateTagMaterialsWithCacheExecutor.update(context, batch, relatedMap);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagMaterialsWithCacheExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagMaterialsWithCacheExecutor.java
index 55eddd259f8ba56c6a4c0ad55167cb151cce8285..1da48c895534477a9c611c57bcf337f1b6e7144c 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagMaterialsWithCacheExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagMaterialsWithCacheExecutor.java
@@ -34,6 +34,12 @@ public class UpdateTagMaterialsWithCacheExecutor extends UpdateTagEntitiesWithCa
         implements IUpdateTagMaterialsWithCacheExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "tag-materials";
+    }
+
     @Override
     protected Class<MaterialPE> getRelatedClass()
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagSamplesExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagSamplesExecutor.java
index f95cf47f047f96aa5ad86704f00422a51b56b98e..437904a238237ebb75d567a8a196af264c8a29c8 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagSamplesExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagSamplesExecutor.java
@@ -28,6 +28,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractUpdateEntityMultipleRelationsExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.sample.IMapSampleByIdExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
 
@@ -58,9 +59,9 @@ public class UpdateTagSamplesExecutor extends AbstractUpdateEntityMultipleRelati
     }
 
     @Override
-    protected void update(IOperationContext context, Map<TagUpdate, MetaprojectPE> entitiesMap, Map<ISampleId, SamplePE> relatedMap)
+    protected void update(IOperationContext context, MapBatch<TagUpdate, MetaprojectPE> batch, Map<ISampleId, SamplePE> relatedMap)
     {
-        updateTagSamplesWithCacheExecutor.update(context, entitiesMap, relatedMap);
+        updateTagSamplesWithCacheExecutor.update(context, batch, relatedMap);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagSamplesWithCacheExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagSamplesWithCacheExecutor.java
index 78b1632a78b3ac4710cc5e4370a62de5a9fc03dc..cea8ae0b5ca45e881e51df46a4417ac09278b70f 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagSamplesWithCacheExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagSamplesWithCacheExecutor.java
@@ -36,6 +36,12 @@ public class UpdateTagSamplesWithCacheExecutor extends UpdateTagEntitiesWithCach
         implements IUpdateTagSamplesWithCacheExecutor
 {
 
+    @Override
+    protected String getRelationName()
+    {
+        return "tag-samples";
+    }
+
     @Override
     protected Class<SamplePE> getRelatedClass()
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/common/batch/Batch.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/common/batch/Batch.java
new file mode 100644
index 0000000000000000000000000000000000000000..0c022950181513c679b86da94ce065cacce89a3f
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/common/batch/Batch.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2016 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.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+ * @author pkupczyk
+ */
+public abstract class Batch<B>
+{
+
+    public static final int DEFAULT_BATCH_SIZE = 1000;
+
+    private int batchIndex;
+
+    private int fromObjectIndex;
+
+    private int toObjectIndex;
+
+    private B objects;
+
+    private int totalObjectCount;
+
+    public Batch(int batchIndex, int fromObjectIndex, int toObjectIndex, B objects, int totalObjectCount)
+    {
+        this.batchIndex = batchIndex;
+        this.fromObjectIndex = fromObjectIndex;
+        this.toObjectIndex = toObjectIndex;
+        this.objects = objects;
+        this.totalObjectCount = totalObjectCount;
+    }
+
+    public int getBatchIndex()
+    {
+        return batchIndex;
+    }
+
+    public int getFromObjectIndex()
+    {
+        return fromObjectIndex;
+    }
+
+    public int getToObjectIndex()
+    {
+        return toObjectIndex;
+    }
+
+    public B getObjects()
+    {
+        return objects;
+    }
+
+    public int getTotalObjectCount()
+    {
+        return totalObjectCount;
+    }
+
+    public static <T> Collection<CollectionBatch<T>> createBatches(Collection<T> objects)
+    {
+        if (objects == null || objects.isEmpty())
+        {
+            return Collections.emptyList();
+        }
+
+        Collection<CollectionBatch<T>> batches = new LinkedList<CollectionBatch<T>>();
+        Iterator<T> iterator = objects.iterator();
+        int batchIndex = 0;
+        int batchSize = DEFAULT_BATCH_SIZE;
+
+        for (int batchStart = 0; batchStart < objects.size(); batchStart += batchSize)
+        {
+            int batchFinish = Math.min(batchStart + batchSize, objects.size());
+            Collection<T> batchObjects = new ArrayList<T>(batchSize);
+
+            for (int objectIndex = batchStart; objectIndex < batchFinish; objectIndex++)
+            {
+                batchObjects.add(iterator.next());
+            }
+
+            CollectionBatch<T> batch =
+                    new CollectionBatch<T>(batchIndex, batchStart, batchFinish, batchObjects, objects.size());
+
+            batches.add(batch);
+            batchIndex++;
+        }
+
+        return batches;
+    }
+
+    public static <K, V> Collection<MapBatch<K, V>> createBatches(Map<K, V> objects)
+    {
+        if (objects == null || objects.isEmpty())
+        {
+            return Collections.emptyList();
+        }
+
+        Collection<MapBatch<K, V>> batches = new LinkedList<MapBatch<K, V>>();
+        Iterator<Map.Entry<K, V>> iterator = objects.entrySet().iterator();
+        int batchIndex = 0;
+        int batchSize = DEFAULT_BATCH_SIZE;
+
+        for (int batchStart = 0; batchStart < objects.size(); batchStart += batchSize)
+        {
+            int batchFinish = Math.min(batchStart + batchSize, objects.size());
+            Map<K, V> batchObjects = new HashMap<K, V>(batchSize);
+
+            for (int objectIndex = batchStart; objectIndex < batchFinish; objectIndex++)
+            {
+                Map.Entry<K, V> entry = iterator.next();
+                batchObjects.put(entry.getKey(), entry.getValue());
+            }
+
+            MapBatch<K, V> batch =
+                    new MapBatch<K, V>(batchIndex, batchStart, batchFinish, batchObjects, objects.size());
+
+            batches.add(batch);
+            batchIndex++;
+        }
+
+        return batches;
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/common/batch/CollectionBatch.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/common/batch/CollectionBatch.java
new file mode 100644
index 0000000000000000000000000000000000000000..300dd5a22968a4afbae3a6cf9831703c5b7498de
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/common/batch/CollectionBatch.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 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.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch;
+
+import java.util.Collection;
+
+/**
+ * @author pkupczyk
+ */
+public class CollectionBatch<T> extends Batch<Collection<T>>
+{
+
+    public CollectionBatch(int batchIndex, int fromObjectIndex, int toObjectIndex, Collection<T> objects, int totalObjectCount)
+    {
+        super(batchIndex, fromObjectIndex, toObjectIndex, objects, totalObjectCount);
+    }
+
+    public boolean isEmpty()
+    {
+        return getObjects() == null || getObjects().isEmpty();
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/common/batch/CollectionBatchProcessor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/common/batch/CollectionBatchProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..26f9ab0ddfb5b4770bfb3ce3d18d32cd4cc957d0
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/common/batch/CollectionBatchProcessor.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2016 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.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch;
+
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.LazyLoadedProgress;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+
+/**
+ * @author pkupczyk
+ */
+public abstract class CollectionBatchProcessor<T>
+{
+
+    public abstract void process(T object);
+
+    public abstract IProgress createProgress(T object, int objectIndex, int totalObjectCount);
+
+    public CollectionBatchProcessor(IOperationContext context, CollectionBatch<? extends T> batch)
+    {
+        int objectIndex = batch.getFromObjectIndex() + 1;
+        int totalObjectCount = batch.getTotalObjectCount();
+
+        for (T object : batch.getObjects())
+        {
+            IProgress progress = new CollectionBatchLazyLoadedProgress(object, objectIndex, totalObjectCount);
+            context.pushProgress(progress);
+            process(object);
+            context.popProgress();
+            objectIndex++;
+        }
+    }
+
+    private class CollectionBatchLazyLoadedProgress extends LazyLoadedProgress
+    {
+
+        private static final long serialVersionUID = 1L;
+
+        private T object;
+
+        private int objectIndex;
+
+        private int totalObjectCount;
+
+        public CollectionBatchLazyLoadedProgress(T object, int objectIndex, int totalObjectCount)
+        {
+            this.object = object;
+            this.objectIndex = objectIndex;
+            this.totalObjectCount = totalObjectCount;
+        }
+
+        @Override
+        protected IProgress load()
+        {
+            return createProgress(object, objectIndex, totalObjectCount);
+        }
+
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/common/batch/MapBatch.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/common/batch/MapBatch.java
new file mode 100644
index 0000000000000000000000000000000000000000..1031ca4d5bbb4365c4f12e97d8114472f1a8f595
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/common/batch/MapBatch.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 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.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch;
+
+import java.util.Map;
+
+/**
+ * @author pkupczyk
+ */
+public class MapBatch<K, V> extends Batch<Map<K, V>>
+{
+
+    public MapBatch(int batchIndex, int fromObjectIndex, int toObjectIndex, Map<K, V> objects, int totalObjectCount)
+    {
+        super(batchIndex, fromObjectIndex, toObjectIndex, objects, totalObjectCount);
+    }
+
+    public boolean isEmpty()
+    {
+        return getObjects() == null || getObjects().isEmpty();
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/common/batch/MapBatchProcessor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/common/batch/MapBatchProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d750b4d68bf28afdc19d5af8bfd0ec3ebccd042
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/common/batch/MapBatchProcessor.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2016 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.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch;
+
+import java.util.Map;
+
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.LazyLoadedProgress;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+
+/**
+ * @author pkupczyk
+ */
+public abstract class MapBatchProcessor<K, V>
+{
+
+    public abstract void process(K key, V value);
+
+    public abstract IProgress createProgress(K key, V value, int objectIndex, int totalObjectCount);
+
+    public MapBatchProcessor(IOperationContext context, MapBatch<K, V> batch)
+    {
+        int objectIndex = batch.getFromObjectIndex() + 1;
+        int totalObjectCount = batch.getTotalObjectCount();
+
+        for (Map.Entry<K, V> entry : batch.getObjects().entrySet())
+        {
+            IProgress progress = new MapBatchLazyLoadedProgress(entry.getKey(), entry.getValue(), objectIndex, totalObjectCount);
+            context.pushProgress(progress);
+            process(entry.getKey(), entry.getValue());
+            context.popProgress();
+            objectIndex++;
+        }
+    }
+
+    private class MapBatchLazyLoadedProgress extends LazyLoadedProgress
+    {
+
+        private static final long serialVersionUID = 1L;
+
+        private K key;
+
+        private V value;
+
+        private int objectIndex;
+
+        private int totalObjectCount;
+
+        public MapBatchLazyLoadedProgress(K key, V value, int objectIndex, int totalObjectCount)
+        {
+            this.key = key;
+            this.value = value;
+            this.objectIndex = objectIndex;
+            this.totalObjectCount = totalObjectCount;
+        }
+
+        @Override
+        protected IProgress load()
+        {
+            return createProgress(key, value, objectIndex, totalObjectCount);
+        }
+
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/CheckAccessProgress.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/CheckAccessProgress.java
new file mode 100644
index 0000000000000000000000000000000000000000..ef079851a85918ce1b61561d131d9ae908814ce4
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/CheckAccessProgress.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 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.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress;
+
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
+
+/**
+ * @author pkupczyk
+ */
+public class CheckAccessProgress extends Progress
+{
+
+    private static final long serialVersionUID = 1L;
+
+    public CheckAccessProgress(Object object, int numItemsProcessed, int totalItemsToProcess)
+    {
+        super("checking access", EntityProgressToStringBuilder.toString(object), numItemsProcessed, totalItemsToProcess);
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/CheckDataProgress.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/CheckDataProgress.java
new file mode 100644
index 0000000000000000000000000000000000000000..47c559718f9e5cc9c592a4f1dcaa21130b2a0525
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/CheckDataProgress.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 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.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress;
+
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
+
+/**
+ * @author pkupczyk
+ */
+public class CheckDataProgress extends Progress
+{
+
+    private static final long serialVersionUID = 1L;
+
+    public CheckDataProgress(Object object, int numItemsProcessed, int totalItemsToProcess)
+    {
+        super("checking data", EntityProgressToStringBuilder.toString(object), numItemsProcessed, totalItemsToProcess);
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/CreateEntityProgress.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/CreateEntityProgress.java
new file mode 100644
index 0000000000000000000000000000000000000000..35102ac44a7b97f30b23db2e02b47a1acfd12c8b
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/CreateEntityProgress.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 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.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress;
+
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
+
+/**
+ * @author pkupczyk
+ */
+public class CreateEntityProgress extends Progress
+{
+
+    private static final long serialVersionUID = 1L;
+
+    public CreateEntityProgress(Object object, int numItemsProcessed, int totalItemsToProcess)
+    {
+        super("creating", EntityProgressToStringBuilder.toString(object), numItemsProcessed, totalItemsToProcess);
+    }
+
+}
\ No newline at end of file
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/EntityProgressToStringBuilder.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/EntityProgressToStringBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..9a6043e8a5836853402f4e4b68ab8630c20acc25
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/EntityProgressToStringBuilder.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016 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.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress;
+
+import org.apache.commons.lang.builder.ReflectionToStringBuilder;
+
+/**
+ * @author pkupczyk
+ */
+public class EntityProgressToStringBuilder
+{
+
+    public static String toString(Object object)
+    {
+        ReflectionToStringBuilder builder = new ReflectionToStringBuilder(object, EntityProgressToStringStyle.ENTITY_PROGRESS_STYLE);
+        return builder.toString();
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/EntityProgressToStringStyle.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/EntityProgressToStringStyle.java
new file mode 100644
index 0000000000000000000000000000000000000000..3dd3f9d677f4c7a05cd82c958c7b4d843ae44b50
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/EntityProgressToStringStyle.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2016 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.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.commons.lang.builder.ReflectionToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.FieldUpdateValue;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.ListUpdateValue;
+
+/**
+ * @author pkupczyk
+ */
+public final class EntityProgressToStringStyle extends ToStringStyle
+{
+
+    public static final ToStringStyle ENTITY_PROGRESS_STYLE = new EntityProgressToStringStyle();
+
+    private static final long serialVersionUID = 1L;
+
+    EntityProgressToStringStyle()
+    {
+        super();
+        this.setContentStart("[");
+        this.setContentEnd("]");
+        this.setArrayStart("[");
+        this.setArrayEnd("]");
+        this.setArraySeparator(", ");
+        this.setFieldSeparator(", ");
+        this.setFieldSeparatorAtStart(false);
+        this.setUseShortClassName(true);
+        this.setUseIdentityHashCode(false);
+        this.setNullText("null");
+    }
+
+    private Object readResolve()
+    {
+        return ENTITY_PROGRESS_STYLE;
+    }
+
+    @Override
+    public void append(StringBuffer buffer, String fieldName, Object value, Boolean fullDetail)
+    {
+        if (value != null)
+        {
+            if (value instanceof FieldUpdateValue<?>)
+            {
+                FieldUpdateValue<?> fieldValue = (FieldUpdateValue<?>) value;
+                if (fieldValue.isModified())
+                {
+                    append(buffer, fieldName, fieldValue.getValue(), fullDetail);
+                }
+            } else if (value instanceof ListUpdateValue<?, ?, ?, ?>)
+            {
+                ListUpdateValue<?, ?, ?, ?> listValue = (ListUpdateValue<?, ?, ?, ?>) value;
+                if (listValue.hasActions())
+                {
+                    Collection<?> added = listValue.getAdded();
+                    if (added != null && false == added.isEmpty())
+                    {
+                        append(buffer, fieldName + ".ADD", added, fullDetail);
+                    }
+                    Collection<?> removed = listValue.getRemoved();
+                    if (removed != null && false == removed.isEmpty())
+                    {
+                        append(buffer, fieldName + ".REMOVE", removed, fullDetail);
+                    }
+                    Collection<?> set = listValue.getSet();
+                    if (set != null && false == set.isEmpty())
+                    {
+                        append(buffer, fieldName + ".SET", set, fullDetail);
+                    }
+                }
+            } else
+            {
+                super.append(buffer, fieldName, value, fullDetail);
+            }
+        }
+    }
+
+    @Override
+    protected void appendDetail(StringBuffer buffer, String fieldName, Object value)
+    {
+        if (value.getClass().getName().startsWith("ch.ethz") && value.getClass().getName().contains("v3"))
+        {
+            buffer.append(ReflectionToStringBuilder.toString(value, this));
+        } else
+        {
+            buffer.append(value);
+        }
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Override
+    protected void appendDetail(StringBuffer buffer, String fieldName, Collection coll)
+    {
+        buffer.append("[");
+
+        int size = coll.size();
+        int index = 0;
+
+        for (Object item : coll)
+        {
+            appendInternalOrNull(buffer, fieldName, item);
+            index++;
+            if (index < size)
+            {
+                buffer.append(", ");
+            }
+        }
+
+        buffer.append("]");
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Override
+    protected void appendDetail(StringBuffer buffer, String fieldName, Map map)
+    {
+        buffer.append("{");
+
+        int size = map.size();
+        int index = 0;
+
+        for (Object key : map.keySet())
+        {
+            appendInternalOrNull(buffer, fieldName, key);
+            buffer.append("=");
+            appendInternalOrNull(buffer, fieldName, map.get(key));
+            index++;
+            if (index < size)
+            {
+                buffer.append(", ");
+            }
+        }
+
+        buffer.append("}");
+    }
+
+    private void appendInternalOrNull(StringBuffer buffer, String fieldName, Object value)
+    {
+        if (value == null)
+        {
+            appendNullText(buffer, fieldName);
+        } else
+        {
+            appendInternal(buffer, fieldName, value, true);
+        }
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/SetEntityRelationProgress.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/SetEntityRelationProgress.java
new file mode 100644
index 0000000000000000000000000000000000000000..2c5c660a1895c31d9908ef9c5010527940b91ab6
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/SetEntityRelationProgress.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 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.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress;
+
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
+
+/**
+ * @author pkupczyk
+ */
+public class SetEntityRelationProgress extends Progress
+{
+
+    private static final long serialVersionUID = 1L;
+
+    public SetEntityRelationProgress(Object object, String relationName, int numItemsProcessed, int totalItemsToProcess)
+    {
+        super("setting relation " + relationName, EntityProgressToStringBuilder.toString(object), numItemsProcessed, totalItemsToProcess);
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/UpdateEntityRelationProgress.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/UpdateEntityRelationProgress.java
new file mode 100644
index 0000000000000000000000000000000000000000..b92cb2d20be733ef2d0b9eb1c9c6e9f293b8de38
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/UpdateEntityRelationProgress.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 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.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress;
+
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
+
+/**
+ * @author pkupczyk
+ */
+public class UpdateEntityRelationProgress extends Progress
+{
+
+    private static final long serialVersionUID = 1L;
+
+    public UpdateEntityRelationProgress(Object object, String relationName, int numItemsProcessed, int totalItemsToProcess)
+    {
+        super("updating relation " + relationName, EntityProgressToStringBuilder.toString(object), numItemsProcessed, totalItemsToProcess);
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/VerifyEntityProgress.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/VerifyEntityProgress.java
new file mode 100644
index 0000000000000000000000000000000000000000..7d3d32fed1100a418ec1b68e65a593357a64d6a1
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/VerifyEntityProgress.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 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.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress;
+
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
+
+/**
+ * @author pkupczyk
+ */
+public class VerifyEntityProgress extends Progress
+{
+
+    private static final long serialVersionUID = 1L;
+
+    public VerifyEntityProgress(int numItemsProcessed, int totalItemsToProcess)
+    {
+        super("verifying entity", null, numItemsProcessed, totalItemsToProcess);
+    }
+
+}
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/VerifyExperimentExecutorTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/VerifyExperimentExecutorTest.java
deleted file mode 100644
index ade4c7c4433e66a1518b24487f8e146729ac5bdf..0000000000000000000000000000000000000000
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/VerifyExperimentExecutorTest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2014 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.ethz.sis.openbis.generic.server.asapi.v3.executor.experiment;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.jmock.Expectations;
-import org.testng.annotations.Test;
-
-import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.AbstractExecutorTest;
-import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.experiment.VerifyExperimentExecutor;
-import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property.IVerifyEntityPropertyExecutor;
-import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
-
-/**
- * @author pkupczyk
- */
-public class VerifyExperimentExecutorTest extends AbstractExecutorTest
-{
-
-    private IVerifyEntityPropertyExecutor verifyEntityPropertyExecutor;
-
-    @Override
-    protected void init()
-    {
-        verifyEntityPropertyExecutor = context.mock(IVerifyEntityPropertyExecutor.class);
-    }
-
-    @Test
-    public void testVerifyNonEmptyList()
-    {
-        final List<ExperimentPE> experiments = Arrays.asList(new ExperimentPE(), new ExperimentPE());
-
-        context.checking(new Expectations()
-            {
-                {
-                    one(verifyEntityPropertyExecutor).verify(operationContext, experiments);
-                }
-            });
-
-        execute(experiments);
-    }
-
-    @Test
-    public void testVerifyEmptyList()
-    {
-        final List<ExperimentPE> experiments = Collections.emptyList();
-        execute(experiments);
-    }
-
-    @Test
-    public void testVerifyNull()
-    {
-        final List<ExperimentPE> experiments = null;
-        execute(experiments);
-    }
-
-    private void execute(Collection<ExperimentPE> experiments)
-    {
-        VerifyExperimentExecutor executor = new VerifyExperimentExecutor(verifyEntityPropertyExecutor);
-        executor.verify(operationContext, experiments);
-    }
-
-}
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/VerifyEntityPropertyExecutorTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/VerifyEntityPropertyExecutorTest.java
index de96f63ccf9472c37c1c1652629b57dc58364da7..5efc5b2d8d3bb7740704142252197b10880025e4 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/VerifyEntityPropertyExecutorTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/VerifyEntityPropertyExecutorTest.java
@@ -24,7 +24,8 @@ import java.util.Set;
 import org.jmock.Expectations;
 import org.testng.annotations.Test;
 
-import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property.VerifyEntityPropertyExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgress;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.CollectionBatch;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IEntityPropertyTypeDAO;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EntityPropertyPE;
@@ -83,6 +84,9 @@ public class VerifyEntityPropertyExecutorTest extends AbstractEntityPropertyExec
         context.checking(new Expectations()
             {
                 {
+                    allowing(operationContext).pushProgress(with(any(IProgress.class)));
+                    allowing(operationContext).popProgress();
+
                     allowing(daoFactory).getEntityPropertyTypeDAO(EntityKind.SAMPLE);
                     will(returnValue(entityPropertyTypeDAO));
 
@@ -103,10 +107,14 @@ public class VerifyEntityPropertyExecutorTest extends AbstractEntityPropertyExec
         execute(Arrays.asList(propertyHolder));
     }
 
-    public void execute(Collection<? extends IEntityInformationWithPropertiesHolder> propertyHolders)
+    public void execute(Collection<IEntityInformationWithPropertiesHolder> propertyHolders)
     {
+        CollectionBatch<? extends IEntityInformationWithPropertiesHolder> batch =
+                new CollectionBatch<IEntityInformationWithPropertiesHolder>(0, 0, propertyHolders.size(), propertyHolders,
+                        propertyHolders.size());
+
         VerifyEntityPropertyExecutor executor = new VerifyEntityPropertyExecutor(daoFactory, managedPropertyEvaluatorFactory);
-        executor.verify(operationContext, propertyHolders);
+        executor.verify(operationContext, batch);
     }
 
 }
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/EntityProgressToStringStyleTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/EntityProgressToStringStyleTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b1c5f6f3cff9f51dd8a02cb61854799f812a341e
--- /dev/null
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/EntityProgressToStringStyleTest.java
@@ -0,0 +1,209 @@
+package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.commons.lang.builder.ReflectionToStringBuilder;
+import org.testng.annotations.Test;
+
+public class EntityProgressToStringStyleTest
+{
+    @Test
+    public void testWithNulls()
+    {
+        TestClass o = new TestClass();
+        assertToStringEquals(o, "TestClass[primitive=0]");
+    }
+
+    @Test
+    public void testWithPrimitive()
+    {
+        TestClass o = new TestClass();
+        o.primitive = 1;
+        assertToStringEquals(o, "TestClass[primitive=1]");
+    }
+
+    @Test
+    public void testWithString()
+    {
+        TestClass o = new TestClass();
+        o.string = "abc";
+        assertToStringEquals(o, "TestClass[primitive=0, string=abc]");
+    }
+
+    @Test
+    public void testWithInteger()
+    {
+        TestClass o = new TestClass();
+        o.integer = 1;
+        assertToStringEquals(o, "TestClass[primitive=0, integer=1]");
+    }
+
+    @Test
+    public void testWithObject()
+    {
+        TestClass o = new TestClass();
+        o.object = new TestClass();
+        assertToStringEquals(o, "TestClass[primitive=0, object=TestClass[primitive=0]]");
+    }
+
+    @Test
+    public void testWithPrimitiveArray()
+    {
+        TestClass o = new TestClass();
+        o.primitiveArray = new int[] { 0, 1, 2 };
+        assertToStringEquals(o, "TestClass[primitive=0, primitiveArray=[0, 1, 2]]");
+    }
+
+    @Test
+    public void testWithStringArray()
+    {
+        TestClass o = new TestClass();
+        o.stringArray = new String[] { "abc", "def", "ghi" };
+        assertToStringEquals(o, "TestClass[primitive=0, stringArray=[abc, def, ghi]]");
+    }
+
+    @Test
+    public void testWithIntegerArray()
+    {
+        TestClass o = new TestClass();
+        o.integerArray = new Integer[] { 1, 2, 3 };
+        assertToStringEquals(o, "TestClass[primitive=0, integerArray=[1, 2, 3]]");
+    }
+
+    @Test
+    public void testWithObjectArray()
+    {
+        TestClass nested1 = new TestClass();
+        nested1.primitive = 1;
+        TestClass nested2 = new TestClass();
+        nested2.primitive = 2;
+
+        TestClass o = new TestClass();
+        o.objectArray = new TestClass[] { nested1, nested2 };
+
+        assertToStringEquals(o, "TestClass[primitive=0, objectArray=[TestClass[primitive=1], TestClass[primitive=2]]]");
+    }
+
+    @Test
+    public void testWithNullCollectionItem()
+    {
+        TestClass o = new TestClass();
+        o.stringCollection = Arrays.asList("abc", null, "ghi");
+        assertToStringEquals(o, "TestClass[primitive=0, stringCollection=[abc, null, ghi]]");
+    }
+
+    @Test
+    public void testWithStringCollection()
+    {
+        TestClass o = new TestClass();
+        o.stringCollection = Arrays.asList("abc", "def", "ghi");
+        assertToStringEquals(o, "TestClass[primitive=0, stringCollection=[abc, def, ghi]]");
+    }
+
+    @Test
+    public void testWithIntegerCollection()
+    {
+        TestClass o = new TestClass();
+        o.integerCollection = Arrays.asList(1, 2, 3);
+        assertToStringEquals(o, "TestClass[primitive=0, integerCollection=[1, 2, 3]]");
+    }
+
+    @Test
+    public void testWithObjectCollection()
+    {
+        TestClass nested1 = new TestClass();
+        nested1.primitive = 1;
+        TestClass nested2 = new TestClass();
+        nested2.primitive = 2;
+
+        TestClass o = new TestClass();
+        o.objectCollection = Arrays.asList(nested1, nested2);
+
+        assertToStringEquals(o, "TestClass[primitive=0, objectCollection=[TestClass[primitive=1], TestClass[primitive=2]]]");
+    }
+
+    @Test
+    public void testWithNullMapKeyAndValue()
+    {
+        Map<String, String> map = new LinkedHashMap<String, String>();
+        map.put("k1", "v1");
+        map.put(null, null);
+
+        TestClass o = new TestClass();
+        o.stringMap = map;
+
+        assertToStringEquals(o, "TestClass[primitive=0, stringMap={k1=v1, null=null}]");
+    }
+
+    @Test
+    public void testWithStringMap()
+    {
+        Map<String, String> map = new LinkedHashMap<String, String>();
+        map.put("k1", "v1");
+        map.put("k2", "v2");
+
+        TestClass o = new TestClass();
+        o.stringMap = map;
+
+        assertToStringEquals(o, "TestClass[primitive=0, stringMap={k1=v1, k2=v2}]");
+    }
+
+    @Test
+    public void testWithIntegerMap()
+    {
+        Map<Integer, Integer> map = new LinkedHashMap<Integer, Integer>();
+        map.put(1, 11);
+        map.put(2, 22);
+
+        TestClass o = new TestClass();
+        o.integerMap = map;
+
+        assertToStringEquals(o, "TestClass[primitive=0, integerMap={1=11, 2=22}]");
+    }
+
+    @Test
+    public void testWithObjectMap()
+    {
+        TestClass nested1 = new TestClass();
+        nested1.primitive = 1;
+        TestClass nested2 = new TestClass();
+        nested2.primitive = 2;
+
+        Map<TestClass, TestClass> map = new LinkedHashMap<TestClass, TestClass>();
+        map.put(nested1, nested1);
+        map.put(nested2, nested2);
+
+        TestClass o = new TestClass();
+        o.objectMap = map;
+
+        assertToStringEquals(o,
+                "TestClass[primitive=0, objectMap={TestClass[primitive=1]=TestClass[primitive=1], TestClass[primitive=2]=TestClass[primitive=2]}]");
+    }
+
+    @Test
+    public void testWithCircularReferences()
+    {
+        TestClass o1 = new TestClass();
+        o1.primitive = 1;
+        TestClass o2 = new TestClass();
+        o2.primitive = 2;
+
+        o1.object = o2;
+        o2.object = o1;
+
+        assertToStringEquals(o1,
+                "TestClass[primitive=1, object=TestClass[primitive=2, object=" + o1.getClass().getName() + "@"
+                        + Integer.toHexString(System.identityHashCode(o1)) + "]]");
+    }
+
+    private void assertToStringEquals(Object object, String expectedToString)
+    {
+        ReflectionToStringBuilder builder = new ReflectionToStringBuilder(object, EntityProgressToStringStyle.ENTITY_PROGRESS_STYLE);
+        assertEquals(builder.toString(), expectedToString);
+    }
+
+}
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/TestClass.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/TestClass.java
new file mode 100644
index 0000000000000000000000000000000000000000..b84f1adc77cc925b7927e95ef543b09d0c6e0c75
--- /dev/null
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/entity/progress/TestClass.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2016 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.ethz.sis.openbis.generic.server.asapi.v3.helper.entity.progress;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * @author pkupczyk
+ */
+public class TestClass
+{
+
+    public int primitive;
+
+    public String string;
+
+    public Integer integer;
+
+    public TestClass object;
+
+    public int[] primitiveArray;
+
+    public String[] stringArray;
+
+    public Integer[] integerArray;
+
+    public TestClass[] objectArray;
+
+    public Collection<String> stringCollection;
+
+    public Collection<Integer> integerCollection;
+
+    public Collection<TestClass> objectCollection;
+
+    public Map<String, String> stringMap;
+
+    public Map<Integer, Integer> integerMap;
+
+    public Map<TestClass, TestClass> objectMap;
+
+    @Override
+    public String toString()
+    {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdateSampleTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdateSampleTest.java
index c2fa1fa8a008338437cc8d4527138f903752f68f..8ef0020b6f3f80868c9b86c2e76fee890003af57 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdateSampleTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdateSampleTest.java
@@ -51,7 +51,6 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.TagCode;
 import ch.ethz.sis.openbis.systemtest.asapi.v3.index.ReindexingState;
 import ch.systemsx.cisd.common.action.IDelegatedAction;
-import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.common.test.AssertionUtil;
 
 import junit.framework.Assert;
@@ -397,7 +396,7 @@ public class UpdateSampleTest extends AbstractSampleTest
     @Test
     public void testUpdateWithExperimentInDifferentSpace()
     {
-        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        final String sessionToken = v3api.login(TEST_USER, PASSWORD);
 
         SampleCreation creation = new SampleCreation();
         creation.setCode("SAMPLE");
@@ -406,25 +405,24 @@ public class UpdateSampleTest extends AbstractSampleTest
 
         List<SamplePermId> ids = v3api.createSamples(sessionToken, Arrays.asList(creation));
 
-        SampleUpdate update = new SampleUpdate();
+        final SampleUpdate update = new SampleUpdate();
         update.setSampleId(ids.get(0));
         update.setExperimentId(new ExperimentPermId("201206190940555-1032"));
 
-        try
-        {
-            v3api.updateSamples(sessionToken, Arrays.asList(update));
-        } catch (UserFailureException e)
-        {
-            Assert.assertEquals(
-                    "Sample space must be the same as experiment space. Sample: /CISD/SAMPLE, Experiment: /TEST-SPACE/TEST-PROJECT/EXP-SPACE-TEST (Context: [verify experiment for sample SAMPLE])",
-                    e.getMessage());
-        }
+        assertUserFailureException(new IDelegatedAction()
+            {
+                @Override
+                public void execute()
+                {
+                    v3api.updateSamples(sessionToken, Arrays.asList(update));
+                }
+            }, "Sample space must be the same as experiment space. Sample: /CISD/SAMPLE, Experiment: /TEST-SPACE/TEST-PROJECT/EXP-SPACE-TEST");
     }
 
     @Test
     public void testUpdateWithExperimentForSharedSample()
     {
-        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        final String sessionToken = v3api.login(TEST_USER, PASSWORD);
 
         SampleCreation creation = new SampleCreation();
         creation.setCode("SAMPLE");
@@ -432,19 +430,18 @@ public class UpdateSampleTest extends AbstractSampleTest
 
         List<SamplePermId> ids = v3api.createSamples(sessionToken, Arrays.asList(creation));
 
-        SampleUpdate update = new SampleUpdate();
+        final SampleUpdate update = new SampleUpdate();
         update.setSampleId(ids.get(0));
         update.setExperimentId(new ExperimentPermId("201206190940555-1032"));
 
-        try
-        {
-            v3api.updateSamples(sessionToken, Arrays.asList(update));
-        } catch (UserFailureException e)
-        {
-            Assert.assertEquals(
-                    "Shared samples cannot be attached to experiments. Sample: /SAMPLE, Experiment: /TEST-SPACE/TEST-PROJECT/EXP-SPACE-TEST (Context: [verify experiment for sample SAMPLE])",
-                    e.getMessage());
-        }
+        assertUserFailureException(new IDelegatedAction()
+            {
+                @Override
+                public void execute()
+                {
+                    v3api.updateSamples(sessionToken, Arrays.asList(update));
+                }
+            }, "Shared samples cannot be attached to experiments. Sample: /SAMPLE, Experiment: /TEST-SPACE/TEST-PROJECT/EXP-SPACE-TEST");
     }
 
     @Test
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/update/ListUpdateValue.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/update/ListUpdateValue.java
index 539017ce1ecb620c1c193260a42a5c1d3653cea6..4194c5a671a500333e684cb5ee7dc8a6fd797b9b 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/update/ListUpdateValue.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/update/ListUpdateValue.java
@@ -4,9 +4,12 @@ import java.io.Serializable;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
 import ch.systemsx.cisd.base.annotation.JsonObject;
 
 @JsonObject("as.dto.common.update.ListUpdateValue")
@@ -76,6 +79,21 @@ public class ListUpdateValue<ADD, REMOVE, SET, ACTION> implements Serializable
         actions.add((ListUpdateAction<ACTION>) action);
     }
 
+    @SuppressWarnings("unchecked")
+    @JsonIgnore
+    public Collection<REMOVE> getRemoved()
+    {
+        Collection<Object> items = new LinkedHashSet<Object>();
+        for (ListUpdateAction<ACTION> action : actions)
+        {
+            if (action instanceof ListUpdateActionRemove<?>)
+            {
+                items.addAll(action.getItems());
+            }
+        }
+        return (Collection<REMOVE>) items;
+    }
+
     @SuppressWarnings("unchecked")
     public void add(ADD... items)
     {
@@ -84,6 +102,21 @@ public class ListUpdateValue<ADD, REMOVE, SET, ACTION> implements Serializable
         actions.add((ListUpdateAction<ACTION>) action);
     }
 
+    @SuppressWarnings("unchecked")
+    @JsonIgnore
+    public Collection<ADD> getAdded()
+    {
+        Collection<Object> items = new LinkedHashSet<Object>();
+        for (ListUpdateAction<ACTION> action : actions)
+        {
+            if (action instanceof ListUpdateActionAdd<?>)
+            {
+                items.addAll(action.getItems());
+            }
+        }
+        return (Collection<ADD>) items;
+    }
+
     @SuppressWarnings("unchecked")
     public void set(SET... items)
     {
@@ -98,4 +131,19 @@ public class ListUpdateValue<ADD, REMOVE, SET, ACTION> implements Serializable
         actions.add((ListUpdateAction<ACTION>) action);
     }
 
+    @SuppressWarnings("unchecked")
+    @JsonIgnore
+    public Collection<SET> getSet()
+    {
+        Collection<Object> items = new LinkedHashSet<Object>();
+        for (ListUpdateAction<ACTION> action : actions)
+        {
+            if (action instanceof ListUpdateActionSet<?>)
+            {
+                items.addAll(action.getItems());
+            }
+        }
+        return (Collection<SET>) items;
+    }
+
 }