From f9590e676c60a86c39f76b47a8fd224a92b781ef Mon Sep 17 00:00:00 2001
From: pkupczyk <pkupczyk>
Date: Wed, 13 Apr 2016 19:25:39 +0000
Subject: [PATCH] SSDM-3476 : V3 AS API - tags - updateTags (both Java and
 Javascript) + createTags started

SVN: 36175
---
 .../openbis-v3-api-test/html/test/dtos.js     |   2 +
 .../server/asapi/v3/ApplicationServerApi.java |  37 +-
 .../asapi/v3/ApplicationServerApiLogger.java  |  16 +
 .../entity/AbstractCreateEntityExecutor.java  |  15 +-
 .../entity/AbstractUpdateEntityExecutor.java  |  13 +-
 ...actUpdateEntityToManyRelationExecutor.java |   2 +-
 .../method/CreateTagMethodExecutor.java       |  44 +++
 .../method/ICreateTagMethodExecutor.java      |  28 ++
 .../method/IUpdateTagMethodExecutor.java      |  27 ++
 .../method/UpdateTagMethodExecutor.java       |  44 +++
 .../executor/tag/AddTagToEntityExecutor.java  |  23 +-
 .../tag/CreateMissingTagExecutor.java         |  90 +++++
 .../v3/executor/tag/CreateTagExecutor.java    |  96 ++++-
 .../tag/ICreateMissingTagExecutor.java        |  34 ++
 .../v3/executor/tag/ICreateTagExecutor.java   |  10 +-
 .../tag/IUpdateTagDataSetsExecutor.java       |  29 ++
 .../IUpdateTagDataSetsWithCacheExecutor.java  |  32 ++
 .../v3/executor/tag/IUpdateTagExecutor.java   |  28 ++
 .../tag/IUpdateTagExperimentsExecutor.java    |  29 ++
 ...UpdateTagExperimentsWithCacheExecutor.java |  32 ++
 .../tag/IUpdateTagMaterialsExecutor.java      |  29 ++
 .../IUpdateTagMaterialsWithCacheExecutor.java |  32 ++
 .../tag/IUpdateTagSamplesExecutor.java        |  29 ++
 .../IUpdateTagSamplesWithCacheExecutor.java   |  31 ++
 .../tag/RemoveTagFromEntityExecutor.java      |   6 +-
 .../executor/tag/SetTagForEntityExecutor.java |  20 +-
 .../tag/UpdateTagDataSetsExecutor.java        |  66 ++++
 .../UpdateTagDataSetsWithCacheExecutor.java   |  65 +++
 .../tag/UpdateTagEntitiesExecutor.java        | 103 +++++
 .../v3/executor/tag/UpdateTagExecutor.java    | 145 +++++++
 .../tag/UpdateTagExperimentsExecutor.java     |  66 ++++
 ...UpdateTagExperimentsWithCacheExecutor.java |  66 ++++
 .../tag/UpdateTagMaterialsExecutor.java       |  66 ++++
 .../UpdateTagMaterialsWithCacheExecutor.java  |  61 +++
 .../tag/UpdateTagSamplesExecutor.java         |  66 ++++
 .../UpdateTagSamplesWithCacheExecutor.java    |  66 ++++
 .../asapi/v3/helper/tag/TagAuthorization.java |  30 +-
 .../server/dataaccess/IMetaprojectDAO.java    |   5 +
 .../server/dataaccess/db/MetaprojectDAO.java  |  28 +-
 .../generic/shared/dto/MetaprojectPE.java     |  14 +-
 .../api/v3/as/dto/tag/create/TagCreation.js   |  27 ++
 .../api/v3/as/dto/tag/update/TagUpdate.js     |  76 ++++
 .../public/resources/api/v3/openbis.js        |  11 +
 .../systemtest/asapi/v3/UpdateTagTest.java    | 369 ++++++++++++++++++
 .../asapi/v3/IApplicationServerApi.java       |  27 +-
 .../asapi/v3/dto/tag/create/TagCreation.java  |  55 +++
 .../asapi/v3/dto/tag/update/TagUpdate.java    | 133 +++++++
 .../generic/sharedapi/v3/dictionary.txt       |  14 +
 48 files changed, 2228 insertions(+), 109 deletions(-)
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/method/CreateTagMethodExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/method/ICreateTagMethodExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/method/IUpdateTagMethodExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/method/UpdateTagMethodExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/CreateMissingTagExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/ICreateMissingTagExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagDataSetsExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagDataSetsWithCacheExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagExperimentsExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagExperimentsWithCacheExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagMaterialsExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagMaterialsWithCacheExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagSamplesExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagSamplesWithCacheExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagDataSetsExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagDataSetsWithCacheExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagEntitiesExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExperimentsExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExperimentsWithCacheExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagMaterialsExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagMaterialsWithCacheExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagSamplesExecutor.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagSamplesWithCacheExecutor.java
 create mode 100644 openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/tag/create/TagCreation.js
 create mode 100644 openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/tag/update/TagUpdate.js
 create mode 100644 openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdateTagTest.java
 create mode 100644 openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/tag/create/TagCreation.java
 create mode 100644 openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/tag/update/TagUpdate.java

diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/dtos.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/dtos.js
index fbd1cdeac1c..37c38bf22fb 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/dtos.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/dtos.js
@@ -294,6 +294,8 @@ var sources = [
 	'as/dto/tag/id/TagPermId',
 	'as/dto/tag/search/TagSearchCriteria',
 	'as/dto/tag/Tag',
+	'as/dto/tag/update/TagUpdate',
+	'as/dto/tag/create/TagCreation',
 	'as/dto/vocabulary/fetchoptions/VocabularyFetchOptions',
 	'as/dto/vocabulary/fetchoptions/VocabularySortOptions',
 	'as/dto/vocabulary/fetchoptions/VocabularyTermFetchOptions',
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApi.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApi.java
index 8d15de9e12a..730dd43af9b 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApi.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApi.java
@@ -98,8 +98,11 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.id.SpacePermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.search.SpaceSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.update.SpaceUpdate;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.Tag;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.create.TagCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.fetchoptions.TagFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.TagPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.VocabularyTerm;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.create.VocabularyTermCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.delete.VocabularyTermDeletionOptions;
@@ -115,6 +118,7 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.method.ICreateMateri
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.method.ICreateProjectMethodExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.method.ICreateSampleMethodExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.method.ICreateSpaceMethodExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.method.ICreateTagMethodExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.method.ICreateVocabularyTermMethodExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.method.IDeleteDataSetMethodExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.method.IDeleteExperimentMethodExecutor;
@@ -154,6 +158,7 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.method.IUpdateMateri
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.method.IUpdateProjectMethodExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.method.IUpdateSampleMethodExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.method.IUpdateSpaceMethodExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.method.IUpdateTagMethodExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.method.IUpdateVocabularyTermMethodExecutor;
 import ch.systemsx.cisd.openbis.common.spring.IInvocationLoggerContext;
 import ch.systemsx.cisd.openbis.generic.server.AbstractServer;
@@ -204,6 +209,9 @@ public class ApplicationServerApi extends AbstractServer<IApplicationServerApi>
     @Autowired
     private ICreateVocabularyTermMethodExecutor createVocabularyTermExecutor;
 
+    @Autowired
+    private ICreateTagMethodExecutor createTagExecutor;
+
     @Autowired
     private IUpdateSpaceMethodExecutor updateSpaceExecutor;
 
@@ -225,6 +233,9 @@ public class ApplicationServerApi extends AbstractServer<IApplicationServerApi>
     @Autowired
     private IUpdateVocabularyTermMethodExecutor updateVocabularyTermExecutor;
 
+    @Autowired
+    private IUpdateTagMethodExecutor updateTagExecutor;
+
     @Autowired
     private IMapSpaceMethodExecutor mapSpaceExecutor;
 
@@ -257,13 +268,13 @@ public class ApplicationServerApi extends AbstractServer<IApplicationServerApi>
 
     @Autowired
     private ISearchExperimentMethodExecutor searchExperimentExecutor;
-    
+
     @Autowired
     private ISearchExperimentTypeMethodExecutor searchExperimentTypeExecutor;
 
     @Autowired
     private ISearchSampleMethodExecutor searchSampleExecutor;
-    
+
     @Autowired
     private ISearchSampleTypeMethodExecutor searchSampleTypeExecutor;
 
@@ -272,13 +283,13 @@ public class ApplicationServerApi extends AbstractServer<IApplicationServerApi>
 
     @Autowired
     private ISearchDataSetTypeMethodExecutor searchDataSetTypeExecutor;
-    
+
     @Autowired
     private ISearchMaterialMethodExecutor searchMaterialExecutor;
 
     @Autowired
     private ISearchMaterialTypeMethodExecutor searchMaterialTypeExecutor;
-    
+
     @Autowired
     private ISearchVocabularyTermMethodExecutor searchVocabularyTermExecutor;
 
@@ -434,6 +445,15 @@ public class ApplicationServerApi extends AbstractServer<IApplicationServerApi>
         return createVocabularyTermExecutor.create(sessionToken, creations);
     }
 
+    @Override
+    @RolesAllowed({ RoleWithHierarchy.SPACE_USER, RoleWithHierarchy.SPACE_ETL_SERVER })
+    @Capability("CREATE_TAG")
+    @DatabaseUpdateModification(value = ObjectKind.METAPROJECT)
+    public List<TagPermId> createTags(String sessionToken, List<TagCreation> creations)
+    {
+        return createTagExecutor.create(sessionToken, creations);
+    }
+
     @Override
     @Transactional
     @RolesAllowed({ RoleWithHierarchy.SPACE_ADMIN, RoleWithHierarchy.SPACE_ETL_SERVER })
@@ -503,6 +523,15 @@ public class ApplicationServerApi extends AbstractServer<IApplicationServerApi>
         updateVocabularyTermExecutor.update(sessionToken, vocabularyTermUpdates);
     }
 
+    @Override
+    @Transactional
+    @RolesAllowed({ RoleWithHierarchy.SPACE_USER, RoleWithHierarchy.SPACE_ETL_SERVER })
+    @Capability("UPDATE_TAG")
+    public void updateTags(String sessionToken, List<TagUpdate> tagUpdates)
+    {
+        updateTagExecutor.update(sessionToken, tagUpdates);
+    }
+
     @Override
     @Transactional(readOnly = true)
     @RolesAllowed({ RoleWithHierarchy.SPACE_OBSERVER, RoleWithHierarchy.SPACE_ETL_SERVER })
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApiLogger.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApiLogger.java
index 8b11da128be..5d6cf69c82b 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApiLogger.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApiLogger.java
@@ -94,8 +94,11 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.id.SpacePermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.search.SpaceSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.update.SpaceUpdate;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.Tag;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.create.TagCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.fetchoptions.TagFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.TagPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.VocabularyTerm;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.create.VocabularyTermCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.delete.VocabularyTermDeletionOptions;
@@ -200,6 +203,13 @@ public class ApplicationServerApiLogger extends AbstractServerLogger implements
         return null;
     }
 
+    @Override
+    public List<TagPermId> createTags(String sessionToken, List<TagCreation> newTags)
+    {
+        logAccess(sessionToken, "create-tags", "NEW_TAGS(%s)", abbreviate(newTags));
+        return null;
+    }
+
     @Override
     public void updateSpaces(String sessionToken, List<SpaceUpdate> spaceUpdates)
     {
@@ -242,6 +252,12 @@ public class ApplicationServerApiLogger extends AbstractServerLogger implements
         logAccess(sessionToken, "update-vocabulary-terms", "VOCABULARY_TERM_UPDATES(%s)", abbreviate(vocabularyTermUpdates));
     }
 
+    @Override
+    public void updateTags(String sessionToken, List<TagUpdate> tagUpdates)
+    {
+        logAccess(sessionToken, "update-tags", "TAG_UPDATES(%s)", abbreviate(tagUpdates));
+    }
+
     @Override
     public Map<ISpaceId, Space> mapSpaces(String sessionToken, List<? extends ISpaceId> spaceIds, SpaceFetchOptions fetchOptions)
     {
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 3bacc39271b..18a8e5e475d 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
@@ -32,12 +32,12 @@ import org.springframework.dao.DataAccessException;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.context.Progress;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
-import ch.systemsx.cisd.openbis.generic.shared.dto.IIdAndCodeHolder;
+import ch.systemsx.cisd.openbis.generic.shared.basic.IIdHolder;
 
 /**
  * @author pkupczyk
  */
-public abstract class AbstractCreateEntityExecutor<CREATION, PE, PERM_ID> implements
+public abstract class AbstractCreateEntityExecutor<CREATION, PE extends IIdHolder, PERM_ID> implements
         ICreateEntityExecutor<CREATION, PERM_ID>
 {
 
@@ -67,7 +67,7 @@ public abstract class AbstractCreateEntityExecutor<CREATION, PE, PERM_ID> implem
             reloadEntities(context, entitiesAll);
 
             updateAll(context, entitiesAll);
-            
+
             daoFactory.getSessionFactory().getCurrentSession().flush();
             reloadEntities(context, entitiesAll);
 
@@ -129,8 +129,7 @@ public abstract class AbstractCreateEntityExecutor<CREATION, PE, PERM_ID> implem
 
         for (PE entity : creationToEntityMap.values())
         {
-            IIdAndCodeHolder idAndCodeHolder = (IIdAndCodeHolder) entity;
-            ids.add(idAndCodeHolder.getId());
+            ids.add(entity.getId());
         }
 
         List<PE> entities = list(context, ids);
@@ -139,14 +138,12 @@ public abstract class AbstractCreateEntityExecutor<CREATION, PE, PERM_ID> implem
 
         for (PE entity : entities)
         {
-            IIdAndCodeHolder idAndCodeHolder = (IIdAndCodeHolder) entity;
-            idToEntityMap.put(idAndCodeHolder.getId(), entity);
+            idToEntityMap.put(entity.getId(), entity);
         }
 
         for (Map.Entry<CREATION, PE> entry : creationToEntityMap.entrySet())
         {
-            IIdAndCodeHolder idAndCodeHolder = (IIdAndCodeHolder) entry.getValue();
-            entry.setValue(idToEntityMap.get(idAndCodeHolder.getId()));
+            entry.setValue(idToEntityMap.get(entry.getValue().getId()));
         }
     }
 
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 2ba03bc546f..fa3d73f5fdd 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
@@ -33,12 +33,12 @@ 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.executor.IOperationContext;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
-import ch.systemsx.cisd.openbis.generic.shared.dto.IIdAndCodeHolder;
+import ch.systemsx.cisd.openbis.generic.shared.basic.IIdHolder;
 
 /**
  * @author pkupczyk
  */
-public abstract class AbstractUpdateEntityExecutor<UPDATE, PE, ID> implements IUpdateEntityExecutor<UPDATE>
+public abstract class AbstractUpdateEntityExecutor<UPDATE, PE extends IIdHolder, ID> implements IUpdateEntityExecutor<UPDATE>
 {
 
     @Autowired
@@ -134,8 +134,7 @@ public abstract class AbstractUpdateEntityExecutor<UPDATE, PE, ID> implements IU
 
         for (PE entity : updateToEntityMap.values())
         {
-            IIdAndCodeHolder idAndCodeHolder = (IIdAndCodeHolder) entity;
-            ids.add(idAndCodeHolder.getId());
+            ids.add(entity.getId());
         }
 
         List<PE> entities = list(context, ids);
@@ -144,14 +143,12 @@ public abstract class AbstractUpdateEntityExecutor<UPDATE, PE, ID> implements IU
 
         for (PE entity : entities)
         {
-            IIdAndCodeHolder idAndCodeHolder = (IIdAndCodeHolder) entity;
-            idToEntityMap.put(idAndCodeHolder.getId(), entity);
+            idToEntityMap.put(entity.getId(), entity);
         }
 
         for (Map.Entry<UPDATE, PE> entry : updateToEntityMap.entrySet())
         {
-            IIdAndCodeHolder idAndCodeHolder = (IIdAndCodeHolder) entry.getValue();
-            entry.setValue(idToEntityMap.get(idAndCodeHolder.getId()));
+            entry.setValue(idToEntityMap.get(entry.getValue().getId()));
         }
     }
 
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 e5afe637da0..07bbfbee4a6 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
@@ -87,8 +87,8 @@ public abstract class AbstractUpdateEntityToManyRelationExecutor<ENTITY_UPDATE,
                             if (related != null)
                             {
                                 relatedCollection.add(related);
+                                check(context, entity, relatedId, related);
                             }
-                            check(context, entity, relatedId, related);
                         }
                         remove(context, entity, relatedCollection);
                     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/method/CreateTagMethodExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/method/CreateTagMethodExecutor.java
new file mode 100644
index 00000000000..5077d2da65b
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/method/CreateTagMethodExecutor.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 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.method;
+
+import org.springframework.beans.factory.annotation.Autowired;
+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.executor.entity.ICreateEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.tag.ICreateTagExecutor;
+
+/**
+ * @author pkupczyk
+ */
+@Component
+public class CreateTagMethodExecutor extends AbstractCreateMethodExecutor<TagPermId, TagCreation> implements
+        ICreateTagMethodExecutor
+{
+
+    @Autowired
+    private ICreateTagExecutor createExecutor;
+
+    @Override
+    protected ICreateEntityExecutor<TagCreation, TagPermId> getCreateExecutor()
+    {
+        return createExecutor;
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/method/ICreateTagMethodExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/method/ICreateTagMethodExecutor.java
new file mode 100644
index 00000000000..5370462ec4c
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/method/ICreateTagMethodExecutor.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2015 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.method;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.create.TagCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.TagPermId;
+
+/**
+ * @author pkupczyk
+ */
+public interface ICreateTagMethodExecutor extends ICreateMethodExecutor<TagPermId, TagCreation>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/method/IUpdateTagMethodExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/method/IUpdateTagMethodExecutor.java
new file mode 100644
index 00000000000..cc7537c7b75
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/method/IUpdateTagMethodExecutor.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2015 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.method;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
+
+/**
+ * @author pkupczyk
+ */
+public interface IUpdateTagMethodExecutor extends IUpdateMethodExecutor<TagUpdate>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/method/UpdateTagMethodExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/method/UpdateTagMethodExecutor.java
new file mode 100644
index 00000000000..26645b269be
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/method/UpdateTagMethodExecutor.java
@@ -0,0 +1,44 @@
+/*
+
+ * Copyright 2015 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.method;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IUpdateEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.tag.IUpdateTagExecutor;
+
+/**
+ * @author pkupczyk
+ */
+@Component
+public class UpdateTagMethodExecutor extends AbstractUpdateMethodExecutor<TagUpdate>
+        implements IUpdateTagMethodExecutor
+{
+
+    @Autowired
+    private IUpdateTagExecutor updateExecutor;
+
+    @Override
+    protected IUpdateEntityExecutor<TagUpdate> getUpdateExecutor()
+    {
+        return updateExecutor;
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/AddTagToEntityExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/AddTagToEntityExecutor.java
index 954cf099d02..e74f5bbcaa6 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/AddTagToEntityExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/AddTagToEntityExecutor.java
@@ -27,7 +27,6 @@ import org.springframework.stereotype.Component;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.tag.TagAuthorization;
-import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.shared.dto.IEntityWithMetaprojects;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 
@@ -38,24 +37,21 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 public class AddTagToEntityExecutor implements IAddTagToEntityExecutor
 {
 
-    @Autowired
-    private IDAOFactory daoFactory;
-
     @Autowired
     private IMapTagByIdExecutor mapTagByIdExecutor;
 
     @Autowired
-    private ICreateTagExecutor createTagExecutor;
+    private ICreateMissingTagExecutor createMissingTagExecutor;
 
     @SuppressWarnings("unused")
     private AddTagToEntityExecutor()
     {
     }
 
-    public AddTagToEntityExecutor(IMapTagByIdExecutor mapTagByIdExecutor, ICreateTagExecutor createTagExecutor)
+    public AddTagToEntityExecutor(IMapTagByIdExecutor mapTagByIdExecutor, ICreateMissingTagExecutor createMissingTagExecutor)
     {
         this.mapTagByIdExecutor = mapTagByIdExecutor;
-        this.createTagExecutor = createTagExecutor;
+        this.createMissingTagExecutor = createMissingTagExecutor;
     }
 
     @Override
@@ -76,9 +72,11 @@ public class AddTagToEntityExecutor implements IAddTagToEntityExecutor
             }
         }
 
-        TagAuthorization authorization = new TagAuthorization(context, daoFactory);
+        TagAuthorization authorization = new TagAuthorization(context);
         Map<ITagId, MetaprojectPE> allTagsMap = mapTagByIdExecutor.map(context, allTagIds);
 
+        createMissingTagExecutor.create(context, allTagIds, allTagsMap);
+
         for (Map.Entry<IEntityWithMetaprojects, Collection<? extends ITagId>> entry : entityToTagIdsMap.entrySet())
         {
             IEntityWithMetaprojects entity = entry.getKey();
@@ -89,18 +87,11 @@ public class AddTagToEntityExecutor implements IAddTagToEntityExecutor
                 for (ITagId tagId : tagIds)
                 {
                     MetaprojectPE tag = allTagsMap.get(tagId);
-
-                    if (tag == null)
-                    {
-                        tag = createTagExecutor.createTag(context, tagId);
-                        allTagsMap.put(tagId, tag);
-                    }
-
                     authorization.checkAccess(tag);
-
                     entity.addMetaproject(tag);
                 }
             }
         }
     }
+
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/CreateMissingTagExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/CreateMissingTagExecutor.java
new file mode 100644
index 00000000000..b0aa1838228
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/CreateMissingTagExecutor.java
@@ -0,0 +1,90 @@
+/*
+ * 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.executor.tag;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.List;
+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.tag.create.TagCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.TagPermId;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.tag.TagAuthorization;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MetaprojectIdentifier;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+
+/**
+ * @author pkupczyk
+ */
+@Component
+public class CreateMissingTagExecutor implements ICreateMissingTagExecutor
+{
+
+    @Autowired
+    private IGetTagIdentifierExecutor getTagIdentifierExecutor;
+
+    @Autowired
+    private ICreateTagExecutor createTagExecutor;
+
+    @Autowired
+    private IMapTagByIdExecutor mapTagByIdExecutor;
+
+    @Override
+    public void create(IOperationContext context, Collection<? extends ITagId> neededTagIds, Map<ITagId, MetaprojectPE> existingTagMap)
+    {
+        TagAuthorization authorization = new TagAuthorization(context);
+
+        List<TagCreation> creations = new ArrayList<TagCreation>();
+        Map<TagCreation, ITagId> neededTagIdMap = new IdentityHashMap<TagCreation, ITagId>();
+
+        for (ITagId neededTagId : neededTagIds)
+        {
+            MetaprojectPE tag = existingTagMap.get(neededTagId);
+            if (tag == null)
+            {
+                MetaprojectIdentifier tagIdentifier = getTagIdentifierExecutor.getIdentifier(context, neededTagId);
+
+                authorization.checkAccess(tagIdentifier);
+
+                TagCreation creation = new TagCreation();
+                creation.setCode(tagIdentifier.getMetaprojectName());
+
+                creations.add(creation);
+                neededTagIdMap.put(creation, neededTagId);
+            }
+        }
+
+        List<TagPermId> createdIds = createTagExecutor.create(context, creations);
+        Map<ITagId, MetaprojectPE> createdMap = mapTagByIdExecutor.map(context, createdIds);
+
+        for (int i = 0; i < creations.size(); i++)
+        {
+            TagCreation creation = creations.get(i);
+            TagPermId createdId = createdIds.get(i);
+            MetaprojectPE created = createdMap.get(createdId);
+            ITagId neededTagId = neededTagIdMap.get(creation);
+            existingTagMap.put(neededTagId, created);
+        }
+    }
+
+}
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 40e7727d3c3..e0d290fe0c9 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
@@ -16,58 +16,112 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.tag;
 
-import java.util.Date;
+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;
+import org.springframework.dao.DataAccessException;
 import org.springframework.stereotype.Component;
 
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
+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.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractCreateEntityExecutor;
 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;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MetaprojectIdentifier;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 
 /**
  * @author pkupczyk
  */
 @Component
-public class CreateTagExecutor implements ICreateTagExecutor
+public class CreateTagExecutor extends AbstractCreateEntityExecutor<TagCreation, MetaprojectPE, TagPermId> implements ICreateTagExecutor
 {
 
     @Autowired
     private IDAOFactory daoFactory;
 
-    @Autowired
-    private IGetTagIdentifierExecutor getTagIdentifierExecutor;
+    @Override
+    protected List<MetaprojectPE> createEntities(IOperationContext context, Collection<TagCreation> creations)
+    {
+        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);
+        }
+
+        return tags;
+    }
+
+    @Override
+    protected TagPermId createPermId(IOperationContext context, MetaprojectPE entity)
+    {
+        return new TagPermId(entity.getOwner().getUserId(), entity.getName());
+    }
+
+    @Override
+    protected void checkData(IOperationContext context, TagCreation creation)
+    {
+        if (StringUtils.isEmpty(creation.getCode()))
+        {
+            throw new UserFailureException("Code cannot be empty.");
+        }
+    }
 
-    @SuppressWarnings("unused")
-    private CreateTagExecutor()
+    @Override
+    protected void checkAccess(IOperationContext context, MetaprojectPE entity)
     {
+        new TagAuthorization(context).checkAccess(entity);
     }
 
-    public CreateTagExecutor(IDAOFactory daoFactory, IGetTagIdentifierExecutor getTagIdentifierExecutor)
+    @Override
+    protected void checkBusinessRules(IOperationContext context, Collection<MetaprojectPE> entities)
     {
-        this.daoFactory = daoFactory;
-        this.getTagIdentifierExecutor = getTagIdentifierExecutor;
+        // nothing to do
     }
 
     @Override
-    public MetaprojectPE createTag(IOperationContext context, ITagId tagId)
+    protected void updateBatch(IOperationContext context, Map<TagCreation, MetaprojectPE> entitiesMap)
     {
-        MetaprojectPE tag = new MetaprojectPE();
-        MetaprojectIdentifier identifier = getTagIdentifierExecutor.getIdentifier(context, tagId);
+        // nothing to do
+    }
 
-        tag.setName(identifier.getMetaprojectName());
-        tag.setOwner(context.getSession().tryGetPerson());
-        tag.setCreationDate(new Date());
-        tag.setPrivate(true);
+    @Override
+    protected void updateAll(IOperationContext context, Map<TagCreation, MetaprojectPE> entitiesMap)
+    {
+        // nothing to do
+    }
 
-        daoFactory.getMetaprojectDAO().createOrUpdateMetaproject(tag, context.getSession().tryGetPerson());
+    @Override
+    protected List<MetaprojectPE> list(IOperationContext context, Collection<Long> ids)
+    {
+        return daoFactory.getMetaprojectDAO().listByIDs(ids);
+    }
 
-        new TagAuthorization(context, daoFactory).checkAccess(tag);
+    @Override
+    protected void save(IOperationContext context, List<MetaprojectPE> entities, boolean clearCache)
+    {
+        for (MetaprojectPE entity : entities)
+        {
+            daoFactory.getMetaprojectDAO().createOrUpdateMetaproject(entity, entity.getOwner());
+        }
+    }
 
-        return tag;
+    @Override
+    protected void handleException(DataAccessException e)
+    {
+        DataAccessExceptionTranslator.throwException(e, "tag", null);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/ICreateMissingTagExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/ICreateMissingTagExecutor.java
new file mode 100644
index 00000000000..3f947b9393e
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/ICreateMissingTagExecutor.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.executor.tag;
+
+import java.util.Collection;
+import java.util.Map;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+
+/**
+ * @author pkupczyk
+ */
+public interface ICreateMissingTagExecutor
+{
+
+    void create(IOperationContext context, Collection<? extends ITagId> neededTagIds, Map<ITagId, MetaprojectPE> existingTagMap);
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/ICreateTagExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/ICreateTagExecutor.java
index 29a1f2068ca..a0f2515ea0c 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/ICreateTagExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/ICreateTagExecutor.java
@@ -16,16 +16,14 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.tag;
 
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
-import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
-import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+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.executor.entity.ICreateEntityExecutor;
 
 /**
  * @author pkupczyk
  */
-public interface ICreateTagExecutor
+public interface ICreateTagExecutor extends ICreateEntityExecutor<TagCreation, TagPermId>
 {
 
-    public MetaprojectPE createTag(IOperationContext context, ITagId tagId);
-
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagDataSetsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagDataSetsExecutor.java
new file mode 100644
index 00000000000..1caf893b9d2
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagDataSetsExecutor.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 ETH Zuerich, Scientific IT Services
+ *
+ * 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.tag;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IUpdateEntityRelationsExecutor;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+
+/**
+ * @author pkupczyk
+ */
+public interface IUpdateTagDataSetsExecutor extends IUpdateEntityRelationsExecutor<TagUpdate, MetaprojectPE>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagDataSetsWithCacheExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagDataSetsWithCacheExecutor.java
new file mode 100644
index 00000000000..f8e9f81f1ee
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagDataSetsWithCacheExecutor.java
@@ -0,0 +1,32 @@
+/*
+ * 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.executor.tag;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.IDataSetId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IUpdateEntityRelationsWithCacheExecutor;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+
+/**
+ * @author pkupczyk
+ */
+public interface IUpdateTagDataSetsWithCacheExecutor
+        extends IUpdateEntityRelationsWithCacheExecutor<TagUpdate, MetaprojectPE, IDataSetId, DataPE>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagExecutor.java
new file mode 100644
index 00000000000..4924e25d4f0
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagExecutor.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2015 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.tag;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IUpdateEntityExecutor;
+
+/**
+ * @author pkupczyk
+ */
+public interface IUpdateTagExecutor extends IUpdateEntityExecutor<TagUpdate>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagExperimentsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagExperimentsExecutor.java
new file mode 100644
index 00000000000..09afc6d589a
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagExperimentsExecutor.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 ETH Zuerich, Scientific IT Services
+ *
+ * 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.tag;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IUpdateEntityRelationsExecutor;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+
+/**
+ * @author pkupczyk
+ */
+public interface IUpdateTagExperimentsExecutor extends IUpdateEntityRelationsExecutor<TagUpdate, MetaprojectPE>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagExperimentsWithCacheExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagExperimentsWithCacheExecutor.java
new file mode 100644
index 00000000000..d5c75556a79
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagExperimentsWithCacheExecutor.java
@@ -0,0 +1,32 @@
+/*
+ * 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.executor.tag;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.IExperimentId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IUpdateEntityRelationsWithCacheExecutor;
+import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+
+/**
+ * @author pkupczyk
+ */
+public interface IUpdateTagExperimentsWithCacheExecutor
+        extends IUpdateEntityRelationsWithCacheExecutor<TagUpdate, MetaprojectPE, IExperimentId, ExperimentPE>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagMaterialsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagMaterialsExecutor.java
new file mode 100644
index 00000000000..41c3b2ea0fe
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagMaterialsExecutor.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 ETH Zuerich, Scientific IT Services
+ *
+ * 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.tag;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IUpdateEntityRelationsExecutor;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+
+/**
+ * @author pkupczyk
+ */
+public interface IUpdateTagMaterialsExecutor extends IUpdateEntityRelationsExecutor<TagUpdate, MetaprojectPE>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagMaterialsWithCacheExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagMaterialsWithCacheExecutor.java
new file mode 100644
index 00000000000..7627be2ae83
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagMaterialsWithCacheExecutor.java
@@ -0,0 +1,32 @@
+/*
+ * 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.executor.tag;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.id.IMaterialId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IUpdateEntityRelationsWithCacheExecutor;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+
+/**
+ * @author pkupczyk
+ */
+public interface IUpdateTagMaterialsWithCacheExecutor
+        extends IUpdateEntityRelationsWithCacheExecutor<TagUpdate, MetaprojectPE, IMaterialId, MaterialPE>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagSamplesExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagSamplesExecutor.java
new file mode 100644
index 00000000000..6fecbcc4ef7
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagSamplesExecutor.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 ETH Zuerich, Scientific IT Services
+ *
+ * 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.tag;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IUpdateEntityRelationsExecutor;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+
+/**
+ * @author pkupczyk
+ */
+public interface IUpdateTagSamplesExecutor extends IUpdateEntityRelationsExecutor<TagUpdate, MetaprojectPE>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagSamplesWithCacheExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagSamplesWithCacheExecutor.java
new file mode 100644
index 00000000000..70a0906fa18
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/IUpdateTagSamplesWithCacheExecutor.java
@@ -0,0 +1,31 @@
+/*
+ * 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.executor.tag;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.ISampleId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IUpdateEntityRelationsWithCacheExecutor;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
+
+/**
+ * @author pkupczyk
+ */
+public interface IUpdateTagSamplesWithCacheExecutor extends IUpdateEntityRelationsWithCacheExecutor<TagUpdate, MetaprojectPE, ISampleId, SamplePE>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/RemoveTagFromEntityExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/RemoveTagFromEntityExecutor.java
index 734c91062ae..fd45e697882 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/RemoveTagFromEntityExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/RemoveTagFromEntityExecutor.java
@@ -25,7 +25,6 @@ import org.springframework.stereotype.Component;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.tag.TagAuthorization;
-import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.shared.dto.IEntityWithMetaprojects;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 
@@ -36,9 +35,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 public class RemoveTagFromEntityExecutor implements IRemoveTagFromEntityExecutor
 {
 
-    @Autowired
-    private IDAOFactory daoFactory;
-
     @Autowired
     private IMapTagByIdExecutor mapTagByIdExecutor;
 
@@ -55,7 +51,7 @@ public class RemoveTagFromEntityExecutor implements IRemoveTagFromEntityExecutor
     @Override
     public void removeTag(IOperationContext context, IEntityWithMetaprojects entity, Collection<? extends ITagId> tagIds)
     {
-        TagAuthorization authorization = new TagAuthorization(context, daoFactory);
+        TagAuthorization authorization = new TagAuthorization(context);
         Map<ITagId, MetaprojectPE> tagMap = mapTagByIdExecutor.map(context, tagIds);
 
         for (MetaprojectPE tag : tagMap.values())
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagForEntityExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagForEntityExecutor.java
index b1528e0ae80..f59a035394d 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagForEntityExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/SetTagForEntityExecutor.java
@@ -27,7 +27,6 @@ import org.springframework.stereotype.Component;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.tag.TagAuthorization;
-import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.shared.dto.IEntityWithMetaprojects;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 
@@ -38,30 +37,27 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 public class SetTagForEntityExecutor implements ISetTagForEntityExecutor
 {
 
-    @Autowired
-    private IDAOFactory daoFactory;
-
     @Autowired
     private IMapTagByIdExecutor mapTagByIdExecutor;
 
     @Autowired
-    private ICreateTagExecutor createTagExecutor;
+    private ICreateMissingTagExecutor createMissingTagExecutor;
 
     @SuppressWarnings("unused")
     private SetTagForEntityExecutor()
     {
     }
 
-    public SetTagForEntityExecutor(IMapTagByIdExecutor mapTagByIdExecutor, ICreateTagExecutor createTagExecutor)
+    public SetTagForEntityExecutor(IMapTagByIdExecutor mapTagByIdExecutor, ICreateMissingTagExecutor createMissingTagExecutor)
     {
         this.mapTagByIdExecutor = mapTagByIdExecutor;
-        this.createTagExecutor = createTagExecutor;
+        this.createMissingTagExecutor = createMissingTagExecutor;
     }
 
     @Override
     public void setTags(IOperationContext context, IEntityWithMetaprojects entity, Collection<? extends ITagId> tagIds)
     {
-        TagAuthorization authorization = new TagAuthorization(context, daoFactory);
+        TagAuthorization authorization = new TagAuthorization(context);
         Map<ITagId, MetaprojectPE> tagMap = mapTagByIdExecutor.map(context, tagIds);
         Set<MetaprojectPE> tags = new HashSet<MetaprojectPE>(tagMap.values());
 
@@ -75,15 +71,11 @@ public class SetTagForEntityExecutor implements ISetTagForEntityExecutor
 
         if (tagIds != null)
         {
+            createMissingTagExecutor.create(context, tagIds, tagMap);
+
             for (ITagId tagId : tagIds)
             {
                 MetaprojectPE tag = tagMap.get(tagId);
-
-                if (tag == null)
-                {
-                    tag = createTagExecutor.createTag(context, tagId);
-                }
-
                 authorization.checkAccess(tag);
                 entity.addMetaproject(tag);
             }
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
new file mode 100644
index 00000000000..3c7b8919a3f
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagDataSetsExecutor.java
@@ -0,0 +1,66 @@
+/*
+ * 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.tag;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.IDataSetId;
+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.systemsx.cisd.openbis.generic.shared.dto.DataPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+
+/**
+ * @author pkupczyk
+ */
+@Component
+public class UpdateTagDataSetsExecutor extends AbstractUpdateEntityMultipleRelationsExecutor<TagUpdate, MetaprojectPE, IDataSetId, DataPE>
+        implements IUpdateTagDataSetsExecutor
+{
+
+    @Autowired
+    private IMapDataSetByIdExecutor mapDataSetByIdExecutor;
+
+    @Autowired
+    private IUpdateTagDataSetsWithCacheExecutor updateTagDataSetsWithCacheExecutor;
+
+    @Override
+    protected void addRelatedIds(Set<IDataSetId> relatedIds, TagUpdate update)
+    {
+        addRelatedIds(relatedIds, update.getDataSetIds());
+    }
+
+    @Override
+    protected Map<IDataSetId, DataPE> map(IOperationContext context, Collection<IDataSetId> relatedIds)
+    {
+        return mapDataSetByIdExecutor.map(context, relatedIds);
+    }
+
+    @Override
+    protected void update(IOperationContext context, Map<TagUpdate, MetaprojectPE> entitiesMap, Map<IDataSetId, DataPE> relatedMap)
+    {
+        updateTagDataSetsWithCacheExecutor.update(context, entitiesMap, 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
new file mode 100644
index 00000000000..ece5c87c35d
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagDataSetsWithCacheExecutor.java
@@ -0,0 +1,65 @@
+/*
+ * 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.tag;
+
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.IdListUpdateValue;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.IDataSetId;
+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.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.MetaprojectAssignmentPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+
+/**
+ * @author pkupczyk
+ */
+@Component
+public class UpdateTagDataSetsWithCacheExecutor extends UpdateTagEntitiesExecutor<IDataSetId, DataPE> implements IUpdateTagDataSetsWithCacheExecutor
+{
+
+    @Override
+    protected Class<DataPE> getRelatedClass()
+    {
+        return DataPE.class;
+    }
+
+    @Override
+    protected DataPE getCurrentlyRelated(MetaprojectAssignmentPE entity)
+    {
+        return entity.getDataSet();
+    }
+
+    @Override
+    protected IdListUpdateValue<? extends IDataSetId> getRelatedUpdate(IOperationContext context, TagUpdate update)
+    {
+        return update.getDataSetIds();
+    }
+
+    @Override
+    protected void check(IOperationContext context, MetaprojectPE entity, IDataSetId relatedId, DataPE related)
+    {
+        if (false == new DataSetPEByExperimentOrSampleIdentifierValidator().doValidation(context.getSession().tryGetPerson(), related))
+        {
+            throw new UnauthorizedObjectAccessException(relatedId);
+        }
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagEntitiesExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagEntitiesExecutor.java
new file mode 100644
index 00000000000..9d9d96aed04
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagEntitiesExecutor.java
@@ -0,0 +1,103 @@
+/*
+ * 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.executor.tag;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+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.AbstractUpdateEntityToManyRelationExecutor;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.db.search.IFullTextIndexUpdateScheduler;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.db.search.IndexUpdateOperation;
+import ch.systemsx.cisd.openbis.generic.shared.dto.IEntityWithMetaprojects;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectAssignmentPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+
+/**
+ * @author pkupczyk
+ */
+public abstract class UpdateTagEntitiesExecutor<RELATED_ID, RELATED_PE extends IEntityWithMetaprojects>
+        extends AbstractUpdateEntityToManyRelationExecutor<TagUpdate, MetaprojectPE, RELATED_ID, RELATED_PE>
+{
+
+    @Autowired
+    private IDAOFactory daoFactory;
+
+    protected abstract Class<RELATED_PE> getRelatedClass();
+
+    protected abstract RELATED_PE getCurrentlyRelated(MetaprojectAssignmentPE entity);
+
+    @Override
+    public void update(IOperationContext context, Map<TagUpdate, MetaprojectPE> entitiesMap, Map<RELATED_ID, RELATED_PE> relatedMap)
+    {
+        super.update(context, entitiesMap, relatedMap);
+
+        daoFactory.getSessionFactory().getCurrentSession().flush();
+
+        IFullTextIndexUpdateScheduler indexUpdater = daoFactory.getPersistencyResources().getIndexUpdateScheduler();
+        List<Long> relatedIds = new ArrayList<Long>();
+
+        for (RELATED_PE related : relatedMap.values())
+        {
+            if (related != null)
+            {
+                relatedIds.add(related.getId());
+            }
+        }
+
+        if (false == relatedIds.isEmpty())
+        {
+            indexUpdater.scheduleUpdate(IndexUpdateOperation.reindex(getRelatedClass(), relatedIds));
+        }
+    }
+
+    @Override
+    protected Collection<RELATED_PE> getCurrentlyRelated(MetaprojectPE entity)
+    {
+        List<RELATED_PE> relatedList = new ArrayList<RELATED_PE>();
+
+        for (MetaprojectAssignmentPE assignment : entity.getAssignments())
+        {
+            RELATED_PE related = getCurrentlyRelated(assignment);
+            if (related != null)
+            {
+                relatedList.add(related);
+            }
+        }
+
+        return relatedList;
+    }
+
+    @Override
+    protected void add(IOperationContext context, MetaprojectPE entity, RELATED_PE related)
+    {
+        related.addMetaproject(entity);
+    }
+
+    @Override
+    protected void remove(IOperationContext context, MetaprojectPE entity, RELATED_PE related)
+    {
+        related.removeMetaproject(entity);
+    }
+
+}
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
new file mode 100644
index 00000000000..586cb6d03bd
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExecutor.java
@@ -0,0 +1,145 @@
+/*
+ * 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.tag;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
+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.tag.TagAuthorization;
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.openbis.generic.server.business.bo.DataAccessExceptionTranslator;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+
+/**
+ * @author pkupczyk
+ */
+@Component
+public class UpdateTagExecutor extends AbstractUpdateEntityExecutor<TagUpdate, MetaprojectPE, ITagId> implements
+        IUpdateTagExecutor
+{
+
+    @Autowired
+    private IDAOFactory daoFactory;
+
+    @Autowired
+    private IMapTagByIdExecutor mapTagByIdExecutor;
+
+    @Autowired
+    private IUpdateTagExperimentsExecutor updateTagExperimentsExecutor;
+
+    @Autowired
+    private IUpdateTagSamplesExecutor updateTagSamplesExecutor;
+
+    @Autowired
+    private IUpdateTagDataSetsExecutor updateTagDataSetsExecutor;
+
+    @Autowired
+    private IUpdateTagMaterialsExecutor updateTagMaterialsExecutor;
+
+    @Override
+    protected ITagId getId(TagUpdate update)
+    {
+        return update.getTagId();
+    }
+
+    @Override
+    protected void checkData(IOperationContext context, TagUpdate update)
+    {
+        if (update.getTagId() == null)
+        {
+            throw new UserFailureException("Tag id cannot be null.");
+        }
+    }
+
+    @Override
+    protected void checkAccess(IOperationContext context, ITagId id, MetaprojectPE entity)
+    {
+        if (false == new TagAuthorization(context).canAccess(entity))
+        {
+            throw new UnauthorizedObjectAccessException(id);
+        }
+    }
+
+    @Override
+    protected void checkBusinessRules(IOperationContext context, Collection<MetaprojectPE> entities)
+    {
+        // nothing to do
+    }
+
+    @Override
+    protected void updateBatch(IOperationContext context, Map<TagUpdate, MetaprojectPE> entitiesMap)
+    {
+        for (Map.Entry<TagUpdate, MetaprojectPE> entry : entitiesMap.entrySet())
+        {
+            TagUpdate update = entry.getKey();
+            MetaprojectPE tag = entry.getValue();
+
+            if (update.getDescription() != null && update.getDescription().isModified())
+            {
+                tag.setDescription(update.getDescription().getValue());
+            }
+        }
+    }
+
+    @Override
+    protected void updateAll(IOperationContext context, Map<TagUpdate, MetaprojectPE> entitiesMap)
+    {
+        updateTagExperimentsExecutor.update(context, entitiesMap);
+        updateTagSamplesExecutor.update(context, entitiesMap);
+        updateTagDataSetsExecutor.update(context, entitiesMap);
+        updateTagMaterialsExecutor.update(context, entitiesMap);
+    }
+
+    @Override
+    protected Map<ITagId, MetaprojectPE> map(IOperationContext context, Collection<ITagId> ids)
+    {
+        return mapTagByIdExecutor.map(context, ids);
+    }
+
+    @Override
+    protected List<MetaprojectPE> list(IOperationContext context, Collection<Long> ids)
+    {
+        return daoFactory.getMetaprojectDAO().listByIDs(ids);
+    }
+
+    @Override
+    protected void save(IOperationContext context, List<MetaprojectPE> entities, boolean clearCache)
+    {
+        for (MetaprojectPE entity : entities)
+        {
+            daoFactory.getMetaprojectDAO().validateAndSaveUpdatedEntity(entity);
+        }
+    }
+
+    @Override
+    protected void handleException(DataAccessException e)
+    {
+        DataAccessExceptionTranslator.throwException(e, "tag", null);
+    }
+
+}
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
new file mode 100644
index 00000000000..f81b9ccd443
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExperimentsExecutor.java
@@ -0,0 +1,66 @@
+/*
+ * 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.tag;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.IExperimentId;
+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.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+
+/**
+ * @author pkupczyk
+ */
+@Component
+public class UpdateTagExperimentsExecutor extends AbstractUpdateEntityMultipleRelationsExecutor<TagUpdate, MetaprojectPE, IExperimentId, ExperimentPE>
+        implements IUpdateTagExperimentsExecutor
+{
+
+    @Autowired
+    private IMapExperimentByIdExecutor mapExperimentByIdExecutor;
+
+    @Autowired
+    private IUpdateTagExperimentsWithCacheExecutor updateTagExperimentsWithCacheExecutor;
+
+    @Override
+    protected void addRelatedIds(Set<IExperimentId> relatedIds, TagUpdate update)
+    {
+        addRelatedIds(relatedIds, update.getExperimentIds());
+    }
+
+    @Override
+    protected Map<IExperimentId, ExperimentPE> map(IOperationContext context, Collection<IExperimentId> relatedIds)
+    {
+        return mapExperimentByIdExecutor.map(context, relatedIds);
+    }
+
+    @Override
+    protected void update(IOperationContext context, Map<TagUpdate, MetaprojectPE> entitiesMap, Map<IExperimentId, ExperimentPE> relatedMap)
+    {
+        updateTagExperimentsWithCacheExecutor.update(context, entitiesMap, 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
new file mode 100644
index 00000000000..7bbed276a12
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagExperimentsWithCacheExecutor.java
@@ -0,0 +1,66 @@
+/*
+ * 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.tag;
+
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.IdListUpdateValue;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.IExperimentId;
+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.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.MetaprojectAssignmentPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+
+/**
+ * @author pkupczyk
+ */
+@Component
+public class UpdateTagExperimentsWithCacheExecutor extends UpdateTagEntitiesExecutor<IExperimentId, ExperimentPE>
+        implements IUpdateTagExperimentsWithCacheExecutor
+{
+
+    @Override
+    protected Class<ExperimentPE> getRelatedClass()
+    {
+        return ExperimentPE.class;
+    }
+
+    @Override
+    protected ExperimentPE getCurrentlyRelated(MetaprojectAssignmentPE entity)
+    {
+        return entity.getExperiment();
+    }
+
+    @Override
+    protected IdListUpdateValue<? extends IExperimentId> getRelatedUpdate(IOperationContext context, TagUpdate update)
+    {
+        return update.getExperimentIds();
+    }
+
+    @Override
+    protected void check(IOperationContext context, MetaprojectPE entity, IExperimentId relatedId, ExperimentPE related)
+    {
+        if (false == new ExperimentByIdentiferValidator().doValidation(context.getSession().tryGetPerson(), related))
+        {
+            throw new UnauthorizedObjectAccessException(relatedId);
+        }
+    }
+
+}
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
new file mode 100644
index 00000000000..1c570ec5c89
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagMaterialsExecutor.java
@@ -0,0 +1,66 @@
+/*
+ * 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.tag;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.id.IMaterialId;
+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.systemsx.cisd.openbis.generic.shared.dto.MaterialPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+
+/**
+ * @author pkupczyk
+ */
+@Component
+public class UpdateTagMaterialsExecutor extends AbstractUpdateEntityMultipleRelationsExecutor<TagUpdate, MetaprojectPE, IMaterialId, MaterialPE>
+        implements IUpdateTagMaterialsExecutor
+{
+
+    @Autowired
+    private IMapMaterialByIdExecutor mapMaterialByIdExecutor;
+
+    @Autowired
+    private IUpdateTagMaterialsWithCacheExecutor updateTagMaterialsWithCacheExecutor;
+
+    @Override
+    protected void addRelatedIds(Set<IMaterialId> relatedIds, TagUpdate update)
+    {
+        addRelatedIds(relatedIds, update.getMaterialIds());
+    }
+
+    @Override
+    protected Map<IMaterialId, MaterialPE> map(IOperationContext context, Collection<IMaterialId> relatedIds)
+    {
+        return mapMaterialByIdExecutor.map(context, relatedIds);
+    }
+
+    @Override
+    protected void update(IOperationContext context, Map<TagUpdate, MetaprojectPE> entitiesMap, Map<IMaterialId, MaterialPE> relatedMap)
+    {
+        updateTagMaterialsWithCacheExecutor.update(context, entitiesMap, 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
new file mode 100644
index 00000000000..8b9ff6f2a6c
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagMaterialsWithCacheExecutor.java
@@ -0,0 +1,61 @@
+/*
+ * 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.tag;
+
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.IdListUpdateValue;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.id.IMaterialId;
+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.systemsx.cisd.openbis.generic.shared.dto.MaterialPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectAssignmentPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+
+/**
+ * @author pkupczyk
+ */
+@Component
+public class UpdateTagMaterialsWithCacheExecutor extends UpdateTagEntitiesExecutor<IMaterialId, MaterialPE>
+        implements IUpdateTagMaterialsWithCacheExecutor
+{
+
+    @Override
+    protected Class<MaterialPE> getRelatedClass()
+    {
+        return MaterialPE.class;
+    }
+
+    @Override
+    protected MaterialPE getCurrentlyRelated(MetaprojectAssignmentPE entity)
+    {
+        return entity.getMaterial();
+    }
+
+    @Override
+    protected IdListUpdateValue<? extends IMaterialId> getRelatedUpdate(IOperationContext context, TagUpdate update)
+    {
+        return update.getMaterialIds();
+    }
+
+    @Override
+    protected void check(IOperationContext context, MetaprojectPE entity, IMaterialId relatedId, MaterialPE related)
+    {
+        // nothing to check for materials
+    }
+
+}
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
new file mode 100644
index 00000000000..f95cf47f047
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagSamplesExecutor.java
@@ -0,0 +1,66 @@
+/*
+ * 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.tag;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.ISampleId;
+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.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
+
+/**
+ * @author pkupczyk
+ */
+@Component
+public class UpdateTagSamplesExecutor extends AbstractUpdateEntityMultipleRelationsExecutor<TagUpdate, MetaprojectPE, ISampleId, SamplePE>
+        implements IUpdateTagSamplesExecutor
+{
+
+    @Autowired
+    private IMapSampleByIdExecutor mapSampleByIdExecutor;
+
+    @Autowired
+    private IUpdateTagSamplesWithCacheExecutor updateTagSamplesWithCacheExecutor;
+
+    @Override
+    protected void addRelatedIds(Set<ISampleId> relatedIds, TagUpdate update)
+    {
+        addRelatedIds(relatedIds, update.getSampleIds());
+    }
+
+    @Override
+    protected Map<ISampleId, SamplePE> map(IOperationContext context, Collection<ISampleId> relatedIds)
+    {
+        return mapSampleByIdExecutor.map(context, relatedIds);
+    }
+
+    @Override
+    protected void update(IOperationContext context, Map<TagUpdate, MetaprojectPE> entitiesMap, Map<ISampleId, SamplePE> relatedMap)
+    {
+        updateTagSamplesWithCacheExecutor.update(context, entitiesMap, 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
new file mode 100644
index 00000000000..b10dddce7dd
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/tag/UpdateTagSamplesWithCacheExecutor.java
@@ -0,0 +1,66 @@
+/*
+ * 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.tag;
+
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.IdListUpdateValue;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.ISampleId;
+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.systemsx.cisd.openbis.generic.server.authorization.validator.SampleByIdentiferValidator;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectAssignmentPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
+
+/**
+ * @author pkupczyk
+ */
+@Component
+public class UpdateTagSamplesWithCacheExecutor extends UpdateTagEntitiesExecutor<ISampleId, SamplePE>
+        implements IUpdateTagSamplesWithCacheExecutor
+{
+
+    @Override
+    protected Class<SamplePE> getRelatedClass()
+    {
+        return SamplePE.class;
+    }
+
+    @Override
+    protected SamplePE getCurrentlyRelated(MetaprojectAssignmentPE entity)
+    {
+        return entity.getSample();
+    }
+
+    @Override
+    protected IdListUpdateValue<? extends ISampleId> getRelatedUpdate(IOperationContext context, TagUpdate update)
+    {
+        return update.getSampleIds();
+    }
+
+    @Override
+    protected void check(IOperationContext context, MetaprojectPE entity, ISampleId relatedId, SamplePE related)
+    {
+        if (false == new SampleByIdentiferValidator().doValidation(context.getSession().tryGetPerson(), related))
+        {
+            throw new UnauthorizedObjectAccessException(relatedId);
+        }
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/tag/TagAuthorization.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/tag/TagAuthorization.java
index 6ee88b04f6b..29506be4c11 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/tag/TagAuthorization.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/tag/TagAuthorization.java
@@ -19,9 +19,7 @@ package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.tag;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.TagPermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessException;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
-import ch.systemsx.cisd.common.exceptions.AuthorizationFailureException;
-import ch.systemsx.cisd.openbis.generic.server.authorization.AuthorizationServiceUtils;
-import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MetaprojectIdentifier;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 
 /**
@@ -30,27 +28,37 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 public class TagAuthorization
 {
 
-    private AuthorizationServiceUtils service;
+    private IOperationContext context;
 
-    public TagAuthorization(IOperationContext context, IDAOFactory daoFactory)
+    public TagAuthorization(IOperationContext context)
     {
-        this.service = new AuthorizationServiceUtils(daoFactory, context.getSession().tryGetPerson());
+        this.context = context;
     }
 
     public void checkAccess(MetaprojectPE tag)
     {
-        try
-        {
-            service.checkAccessMetaproject(tag);
-        } catch (AuthorizationFailureException e)
+        if (false == canAccess(tag.getOwner().getUserId()))
         {
             throw new UnauthorizedObjectAccessException(new TagPermId(tag.getOwner().getUserId(), tag.getName()));
         }
     }
 
+    public void checkAccess(MetaprojectIdentifier tagIdentifier)
+    {
+        if (false == canAccess(tagIdentifier.getMetaprojectOwnerId()))
+        {
+            throw new UnauthorizedObjectAccessException(new TagPermId(tagIdentifier.getMetaprojectOwnerId(), tagIdentifier.getMetaprojectName()));
+        }
+    }
+
     public boolean canAccess(MetaprojectPE tag)
     {
-        return service.canAccessMetaproject(tag);
+        return canAccess(tag.getOwner().getUserId());
+    }
+
+    private boolean canAccess(String owner)
+    {
+        return owner.equals(context.getSession().tryGetPerson().getUserId());
     }
 
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IMetaprojectDAO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IMetaprojectDAO.java
index 6cc9516fca9..b7cb69c6d77 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IMetaprojectDAO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IMetaprojectDAO.java
@@ -77,4 +77,9 @@ public interface IMetaprojectDAO extends IGenericDAO<MetaprojectPE>
      */
     public int getMetaprojectAssignmentsCount(Long metaprojectId, EntityKind entityKind);
 
+    /**
+     * List metaprojects by ids.
+     */
+    public List<MetaprojectPE> listByIDs(Collection<Long> ids);
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/MetaprojectDAO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/MetaprojectDAO.java
index dfde0d7c4ec..7b7bf1ddc68 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/MetaprojectDAO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/MetaprojectDAO.java
@@ -169,7 +169,8 @@ public class MetaprojectDAO extends AbstractGenericEntityDAO<MetaprojectPE> impl
         {
             operationLog.debug(String.format(
                     "%s(%s, %s): %d metaproject entity ids have been found.", MethodUtils
-                            .getCurrentMethod().getName(), metaprojectId, entityKind, idsAsLongs
+                            .getCurrentMethod().getName(),
+                    metaprojectId, entityKind, idsAsLongs
                             .size()));
         }
 
@@ -243,9 +244,32 @@ public class MetaprojectDAO extends AbstractGenericEntityDAO<MetaprojectPE> impl
         {
             operationLog.debug(String.format(
                     "%s(%s, %s): %d metaproject assignments have been found.", MethodUtils
-                            .getCurrentMethod().getName(), metaprojectId, entityKind, count));
+                            .getCurrentMethod().getName(),
+                    metaprojectId, entityKind, count));
         }
 
         return count.intValue();
     }
+
+    @Override
+    public List<MetaprojectPE> listByIDs(Collection<Long> ids)
+    {
+        return listByIDsOfName("id", ids);
+    }
+
+    private List<MetaprojectPE> listByIDsOfName(String idName, Collection<?> ids)
+    {
+        if (ids == null || ids.isEmpty())
+        {
+            return new ArrayList<MetaprojectPE>();
+        }
+        final List<MetaprojectPE> list =
+                DAOUtils.listByCollection(getHibernateTemplate(), MetaprojectPE.class, idName, ids);
+        if (operationLog.isDebugEnabled())
+        {
+            operationLog.debug(String.format("%d metaproject(s) have been found.", list.size()));
+        }
+        return list;
+    }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/MetaprojectPE.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/MetaprojectPE.java
index c89cadbbccb..dccc0642dd2 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/MetaprojectPE.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/MetaprojectPE.java
@@ -47,11 +47,11 @@ import org.hibernate.annotations.GenerationTime;
 import org.hibernate.search.annotations.Analyzer;
 import org.hibernate.search.annotations.DocumentId;
 import org.hibernate.search.annotations.Field;
-import org.hibernate.search.annotations.FieldBridge;
 import org.hibernate.search.annotations.Index;
 import org.hibernate.search.annotations.Store;
 
 import ch.systemsx.cisd.openbis.generic.shared.IServer;
+import ch.systemsx.cisd.openbis.generic.shared.basic.ICodeHolder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IIdHolder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MetaprojectIdentifier;
 import ch.systemsx.cisd.openbis.generic.shared.dto.hibernate.SearchFieldConstants;
@@ -62,9 +62,8 @@ import ch.systemsx.cisd.openbis.generic.shared.util.EqualsHashUtils;
  * @author Pawel Glyzewski
  */
 @Entity
-@Table(name = TableNames.METAPROJECTS_TABLE, uniqueConstraints = @UniqueConstraint(columnNames =
-{ ColumnNames.NAME_COLUMN, ColumnNames.OWNER }))
-public class MetaprojectPE implements Serializable, IIdHolder
+@Table(name = TableNames.METAPROJECTS_TABLE, uniqueConstraints = @UniqueConstraint(columnNames = { ColumnNames.NAME_COLUMN, ColumnNames.OWNER }) )
+public class MetaprojectPE implements Serializable, IIdHolder, ICodeHolder
 {
     private static final long serialVersionUID = IServer.VERSION;
 
@@ -109,6 +108,13 @@ public class MetaprojectPE implements Serializable, IIdHolder
         this.name = name;
     }
 
+    @Override
+    @Transient
+    public String getCode()
+    {
+        return getName();
+    }
+
     @Transient
     @Field(index = Index.YES, name = SearchFieldConstants.IDENTIFIER, store = Store.YES)
     @Analyzer(impl = IgnoreCaseAnalyzer.class)
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/tag/create/TagCreation.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/tag/create/TagCreation.js
new file mode 100644
index 00000000000..51f55b8d5a7
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/tag/create/TagCreation.js
@@ -0,0 +1,27 @@
+/**
+ * @author pkupczyk
+ */
+define([ "stjs" ], function(stjs) {
+	var TagCreation = function() {
+	};
+	stjs.extend(TagCreation, null, [], function(constructor, prototype) {
+		prototype['@type'] = 'as.dto.tag.create.TagCreation';
+		constructor.serialVersionUID = 1;
+		prototype.code = null;
+		prototype.description = null;
+
+		prototype.getCode = function() {
+			return this.code;
+		};
+		prototype.setCode = function(code) {
+			this.code = code;
+		};
+		prototype.getDescription = function() {
+			return this.description;
+		};
+		prototype.setDescription = function(description) {
+			this.description = description;
+		};
+	}, {});
+	return TagCreation;
+})
\ No newline at end of file
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/tag/update/TagUpdate.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/tag/update/TagUpdate.js
new file mode 100644
index 00000000000..c3badc702d4
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/tag/update/TagUpdate.js
@@ -0,0 +1,76 @@
+/**
+ * @author pkupczyk
+ */
+define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/common/update/IdListUpdateValue" ], function(stjs, FieldUpdateValue, IdListUpdateValue) {
+	var TagUpdate = function() {
+		this.description = new FieldUpdateValue();
+		this.experimentIds = new IdListUpdateValue();
+		this.sampleIds = new IdListUpdateValue();
+		this.dataSetIds = new IdListUpdateValue();
+		this.materialIds = new IdListUpdateValue();
+	};
+	stjs.extend(TagUpdate, null, [], function(constructor, prototype) {
+		prototype['@type'] = 'as.dto.tag.update.TagUpdate';
+		constructor.serialVersionUID = 1;
+		prototype.tagId = null;
+		prototype.description = null;
+		prototype.experimentIds = null;
+		prototype.sampleIds = null;
+		prototype.dataSetIds = null;
+		prototype.materialIds = null;
+
+		prototype.getTagId = function() {
+			return this.tagId;
+		};
+		prototype.setTagId = function(tagId) {
+			this.tagId = tagId;
+		};
+		prototype.getExperimentIds = function() {
+			return this.experimentIds;
+		};
+		prototype.setExperimentActions = function(actions) {
+			this.experimentIds.setActions(actions);
+		};
+		prototype.getSampleIds = function() {
+			return this.sampleIds;
+		};
+		prototype.setSampleActions = function(actions) {
+			this.sampleIds.setActions(actions);
+		};
+		prototype.getDataSetIds = function() {
+			return this.dataSetIds;
+		};
+		prototype.setDataSetActions = function(actions) {
+			this.dataSetIds.setActions(actions);
+		};
+		prototype.getMaterialIds = function() {
+			return this.materialIds;
+		};
+		prototype.setMaterialActions = function(actions) {
+			this.materialIds.setActions(actions);
+		};
+	}, {
+		tagId : "ITagId",
+		description : {
+			name : "FieldUpdateValue",
+			arguments : [ "String" ]
+		},
+		experimentIds : {
+			name : "IdListUpdateValue",
+			arguments : [ "IExperimentId" ]
+		},
+		sampleIds : {
+			name : "IdListUpdateValue",
+			arguments : [ "ISampleId" ]
+		},
+		dataSetIds : {
+			name : "IdListUpdateValue",
+			arguments : [ "IDataSetId" ]
+		},
+		materialIds : {
+			name : "IdListUpdateValue",
+			arguments : [ "IMaterialId" ]
+		}
+	});
+	return TagUpdate;
+})
\ No newline at end of file
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js
index 17654f7a30f..8a09498d7cf 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js
@@ -311,6 +311,17 @@ define([ 'jquery', 'util/Json' ], function($, stjsUtil) {
 			});
 		}
 
+		this.updateTags = function(updates) {
+			var thisFacade = this;
+			return thisFacade._private.ajaxRequest({
+				url : openbisUrl,
+				data : {
+					"method" : "updateTags",
+					"params" : [ thisFacade._private.sessionToken, updates ]
+				}
+			});
+		}
+
 		this.mapSpaces = function(ids, fetchOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdateTagTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdateTagTest.java
new file mode 100644
index 00000000000..55f692e541b
--- /dev/null
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdateTagTest.java
@@ -0,0 +1,369 @@
+/*
+ * 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.systemtest.asapi.v3;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import org.testng.annotations.Test;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.DataSetPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.ExperimentIdentifier;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.id.MaterialPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.SampleIdentifier;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.Tag;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.fetchoptions.TagFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.TagPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
+import ch.systemsx.cisd.common.action.IDelegatedAction;
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+
+/**
+ * @author pkupczyk
+ */
+@Test(groups = { "before remote api" })
+public class UpdateTagTest extends AbstractTest
+{
+
+    @Test(expectedExceptions = UserFailureException.class, expectedExceptionsMessageRegExp = ".*Tag id cannot be null.*")
+    public void testUpdateWithTagIdNull()
+    {
+        TagUpdate update = new TagUpdate();
+        updateTag(TEST_USER, PASSWORD, update);
+    }
+
+    @Test
+    public void testUpdateWithTagIdNonexistent()
+    {
+        final ITagId id = new TagPermId(TEST_USER, "IDONTEXIST");
+        assertObjectNotFoundException(new IDelegatedAction()
+            {
+                @Override
+                public void execute()
+                {
+                    TagUpdate update = new TagUpdate();
+                    update.setTagId(id);
+                    updateTag(TEST_USER, PASSWORD, update);
+                }
+            }, id);
+    }
+
+    @Test
+    public void testUpdateWithTagUnauthorized()
+    {
+        final ITagId id = new TagPermId(TEST_USER, "TEST_METAPROJECTS");
+        assertUnauthorizedObjectAccessException(new IDelegatedAction()
+            {
+                @Override
+                public void execute()
+                {
+                    TagUpdate update = new TagUpdate();
+                    update.setTagId(id);
+                    updateTag(TEST_SPACE_USER, PASSWORD, update);
+                }
+            }, id);
+    }
+
+    @Test
+    public void testUpdateWithDescription()
+    {
+        Tag before = getTag(TEST_USER, PASSWORD, new TagPermId(TEST_USER, "TEST_METAPROJECTS"));
+        assertEquals(before.getDescription(), "Example metaproject no. 1");
+
+        TagUpdate update = new TagUpdate();
+        update.setTagId(before.getPermId());
+        update.setDescription("brand new description");
+
+        Tag after = updateTag(TEST_USER, PASSWORD, update);
+
+        assertEquals(after.getDescription(), update.getDescription().getValue());
+    }
+
+    @Test
+    public void testUpdateWithExperimentsAdd()
+    {
+        Tag before = getTag(TEST_USER, PASSWORD, new TagPermId(TEST_USER, "TEST_METAPROJECTS"));
+        assertExperimentIdentifiers(before.getExperiments(), "/CISD/NEMO/EXP11", "/TEST-SPACE/TEST-PROJECT/EXP-SPACE-TEST");
+
+        TagUpdate update = new TagUpdate();
+        update.setTagId(before.getPermId());
+        update.getExperimentIds().add(new ExperimentIdentifier("/CISD/NEMO/EXP10"));
+
+        Tag after = updateTag(TEST_USER, PASSWORD, update);
+
+        assertExperimentIdentifiers(after.getExperiments(), "/CISD/NEMO/EXP10", "/CISD/NEMO/EXP11", "/TEST-SPACE/TEST-PROJECT/EXP-SPACE-TEST");
+    }
+
+    @Test
+    public void testUpdateWithExperimentsRemove()
+    {
+        Tag before = getTag(TEST_USER, PASSWORD, new TagPermId(TEST_USER, "TEST_METAPROJECTS"));
+        assertExperimentIdentifiers(before.getExperiments(), "/CISD/NEMO/EXP11", "/TEST-SPACE/TEST-PROJECT/EXP-SPACE-TEST");
+
+        TagUpdate update = new TagUpdate();
+        update.setTagId(before.getPermId());
+        update.getExperimentIds().remove(new ExperimentIdentifier("/CISD/NEMO/EXP11"));
+
+        Tag after = updateTag(TEST_USER, PASSWORD, update);
+
+        assertExperimentIdentifiers(after.getExperiments(), "/TEST-SPACE/TEST-PROJECT/EXP-SPACE-TEST");
+    }
+
+    @Test
+    public void testUpdateWithExperimentsSet()
+    {
+        Tag before = getTag(TEST_USER, PASSWORD, new TagPermId(TEST_USER, "TEST_METAPROJECTS"));
+        assertExperimentIdentifiers(before.getExperiments(), "/CISD/NEMO/EXP11", "/TEST-SPACE/TEST-PROJECT/EXP-SPACE-TEST");
+
+        TagUpdate update = new TagUpdate();
+        update.setTagId(before.getPermId());
+        update.getExperimentIds().set(new ExperimentIdentifier("/CISD/NEMO/EXP10"));
+
+        Tag after = updateTag(TEST_USER, PASSWORD, update);
+
+        assertExperimentIdentifiers(after.getExperiments(), "/CISD/NEMO/EXP10");
+    }
+
+    @Test
+    public void testUpdateWithExperimentsUnauthorized()
+    {
+        assertUnauthorizedObjectAccessException(new IDelegatedAction()
+            {
+                @Override
+                public void execute()
+                {
+                    Tag before = getTag(TEST_SPACE_USER, PASSWORD, new TagPermId(TEST_SPACE_USER, "TEST_METAPROJECTS"));
+                    assertEquals(before.getExperiments().size(), 0);
+
+                    TagUpdate update = new TagUpdate();
+                    update.setTagId(before.getPermId());
+                    update.getExperimentIds().add(new ExperimentIdentifier("/CISD/NEMO/EXP10"));
+
+                    updateTag(TEST_SPACE_USER, PASSWORD, update);
+                }
+            }, new ExperimentIdentifier("/CISD/NEMO/EXP10"));
+    }
+
+    @Test
+    public void testUpdateWithSamplesAdd()
+    {
+        Tag before = getTag(TEST_USER, PASSWORD, new TagPermId(TEST_USER, "TEST_METAPROJECTS"));
+        assertSampleIdentifiers(before.getSamples(), "/TEST-SPACE/EV-TEST");
+
+        TagUpdate update = new TagUpdate();
+        update.setTagId(before.getPermId());
+        update.getSampleIds().add(new SampleIdentifier("/TEST-SPACE/FV-TEST"));
+
+        Tag after = updateTag(TEST_USER, PASSWORD, update);
+
+        assertSampleIdentifiers(after.getSamples(), "/TEST-SPACE/EV-TEST", "/TEST-SPACE/FV-TEST");
+    }
+
+    @Test
+    public void testUpdateWithSamplesRemove()
+    {
+        Tag before = getTag(TEST_USER, PASSWORD, new TagPermId(TEST_USER, "TEST_METAPROJECTS"));
+        assertSampleIdentifiers(before.getSamples(), "/TEST-SPACE/EV-TEST");
+
+        TagUpdate update = new TagUpdate();
+        update.setTagId(before.getPermId());
+        update.getSampleIds().remove(new SampleIdentifier("/TEST-SPACE/EV-TEST"));
+
+        Tag after = updateTag(TEST_USER, PASSWORD, update);
+
+        assertEquals(after.getSamples().size(), 0);
+    }
+
+    @Test
+    public void testUpdateWithSamplesSet()
+    {
+        Tag before = getTag(TEST_USER, PASSWORD, new TagPermId(TEST_USER, "TEST_METAPROJECTS"));
+        assertSampleIdentifiers(before.getSamples(), "/TEST-SPACE/EV-TEST");
+
+        TagUpdate update = new TagUpdate();
+        update.setTagId(before.getPermId());
+        update.getSampleIds().set(new SampleIdentifier("/TEST-SPACE/FV-TEST"));
+
+        Tag after = updateTag(TEST_USER, PASSWORD, update);
+
+        assertSampleIdentifiers(after.getSamples(), "/TEST-SPACE/FV-TEST");
+    }
+
+    @Test
+    public void testUpdateWithSamplesUnauthorized()
+    {
+        assertUnauthorizedObjectAccessException(new IDelegatedAction()
+            {
+                @Override
+                public void execute()
+                {
+                    Tag before = getTag(TEST_SPACE_USER, PASSWORD, new TagPermId(TEST_SPACE_USER, "TEST_METAPROJECTS"));
+                    assertSampleIdentifiers(before.getSamples(), "/TEST-SPACE/EV-TEST", "/TEST-SPACE/FV-TEST");
+
+                    TagUpdate update = new TagUpdate();
+                    update.setTagId(before.getPermId());
+                    update.getSampleIds().add(new SampleIdentifier("/CISD/CP-TEST-1"));
+
+                    updateTag(TEST_SPACE_USER, PASSWORD, update);
+                }
+            }, new SampleIdentifier("/CISD/CP-TEST-1"));
+    }
+
+    @Test
+    public void testUpdateWithDataSetsAdd()
+    {
+        Tag before = getTag(TEST_USER, PASSWORD, new TagPermId(TEST_USER, "TEST_METAPROJECTS"));
+        assertDataSetCodes(before.getDataSets(), "20120619092259000-22");
+
+        TagUpdate update = new TagUpdate();
+        update.setTagId(before.getPermId());
+        update.getDataSetIds().add(new DataSetPermId("20081105092159111-1"));
+
+        Tag after = updateTag(TEST_USER, PASSWORD, update);
+
+        assertDataSetCodes(after.getDataSets(), "20120619092259000-22", "20081105092159111-1");
+    }
+
+    @Test
+    public void testUpdateWithDataSetsRemove()
+    {
+        Tag before = getTag(TEST_USER, PASSWORD, new TagPermId(TEST_USER, "TEST_METAPROJECTS"));
+        assertDataSetCodes(before.getDataSets(), "20120619092259000-22");
+
+        TagUpdate update = new TagUpdate();
+        update.setTagId(before.getPermId());
+        update.getDataSetIds().remove(new DataSetPermId("20120619092259000-22"));
+
+        Tag after = updateTag(TEST_USER, PASSWORD, update);
+
+        assertEquals(after.getDataSets().size(), 0);
+    }
+
+    @Test
+    public void testUpdateWithDataSetsSet()
+    {
+        Tag before = getTag(TEST_USER, PASSWORD, new TagPermId(TEST_USER, "TEST_METAPROJECTS"));
+        assertDataSetCodes(before.getDataSets(), "20120619092259000-22");
+
+        TagUpdate update = new TagUpdate();
+        update.setTagId(before.getPermId());
+        update.getDataSetIds().set(new DataSetPermId("20081105092159111-1"));
+
+        Tag after = updateTag(TEST_USER, PASSWORD, update);
+
+        assertDataSetCodes(after.getDataSets(), "20081105092159111-1");
+    }
+
+    @Test
+    public void testUpdateWithDataSetsUnauthorized()
+    {
+        assertUnauthorizedObjectAccessException(new IDelegatedAction()
+            {
+                @Override
+                public void execute()
+                {
+                    Tag before = getTag(TEST_SPACE_USER, PASSWORD, new TagPermId(TEST_SPACE_USER, "TEST_METAPROJECTS"));
+                    assertDataSetCodes(before.getDataSets(), "20120619092259000-22", "20120628092259000-24");
+
+                    TagUpdate update = new TagUpdate();
+                    update.setTagId(before.getPermId());
+                    // data set connected to experiment /CISD/NEMO/EXP-TEST-1
+                    update.getDataSetIds().add(new DataSetPermId("20081105092159111-1"));
+
+                    updateTag(TEST_SPACE_USER, PASSWORD, update);
+                }
+            }, new DataSetPermId("20081105092159111-1"));
+    }
+
+    @Test
+    public void testUpdateWithMaterialAdd()
+    {
+        Tag before = getTag(TEST_USER, PASSWORD, new TagPermId(TEST_USER, "TEST_METAPROJECTS"));
+        assertMaterialPermIds(before.getMaterials(), new MaterialPermId("AD3", "VIRUS"));
+
+        TagUpdate update = new TagUpdate();
+        update.setTagId(before.getPermId());
+        update.getMaterialIds().add(new MaterialPermId("AD5", "VIRUS"));
+
+        Tag after = updateTag(TEST_USER, PASSWORD, update);
+
+        assertMaterialPermIds(after.getMaterials(), new MaterialPermId("AD3", "VIRUS"), new MaterialPermId("AD5", "VIRUS"));
+    }
+
+    @Test
+    public void testUpdateWithMaterialRemove()
+    {
+        Tag before = getTag(TEST_USER, PASSWORD, new TagPermId(TEST_USER, "TEST_METAPROJECTS"));
+        assertMaterialPermIds(before.getMaterials(), new MaterialPermId("AD3", "VIRUS"));
+
+        TagUpdate update = new TagUpdate();
+        update.setTagId(before.getPermId());
+        update.getMaterialIds().remove(new MaterialPermId("AD3", "VIRUS"));
+
+        Tag after = updateTag(TEST_USER, PASSWORD, update);
+
+        assertEquals(after.getMaterials().size(), 0);
+    }
+
+    @Test
+    public void testUpdateWithMaterialSet()
+    {
+        Tag before = getTag(TEST_USER, PASSWORD, new TagPermId(TEST_USER, "TEST_METAPROJECTS"));
+        assertMaterialPermIds(before.getMaterials(), new MaterialPermId("AD3", "VIRUS"));
+
+        TagUpdate update = new TagUpdate();
+        update.setTagId(before.getPermId());
+        update.getMaterialIds().set(new MaterialPermId("AD5", "VIRUS"));
+
+        Tag after = updateTag(TEST_USER, PASSWORD, update);
+
+        assertMaterialPermIds(after.getMaterials(), new MaterialPermId("AD5", "VIRUS"));
+    }
+
+    @Test
+    public void testUpdateWithMaterialUnauthorized()
+    {
+        // nothing to test as the materials can be accessed by every user
+    }
+
+    private Tag getTag(String user, String password, ITagId tagId)
+    {
+        TagFetchOptions fetchOptions = new TagFetchOptions();
+        fetchOptions.withExperiments();
+        fetchOptions.withSamples();
+        fetchOptions.withDataSets();
+        fetchOptions.withMaterials();
+        fetchOptions.withOwner();
+
+        String sessionToken = v3api.login(user, password);
+        Map<ITagId, Tag> tags = v3api.mapTags(sessionToken, Arrays.asList(tagId), fetchOptions);
+        return tags.get(tagId);
+    }
+
+    private Tag updateTag(String user, String password, TagUpdate update)
+    {
+        String sessionToken = v3api.login(user, password);
+
+        v3api.updateTags(sessionToken, Arrays.asList(update));
+
+        return getTag(user, password, update.getTagId());
+    }
+
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/IApplicationServerApi.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/IApplicationServerApi.java
index 37b42082b88..d267ec35e51 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/IApplicationServerApi.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/IApplicationServerApi.java
@@ -93,8 +93,11 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.id.SpacePermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.search.SpaceSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.update.SpaceUpdate;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.Tag;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.create.TagCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.fetchoptions.TagFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.TagPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.update.TagUpdate;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.VocabularyTerm;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.create.VocabularyTermCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.delete.VocabularyTermDeletionOptions;
@@ -144,6 +147,8 @@ public interface IApplicationServerApi extends IRpcService
 
     public List<VocabularyTermPermId> createVocabularyTerms(String sessionToken, List<VocabularyTermCreation> newVocabularyTerms);
 
+    public List<TagPermId> createTags(String sessionToken, List<TagCreation> newTags);
+
     public void updateSpaces(String sessionToken, List<SpaceUpdate> spaceUpdates);
 
     public void updateProjects(String sessionToken, List<ProjectUpdate> projectUpdates);
@@ -158,6 +163,8 @@ public interface IApplicationServerApi extends IRpcService
 
     public void updateVocabularyTerms(String sessionToken, List<VocabularyTermUpdate> vocabularyTermUpdates);
 
+    public void updateTags(String sessionToken, List<TagUpdate> tagUpdates);
+
     public Map<ISpaceId, Space> mapSpaces(String sessionToken, List<? extends ISpaceId> spaceIds,
             SpaceFetchOptions fetchOptions);
 
@@ -185,20 +192,24 @@ public interface IApplicationServerApi extends IRpcService
     public SearchResult<Experiment> searchExperiments(String sessionToken, ExperimentSearchCriteria searchCriteria,
             ExperimentFetchOptions fetchOptions);
 
-    public SearchResult<ExperimentType> searchExperimentTypes(String sessionToken, EntityTypeSearchCriteria searchCriteria, ExperimentTypeFetchOptions fetchOptions);
-    
+    public SearchResult<ExperimentType> searchExperimentTypes(String sessionToken, EntityTypeSearchCriteria searchCriteria,
+            ExperimentTypeFetchOptions fetchOptions);
+
     public SearchResult<Sample> searchSamples(String sessionToken, SampleSearchCriteria searchCriteria, SampleFetchOptions fetchOptions);
 
-    public SearchResult<SampleType> searchSampleTypes(String sessionToken, EntityTypeSearchCriteria searchCriteria, SampleTypeFetchOptions fetchOptions);
-    
+    public SearchResult<SampleType> searchSampleTypes(String sessionToken, EntityTypeSearchCriteria searchCriteria,
+            SampleTypeFetchOptions fetchOptions);
+
     public SearchResult<DataSet> searchDataSets(String sessionToken, DataSetSearchCriteria searchCriteria, DataSetFetchOptions fetchOptions);
 
-    public SearchResult<DataSetType> searchDataSetTypes(String sessionToken, EntityTypeSearchCriteria searchCriteria, DataSetTypeFetchOptions fetchOptions);
-    
+    public SearchResult<DataSetType> searchDataSetTypes(String sessionToken, EntityTypeSearchCriteria searchCriteria,
+            DataSetTypeFetchOptions fetchOptions);
+
     public SearchResult<Material> searchMaterials(String sessionToken, MaterialSearchCriteria searchCriteria, MaterialFetchOptions fetchOptions);
 
-    public SearchResult<MaterialType> searchMaterialTypes(String sessionToken, EntityTypeSearchCriteria searchCriteria, MaterialTypeFetchOptions fetchOptions);
-    
+    public SearchResult<MaterialType> searchMaterialTypes(String sessionToken, EntityTypeSearchCriteria searchCriteria,
+            MaterialTypeFetchOptions fetchOptions);
+
     public SearchResult<VocabularyTerm> searchVocabularyTerms(String sessionToken, VocabularyTermSearchCriteria searchCriteria,
             VocabularyTermFetchOptions fetchOptions);
 
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/tag/create/TagCreation.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/tag/create/TagCreation.java
new file mode 100644
index 00000000000..de34417a392
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/tag/create/TagCreation.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2013 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.asapi.v3.dto.tag.create;
+
+import java.io.Serializable;
+
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author pkupczyk
+ */
+@JsonObject("as.dto.tag.create.TagCreation")
+public class TagCreation implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    private String code;
+
+    private String description;
+
+    public String getCode()
+    {
+        return code;
+    }
+
+    public void setCode(String code)
+    {
+        this.code = code;
+    }
+
+    public String getDescription()
+    {
+        return description;
+    }
+
+    public void setDescription(String description)
+    {
+        this.description = description;
+    }
+
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/tag/update/TagUpdate.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/tag/update/TagUpdate.java
new file mode 100644
index 00000000000..990adc8d568
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/tag/update/TagUpdate.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2013 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.asapi.v3.dto.tag.update;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+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.asapi.v3.dto.dataset.id.IDataSetId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.IExperimentId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.id.IMaterialId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.ISampleId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author pkupczyk
+ */
+@JsonObject("as.dto.tag.update.TagUpdate")
+public class TagUpdate implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    @JsonProperty
+    private ITagId tagId;
+
+    @JsonProperty
+    private FieldUpdateValue<String> description = new FieldUpdateValue<String>();
+
+    @JsonProperty
+    private IdListUpdateValue<IExperimentId> experimentIds = new IdListUpdateValue<IExperimentId>();
+
+    @JsonProperty
+    private IdListUpdateValue<ISampleId> sampleIds = new IdListUpdateValue<ISampleId>();
+
+    @JsonProperty
+    private IdListUpdateValue<IDataSetId> dataSetIds = new IdListUpdateValue<IDataSetId>();
+
+    @JsonProperty
+    private IdListUpdateValue<IMaterialId> materialIds = new IdListUpdateValue<IMaterialId>();
+
+    @JsonIgnore
+    public ITagId getTagId()
+    {
+        return tagId;
+    }
+
+    @JsonIgnore
+    public void setTagId(ITagId tagId)
+    {
+        this.tagId = tagId;
+    }
+
+    @JsonIgnore
+    public FieldUpdateValue<String> getDescription()
+    {
+        return description;
+    }
+
+    @JsonIgnore
+    public void setDescription(String description)
+    {
+        this.description.setValue(description);
+    }
+
+    @JsonIgnore
+    public IdListUpdateValue<IExperimentId> getExperimentIds()
+    {
+        return experimentIds;
+    }
+
+    @JsonIgnore
+    public void setExperimentActions(List<ListUpdateAction<IExperimentId>> actions)
+    {
+        experimentIds.setActions(actions);
+    }
+
+    @JsonIgnore
+    public IdListUpdateValue<ISampleId> getSampleIds()
+    {
+        return sampleIds;
+    }
+
+    @JsonIgnore
+    public void setSampleActions(List<ListUpdateAction<ISampleId>> actions)
+    {
+        sampleIds.setActions(actions);
+    }
+
+    @JsonIgnore
+    public IdListUpdateValue<IDataSetId> getDataSetIds()
+    {
+        return dataSetIds;
+    }
+
+    @JsonIgnore
+    public void setDataSetActions(List<ListUpdateAction<IDataSetId>> actions)
+    {
+        dataSetIds.setActions(actions);
+    }
+
+    @JsonIgnore
+    public IdListUpdateValue<IMaterialId> getMaterialIds()
+    {
+        return materialIds;
+    }
+
+    @JsonIgnore
+    public void setMaterialActions(List<ListUpdateAction<IMaterialId>> actions)
+    {
+        materialIds.setActions(actions);
+    }
+
+}
diff --git a/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt b/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt
index ef713ff42e7..4e56f69d794 100644
--- a/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt
+++ b/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt
@@ -1113,3 +1113,17 @@ map Tags
 set Materials
 with Materials
 with Materials Using
+get Data Set Ids
+get Experiment Ids
+get Material Ids
+get Sample Ids
+get Tag Id
+set Data Set Actions
+set Experiment Actions
+set Material Actions
+set Sample Actions
+set Tag Id
+Tag Update
+update Tags
+create Tags
+Tag Creation
\ No newline at end of file
-- 
GitLab