From fd11cab6472784ed89bcc86c4f38d95a16e0774f Mon Sep 17 00:00:00 2001
From: pkupczyk <pkupczyk>
Date: Wed, 17 Oct 2012 07:49:18 +0000
Subject: [PATCH] SP-344 / BIS-178 : Metaprojects: API methods should accept
 lists of permIds instead of lists of tech ids: - unit tests - bugfixes -
 javadocs

SVN: 27212
---
 .../openbis/generic/server/CommonServer.java  |  72 ++-
 .../generic/server/CommonServerLogger.java    |  34 +-
 .../v1/GeneralInformationChangingService.java |  12 +-
 ...neralInformationChangingServiceLogger.java |  27 +-
 .../server/dataaccess/db/MetaprojectDAO.java  |   2 +
 .../openbis/generic/shared/ICommonServer.java |   6 +-
 .../dataaccess/db/MetaprojectDAOTest.java     |  14 +
 ...GeneralInformationChangingServiceTest.java | 434 +++++++++++++++++-
 .../api/v1/GeneralInformationServiceTest.java |  25 +-
 .../sql/postgresql/124/043=metaprojects.tsv   |   1 +
 .../IGeneralInformationChangingService.java   |  18 +-
 .../api/v1/IGeneralInformationService.java    |   3 +
 .../shared/api/v1/dto/id/IObjectId.java       |   2 +
 .../api/v1/dto/id/ObjectIdentifierId.java     |   8 +
 .../shared/api/v1/dto/id/ObjectPermIdId.java  |   8 +
 .../shared/api/v1/dto/id/ObjectTechIdId.java  |   8 +
 .../api/v1/dto/id/dataset/DataSetCodeId.java  |  11 +
 .../v1/dto/id/dataset/DataSetTechIdId.java    |   5 +
 .../api/v1/dto/id/dataset/IDataSetId.java     |   2 +
 .../id/experiment/ExperimentIdentifierId.java |   5 +
 .../dto/id/experiment/ExperimentPermIdId.java |   5 +
 .../dto/id/experiment/ExperimentTechIdId.java |   5 +
 .../v1/dto/id/experiment/IExperimentId.java   |   2 +
 .../api/v1/dto/id/material/IMaterialId.java   |   2 +
 .../material/MaterialCodeAndTypeCodeId.java   |  12 +
 .../v1/dto/id/material/MaterialTechIdId.java  |   5 +
 .../v1/dto/id/metaproject/IMetaprojectId.java |   2 +
 .../metaproject/MetaprojectIdentifierId.java  |   5 +
 .../id/metaproject/MetaprojectTechIdId.java   |   5 +
 .../api/v1/dto/id/sample/ISampleId.java       |   2 +
 .../v1/dto/id/sample/SampleIdentifierId.java  |   6 +
 .../api/v1/dto/id/sample/SamplePermIdId.java  |   5 +
 .../api/v1/dto/id/sample/SampleTechIdId.java  |   5 +
 .../basic/dto/MetaprojectIdentifier.java      |  10 +-
 34 files changed, 688 insertions(+), 80 deletions(-)

diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
index 188e9875506..1f61940f265 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
@@ -66,7 +66,6 @@ import ch.systemsx.cisd.openbis.generic.server.authorization.predicate.DataSetUp
 import ch.systemsx.cisd.openbis.generic.server.authorization.predicate.DeletionTechIdCollectionPredicate;
 import ch.systemsx.cisd.openbis.generic.server.authorization.predicate.ExperimentUpdatesPredicate;
 import ch.systemsx.cisd.openbis.generic.server.authorization.predicate.ListSampleCriteriaPredicate;
-import ch.systemsx.cisd.openbis.generic.server.authorization.predicate.MetaprojectPredicate;
 import ch.systemsx.cisd.openbis.generic.server.authorization.predicate.ProjectUpdatesPredicate;
 import ch.systemsx.cisd.openbis.generic.server.authorization.predicate.RevertDeletionPredicate;
 import ch.systemsx.cisd.openbis.generic.server.authorization.predicate.SampleTechIdCollectionPredicate;
@@ -3450,14 +3449,14 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
     public MetaprojectAssignments getMetaprojectAssignments(String sessionToken,
             IMetaprojectId metaprojectId)
     {
-        Session session = getSession(sessionToken);
-        String baseIndexURL = getBaseIndexURL(sessionToken);
-
         if (metaprojectId == null)
         {
             throw new UserFailureException("Metaproject id cannot be null");
         }
 
+        Session session = getSession(sessionToken);
+        String baseIndexURL = getBaseIndexURL(sessionToken);
+
         IMetaprojectBO metaprojectBO = getBusinessObjectFactory().createMetaprojectBO(session);
         MetaprojectPE metaprojectPE = metaprojectBO.tryFindByMetaprojectId(metaprojectId);
 
@@ -3533,6 +3532,15 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
     public void addToMetaproject(String sessionToken, IMetaprojectId metaprojectId,
             MetaprojectAssignmentsIds assignmentsToAdd)
     {
+        if (metaprojectId == null)
+        {
+            throw new UserFailureException("Metaproject id cannot be null");
+        }
+        if (assignmentsToAdd == null)
+        {
+            throw new UserFailureException("Assignments to add cannot be null");
+        }
+
         Session session = getSession(sessionToken);
 
         IMetaprojectBO metaprojectBO = getBusinessObjectFactory().createMetaprojectBO(session);
@@ -3553,6 +3561,15 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
     public void removeFromMetaproject(String sessionToken, IMetaprojectId metaprojectId,
             MetaprojectAssignmentsIds assignmentsToRemove)
     {
+        if (metaprojectId == null)
+        {
+            throw new UserFailureException("Metaproject id cannot be null");
+        }
+        if (assignmentsToRemove == null)
+        {
+            throw new UserFailureException("Assignments to remove cannot be null");
+        }
+
         Session session = getSession(sessionToken);
 
         IMetaprojectBO metaprojectBO = getBusinessObjectFactory().createMetaprojectBO(session);
@@ -3572,6 +3589,11 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
     @RolesAllowed(RoleWithHierarchy.SPACE_USER)
     public void deleteMetaproject(String sessionToken, IMetaprojectId metaprojectId)
     {
+        if (metaprojectId == null)
+        {
+            throw new UserFailureException("Metaproject id cannot be null");
+        }
+
         Session session = getSession(sessionToken);
 
         IMetaprojectBO metaprojectBO = getBusinessObjectFactory().createMetaprojectBO(session);
@@ -3584,8 +3606,14 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
 
     @Override
     @RolesAllowed(RoleWithHierarchy.SPACE_USER)
-    public Metaproject registerMetaproject(String sessionToken, Metaproject metaproject)
+    public Metaproject registerMetaproject(String sessionToken, String name,
+            String descriptionOrNull)
     {
+        if (name == null)
+        {
+            throw new UserFailureException("Metaproject name cannot be null");
+        }
+
         Session session = getSession(sessionToken);
 
         IDAOFactory daoFactory = getDAOFactory();
@@ -3595,29 +3623,43 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
 
         MetaprojectPE metaprojectPE = new MetaprojectPE();
         metaprojectPE.setOwner(owner);
-        MetaprojectTranslator.translate(metaproject, metaprojectPE);
+        metaprojectPE.setName(name);
+        metaprojectPE.setDescription(descriptionOrNull);
+        metaprojectPE.setPrivate(true);
         metaprojectDAO.createOrUpdateMetaproject(metaprojectPE, session.tryGetPerson());
         return MetaprojectTranslator.translate(metaprojectPE);
     }
 
     @Override
     @RolesAllowed(RoleWithHierarchy.SPACE_USER)
-    public Metaproject updateMetaproject(String sessionToken,
-            @AuthorizationGuard(guardClass = MetaprojectPredicate.class)
-            Metaproject metaproject)
+    public Metaproject updateMetaproject(String sessionToken, IMetaprojectId metaprojectId,
+            String name, String descriptionOrNull)
     {
+        if (metaprojectId == null)
+        {
+            throw new UserFailureException("Metaproject id cannot be null");
+        }
+        if (name == null)
+        {
+            throw new UserFailureException("Metaproject name cannot be null");
+        }
+
         Session session = getSession(sessionToken);
 
-        IDAOFactory daoFactory = getDAOFactory();
-        IMetaprojectDAO metaprojectDAO = daoFactory.getMetaprojectDAO();
+        IMetaprojectBO metaprojectBO = getBusinessObjectFactory().createMetaprojectBO(session);
+        MetaprojectPE metaprojectPE = metaprojectBO.tryFindByMetaprojectId(metaprojectId);
 
-        if (metaproject.getId() == null)
+        if (metaprojectPE == null)
         {
-            throw new UserFailureException("The ID of metaproject for update cannot be null.");
+            throw new UserFailureException("Metaproject with id: " + metaprojectId
+                    + " doesn't exist");
         }
-        MetaprojectPE metaprojectPE = metaprojectDAO.getByTechId(new TechId(metaproject.getId()));
 
-        MetaprojectTranslator.translate(metaproject, metaprojectPE);
+        getAuthorizationService(session).checkAccessMetaproject(metaprojectPE);
+
+        metaprojectPE.setName(name);
+        metaprojectPE.setDescription(descriptionOrNull);
+        IMetaprojectDAO metaprojectDAO = getDAOFactory().getMetaprojectDAO();
         metaprojectDAO.createOrUpdateMetaproject(metaprojectPE, session.tryGetPerson());
         return MetaprojectTranslator.translate(metaprojectPE);
     }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServerLogger.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServerLogger.java
index 8efb3693c6a..107b5545801 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServerLogger.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServerLogger.java
@@ -1475,24 +1475,28 @@ final class CommonServerLogger extends AbstractServerLogger implements ICommonSe
     public void addToMetaproject(String sessionToken, IMetaprojectId metaprojectId,
             MetaprojectAssignmentsIds assignmentsToAdd)
     {
+        MetaprojectAssignmentsIds assignments =
+                assignmentsToAdd != null ? assignmentsToAdd : new MetaprojectAssignmentsIds();
+
         logAccess(sessionToken, "addToMetaproject",
                 "METAPROJECT_ID(%s), EXPERIMENTS(%s), SAMPLES(%s), DATA_SETS(%s), MATERIALS(%s)",
-                metaprojectId, abbreviate(assignmentsToAdd.getExperiments()),
-                abbreviate(assignmentsToAdd.getSamples()),
-                abbreviate(assignmentsToAdd.getDataSets()),
-                abbreviate(assignmentsToAdd.getMaterials()));
+                metaprojectId, abbreviate(assignments.getExperiments()),
+                abbreviate(assignments.getSamples()), abbreviate(assignments.getDataSets()),
+                abbreviate(assignments.getMaterials()));
     }
 
     @Override
     public void removeFromMetaproject(String sessionToken, IMetaprojectId metaprojectId,
             MetaprojectAssignmentsIds assignmentsToRemove)
     {
+        MetaprojectAssignmentsIds assignments =
+                assignmentsToRemove != null ? assignmentsToRemove : new MetaprojectAssignmentsIds();
+
         logAccess(sessionToken, "removeFromMetaproject",
                 "METAPROJECT_ID(%s), EXPERIMENTS(%s), SAMPLES(%s), DATA_SETS(%s), MATERIALS(%s)",
-                metaprojectId, abbreviate(assignmentsToRemove.getExperiments()),
-                abbreviate(assignmentsToRemove.getSamples()),
-                abbreviate(assignmentsToRemove.getDataSets()),
-                abbreviate(assignmentsToRemove.getMaterials()));
+                metaprojectId, abbreviate(assignments.getExperiments()),
+                abbreviate(assignments.getSamples()), abbreviate(assignments.getDataSets()),
+                abbreviate(assignments.getMaterials()));
     }
 
     @Override
@@ -1502,18 +1506,20 @@ final class CommonServerLogger extends AbstractServerLogger implements ICommonSe
     }
 
     @Override
-    public Metaproject registerMetaproject(String sessionToken, Metaproject metaproject)
+    public Metaproject registerMetaproject(String sessionToken, String name,
+            String descriptionOrNull)
     {
-        String identifier = metaproject == null ? "null" : metaproject.getIdentifier();
-        logAccess(sessionToken, "registerMetaproject", "METAPROJECT(%s)", identifier);
+        logAccess(sessionToken, "registerMetaproject", "NAME(%s) DESCRIPTION(%s)", name,
+                descriptionOrNull);
         return null;
     }
 
     @Override
-    public Metaproject updateMetaproject(String sessionToken, Metaproject metaproject)
+    public Metaproject updateMetaproject(String sessionToken, IMetaprojectId metaprojectId,
+            String name, String descriptionOrNull)
     {
-        String identifier = metaproject == null ? "null" : metaproject.getIdentifier();
-        logAccess(sessionToken, "updateMetaproject", "METAPROJECT(%s)", identifier);
+        logAccess(sessionToken, "updateMetaproject", "METAPROJECT_ID(%s) NAME(%s) DESCRIPTION(%s)",
+                metaprojectId, name, descriptionOrNull);
         return null;
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingService.java
index 8eec036e736..0405ff93b82 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingService.java
@@ -148,20 +148,18 @@ public class GeneralInformationChangingService extends
     @Override
     @Transactional(readOnly = false)
     @RolesAllowed(RoleWithHierarchy.SPACE_USER)
-    public Metaproject createMetaproject(String sessionToken, String name, String description)
+    public Metaproject createMetaproject(String sessionToken, String name, String descriptionOrNull)
     {
-        Metaproject metaproject = new Metaproject();
-        metaproject.setName(name);
-        metaproject.setDescription(description);
-        return server.registerMetaproject(sessionToken, metaproject);
+        return server.registerMetaproject(sessionToken, name, descriptionOrNull);
     }
 
     @Override
     @Transactional(readOnly = false)
     @RolesAllowed(RoleWithHierarchy.SPACE_USER)
-    public Metaproject updateMetaproject(String sessionToken, Metaproject metaproject)
+    public Metaproject updateMetaproject(String sessionToken, IMetaprojectId metaprojectId,
+            String name, String descriptionOrNull)
     {
-        return server.updateMetaproject(sessionToken, metaproject);
+        return server.updateMetaproject(sessionToken, metaprojectId, name, descriptionOrNull);
     }
 
     @Override
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingServiceLogger.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingServiceLogger.java
index cf0c2668ddd..5ce162954b0 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingServiceLogger.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationChangingServiceLogger.java
@@ -90,10 +90,11 @@ class GeneralInformationChangingServiceLogger extends AbstractServerLogger imple
     }
 
     @Override
-    public Metaproject updateMetaproject(String sessionToken, Metaproject metaproject)
+    public Metaproject updateMetaproject(String sessionToken, IMetaprojectId metaprojectId,
+            String name, String descriptionOrNull)
     {
-        String identifier = metaproject == null ? "null" : metaproject.getIdentifier();
-        logAccess(sessionToken, "updateMetaproject", "METAPROJECT(%s)", identifier);
+        logAccess(sessionToken, "updateMetaproject", "METAPROJECT_ID(%s) NAME(%s) DESCRIPTION(%s)",
+                metaprojectId, name, descriptionOrNull);
         return null;
     }
 
@@ -107,24 +108,28 @@ class GeneralInformationChangingServiceLogger extends AbstractServerLogger imple
     public void addToMetaproject(String sessionToken, IMetaprojectId metaprojectId,
             MetaprojectAssignmentsIds assignmentsToAdd)
     {
+        MetaprojectAssignmentsIds assignments =
+                assignmentsToAdd != null ? assignmentsToAdd : new MetaprojectAssignmentsIds();
+
         logAccess(sessionToken, "addToMetaproject",
                 "METAPROJECT_ID(%s), EXPERIMENTS(%s), SAMPLES(%s), DATA_SETS(%s), MATERIALS(%s)",
-                metaprojectId, abbreviate(assignmentsToAdd.getExperiments()),
-                abbreviate(assignmentsToAdd.getSamples()),
-                abbreviate(assignmentsToAdd.getDataSets()),
-                abbreviate(assignmentsToAdd.getMaterials()));
+                metaprojectId, abbreviate(assignments.getExperiments()),
+                abbreviate(assignments.getSamples()), abbreviate(assignments.getDataSets()),
+                abbreviate(assignments.getMaterials()));
     }
 
     @Override
     public void removeFromMetaproject(String sessionToken, IMetaprojectId metaprojectId,
             MetaprojectAssignmentsIds assignmentsToRemove)
     {
+        MetaprojectAssignmentsIds assignments =
+                assignmentsToRemove != null ? assignmentsToRemove : new MetaprojectAssignmentsIds();
+
         logAccess(sessionToken, "removeFromMetaproject",
                 "METAPROJECT_ID(%s), EXPERIMENTS(%s), SAMPLES(%s), DATA_SETS(%s), MATERIALS(%s)",
-                metaprojectId, abbreviate(assignmentsToRemove.getExperiments()),
-                abbreviate(assignmentsToRemove.getSamples()),
-                abbreviate(assignmentsToRemove.getDataSets()),
-                abbreviate(assignmentsToRemove.getMaterials()));
+                metaprojectId, abbreviate(assignments.getExperiments()),
+                abbreviate(assignments.getSamples()), abbreviate(assignments.getDataSets()),
+                abbreviate(assignments.getMaterials()));
     }
 
     @Override
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 ae53f3e8de7..084c9a7bd98 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
@@ -21,6 +21,7 @@ import java.util.List;
 import org.apache.log4j.Logger;
 import org.hibernate.SessionFactory;
 import org.hibernate.criterion.DetachedCriteria;
+import org.hibernate.criterion.Order;
 import org.hibernate.criterion.Restrictions;
 import org.springframework.orm.hibernate3.HibernateTemplate;
 
@@ -73,6 +74,7 @@ public class MetaprojectDAO extends AbstractGenericEntityDAO<MetaprojectPE> impl
     {
         final DetachedCriteria criteria = DetachedCriteria.forClass(MetaprojectPE.class);
         criteria.add(Restrictions.eq("owner", owner));
+        criteria.addOrder(Order.asc("name"));
         final List<MetaprojectPE> list = cast(getHibernateTemplate().findByCriteria(criteria));
         if (operationLog.isDebugEnabled())
         {
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java
index 519d907d7c0..88bec154d14 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java
@@ -1376,11 +1376,13 @@ public interface ICommonServer extends IServer
      * Registers a new metaproject.
      */
     @Transactional
-    public Metaproject registerMetaproject(String sessionToken, Metaproject metaproject);
+    public Metaproject registerMetaproject(String sessionToken, String name,
+            String descriptionOrNull);
 
     /**
      * Updates existing metaprojest.
      */
     @Transactional
-    public Metaproject updateMetaproject(String sessionToken, Metaproject metaproject);
+    public Metaproject updateMetaproject(String sessionToken, IMetaprojectId metaprojectId,
+            String name, String descriptionOrNull);
 }
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/MetaprojectDAOTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/MetaprojectDAOTest.java
index 54ddad5dc4e..705c436ebb8 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/MetaprojectDAOTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/MetaprojectDAOTest.java
@@ -33,6 +33,20 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
  */
 public class MetaprojectDAOTest extends AbstractDAOTest
 {
+
+    @Test
+    public void testFindByOwnerAndName()
+    {
+        MetaprojectPE metaproject =
+                daoFactory.getMetaprojectDAO().tryFindByOwnerAndName("test", "TEST_METAPROJECTS");
+        assertEquals(Long.valueOf(1), metaproject.getId());
+
+        metaproject =
+                daoFactory.getMetaprojectDAO().tryFindByOwnerAndName("test",
+                        "ANOTHER_TEST_METAPROJECTS");
+        assertEquals(Long.valueOf(3), metaproject.getId());
+    }
+
     @Test
     public void testListMetaprojects()
     {
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationChangingServiceTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationChangingServiceTest.java
index 10cca0a1bcd..c6f1a447687 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationChangingServiceTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationChangingServiceTest.java
@@ -17,20 +17,42 @@
 package ch.systemsx.cisd.openbis.systemtest.api.v1;
 
 import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertNull;
+import static org.testng.AssertJUnit.assertTrue;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 
 import org.springframework.beans.factory.annotation.Autowired;
-import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import ch.systemsx.cisd.common.exception.AuthorizationFailureException;
+import ch.systemsx.cisd.common.exception.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.shared.ICommonServer;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationChangingService;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationService;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.MetaprojectAssignments;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.MetaprojectAssignmentsIds;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.dataset.DataSetCodeId;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.dataset.DataSetTechIdId;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.experiment.ExperimentIdentifierId;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.experiment.ExperimentPermIdId;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.experiment.ExperimentTechIdId;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.material.MaterialCodeAndTypeCodeId;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.material.MaterialTechIdId;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.metaproject.MetaprojectIdentifierId;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.metaproject.MetaprojectTechIdId;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.sample.SampleIdentifierId;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.sample.SamplePermIdId;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.sample.SampleTechIdId;
+import ch.systemsx.cisd.openbis.generic.shared.basic.IIdHolder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Metaproject;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewETPTAssignment;
 import ch.systemsx.cisd.openbis.systemtest.PropertyHistory;
 import ch.systemsx.cisd.openbis.systemtest.SystemTestCase;
@@ -58,12 +80,6 @@ public class GeneralInformationChangingServiceTest extends SystemTestCase
         sessionToken = generalInformationService.tryToAuthenticateForAllServices("test", "a");
     }
 
-    @AfterMethod
-    public void afterMethod()
-    {
-        generalInformationService.logout(sessionToken);
-    }
-
     @Test
     public void testUpdateExperimentProperties()
     {
@@ -78,8 +94,8 @@ public class GeneralInformationChangingServiceTest extends SystemTestCase
         localCommonServer.assignPropertyType(sessionToken, new NewETPTAssignment(EntityKind.SAMPLE,
                 "GENDER", "CELL_PLATE", false, null, null, 1L, false, false, null, true));
         assertProperties("[ANY_MATERIAL: 2 (GENE), BACTERIUM: BACTERIUM-Y (BACTERIUM), "
-                + "COMMENT: extremely simple stuff, ORGANISM: GORILLA, SIZE: 321]", localCommonServer
-                .getSampleInfo(sessionToken, id).getParent());
+                + "COMMENT: extremely simple stuff, ORGANISM: GORILLA, SIZE: 321]",
+                localCommonServer.getSampleInfo(sessionToken, id).getParent());
         HashMap<String, String> properties = new HashMap<String, String>();
         properties.put("SIZE", "42");
         properties.put("any_material", "1 (GENE)");
@@ -99,6 +115,406 @@ public class GeneralInformationChangingServiceTest extends SystemTestCase
         assertEquals(
                 "[ANY_MATERIAL: material:2 [GENE]<a:1>, ORGANISM: term:GORILLA [ORGANISM]<a:1>, SIZE: 321<a:1>]",
                 history.toString());
+    }
+
+    @Test
+    public void testCreateMetaproject()
+    {
+        String name = "BRAND_NEW_METAPROJECT";
+        String description = "I'm brand new";
+
+        List<String> beforeNames = listMetaprojectNames();
+        assertFalse(beforeNames.contains(name));
+
+        Metaproject metaproject =
+                generalInformationChangingService
+                        .createMetaproject(sessionToken, name, description);
+
+        List<String> afterNames = listMetaprojectNames();
+        assertTrue(afterNames.contains(name));
+
+        assertEquals(beforeNames.size() + 1, afterNames.size());
+        afterNames.remove(name);
+        assertEquals(beforeNames, afterNames);
+
+        assertNotNull(metaproject.getId());
+        assertEquals("test", metaproject.getOwnerId());
+        assertEquals("/test/" + name, metaproject.getIdentifier());
+        assertEquals(name, metaproject.getName());
+        assertEquals(description, metaproject.getDescription());
+        assertNotNull(metaproject.getCreationDate());
+    }
+
+    @Test(expectedExceptions = UserFailureException.class)
+    public void testCreateMetaprojectWithNameDuplicatedForSameOwner()
+    {
+        generalInformationChangingService.createMetaproject(sessionToken, "TEST_METAPROJECTS",
+                "My name is already used by the same owner");
+    }
+
+    @Test
+    public void testCreateMetaprojectWithNameDuplicatedForDifferentOwner()
+    {
+        generalInformationChangingService.createMetaproject(sessionToken, "TEST_METAPROJECTS_2",
+                "My name is already used by a different owner");
+    }
+
+    @Test(expectedExceptions = UserFailureException.class)
+    public void testCreateMetaprojectWithEmptyName()
+    {
+        generalInformationChangingService.createMetaproject(sessionToken, null, "My name is empty");
+    }
+
+    @Test
+    public void testCreateMetaprojectWithEmptyDescription()
+    {
+        String name = "METAPROJECT_WITH_EMPTY_DESCRIPTION";
+
+        Metaproject metaproject =
+                generalInformationChangingService.createMetaproject(sessionToken, name, null);
+        assertEquals(name, metaproject.getName());
+        assertNull(metaproject.getDescription());
+    }
+
+    @Test
+    public void testUpdateMetaproject()
+    {
+        String beforeName = "TEST_METAPROJECTS";
+        String afterName = "TEST_METAPROJECTS_UPDATED";
+        String description = "My description is brand new";
+
+        List<String> beforeNames = listMetaprojectNames();
+        assertTrue(beforeNames.contains(beforeName));
+        assertFalse(beforeNames.contains(afterName));
+
+        Metaproject metaproject =
+                generalInformationChangingService.updateMetaproject(sessionToken,
+                        new MetaprojectIdentifierId("/test/" + beforeName), afterName, description);
+
+        List<String> afterNames = listMetaprojectNames();
+        assertFalse(afterNames.contains(beforeName));
+        assertTrue(afterNames.contains(afterName));
+
+        assertEquals(beforeNames.size(), afterNames.size());
+        beforeNames.remove(beforeName);
+        afterNames.remove(afterName);
+        assertEquals(beforeNames, afterNames);
+
+        assertNotNull(metaproject.getId());
+        assertEquals("test", metaproject.getOwnerId());
+        assertEquals("/test/" + afterName, metaproject.getIdentifier());
+        assertEquals(afterName, metaproject.getName());
+        assertEquals(description, metaproject.getDescription());
+        assertNotNull(metaproject.getCreationDate());
+    }
+
+    @Test(expectedExceptions = AuthorizationFailureException.class)
+    public void testUpdateMetaprojectOwnedBySomebodyElse()
+    {
+        generalInformationChangingService.updateMetaproject(sessionToken,
+                new MetaprojectIdentifierId("/test_role/TEST_METAPROJECTS"), "SOME_NEW_NAME",
+                "some new description");
+    }
+
+    @Test(expectedExceptions = UserFailureException.class)
+    public void testUpdateMetaprojectWithEmptyId()
+    {
+        generalInformationChangingService.updateMetaproject(sessionToken, null, "SOME_NEW_NAME",
+                "some new description");
+    }
+
+    @Test(expectedExceptions = UserFailureException.class)
+    public void testUpdateMetaprojectThatDoesntExist()
+    {
+        generalInformationChangingService.updateMetaproject(sessionToken,
+                new MetaprojectIdentifierId("/test/METAPROJECT_THAT_DOESNT_EXIST"),
+                "SOME_NEW_NAME", "some new description");
+    }
+
+    @Test(expectedExceptions = UserFailureException.class)
+    public void testUpdateMetaprojectToEmptyName()
+    {
+        generalInformationChangingService.updateMetaproject(sessionToken,
+                new MetaprojectIdentifierId("/test/TEST_METAPROJECTS"), null,
+                "some new description");
+    }
+
+    @Test
+    public void testUpdateMetaprojectToEmptyDescription()
+    {
+        generalInformationChangingService.updateMetaproject(sessionToken,
+                new MetaprojectIdentifierId("/test/TEST_METAPROJECTS"), "TEST_METAPROJECTS", null);
+    }
+
+    @Test
+    public void testDeleteMetaproject()
+    {
+        String name = "TEST_METAPROJECTS";
+
+        List<String> beforeNames = listMetaprojectNames();
+        assertTrue(beforeNames.contains(name));
+
+        generalInformationChangingService.deleteMetaproject(sessionToken,
+                new MetaprojectIdentifierId("/test/" + name));
+
+        List<String> afterNames = listMetaprojectNames();
+        assertFalse(afterNames.contains(name));
+
+        assertEquals(beforeNames.size() - 1, afterNames.size());
+        beforeNames.remove(name);
+        assertEquals(beforeNames, afterNames);
+    }
+
+    @Test(expectedExceptions = AuthorizationFailureException.class)
+    public void testDeleteMetaprojectOwnedBySomebodyElse()
+    {
+        generalInformationChangingService.deleteMetaproject(sessionToken,
+                new MetaprojectIdentifierId("/test_role/TEST_METAPROJECTS"));
+    }
+
+    @Test(expectedExceptions = UserFailureException.class)
+    public void testDeleteMetaprojectWithEmptyId()
+    {
+        generalInformationChangingService.deleteMetaproject(sessionToken, null);
+    }
+
+    @Test(expectedExceptions = UserFailureException.class)
+    public void testDeleteMetaprojectThatDoesntExist()
+    {
+        generalInformationChangingService.deleteMetaproject(sessionToken,
+                new MetaprojectIdentifierId("/test/METAPROJECT_THAT_DOESNT_EXIST"));
+    }
+
+    @Test
+    public void testAddToMetaproject()
+    {
+        Metaproject metaproject = createMetaprojectWithAssignments();
+
+        MetaprojectAssignments assignments =
+                generalInformationService.getMetaproject(sessionToken, new MetaprojectIdentifierId(
+                        metaproject.getIdentifier()));
+
+        List<Long> experimentIds = getObjectIds(assignments.getExperiments());
+        assertEquals(3, experimentIds.size());
+        assertTrue(experimentIds.contains(2L));
+        assertTrue(experimentIds.contains(22L));
+        assertTrue(experimentIds.contains(23L));
+
+        List<Long> samplesIds = getObjectIds(assignments.getSamples());
+        assertEquals(4, samplesIds.size());
+        assertTrue(samplesIds.contains(647L));
+        assertTrue(samplesIds.contains(602L));
+        assertTrue(samplesIds.contains(340L));
+        assertTrue(samplesIds.contains(342L));
 
+        List<Long> dataSetIds = getObjectIds(assignments.getDataSets());
+        assertEquals(2, dataSetIds.size());
+        assertTrue(dataSetIds.contains(8L));
+        assertTrue(dataSetIds.contains(12L));
+
+        List<Long> materialIds = getObjectIds(assignments.getMaterials());
+        assertEquals(2, materialIds.size());
+        assertTrue(materialIds.contains(18L));
+        assertTrue(materialIds.contains(8L));
+    }
+
+    @Test(expectedExceptions = AuthorizationFailureException.class)
+    public void testAddToMetaprojectOwnedBySomebodyElse()
+    {
+        generalInformationChangingService.addToMetaproject(sessionToken,
+                new MetaprojectIdentifierId("/test_role/TEST_METAPROJECTS"),
+                new MetaprojectAssignmentsIds());
+    }
+
+    @Test
+    public void testAddToMetaprojectAssignmentsThatAlreadyExist()
+    {
+        Metaproject metaproject =
+                generalInformationChangingService.createMetaproject(sessionToken,
+                        "BRAND_NEW_METAPROJECT", null);
+
+        MetaprojectAssignmentsIds assignmentsToAdd = new MetaprojectAssignmentsIds();
+        assignmentsToAdd.addExperiment(new ExperimentIdentifierId("/CISD/NEMO/EXP1"));
+
+        generalInformationChangingService.addToMetaproject(sessionToken, new MetaprojectTechIdId(
+                metaproject.getId()), assignmentsToAdd);
+        generalInformationChangingService.addToMetaproject(sessionToken, new MetaprojectTechIdId(
+                metaproject.getId()), assignmentsToAdd);
+
+        MetaprojectAssignments assignments =
+                generalInformationService.getMetaproject(sessionToken, new MetaprojectTechIdId(
+                        metaproject.getId()));
+        assertEquals(1, assignments.getExperiments().size());
+    }
+
+    @Test(expectedExceptions = UserFailureException.class)
+    public void testAddToMetaprojectWithEmptyId()
+    {
+        generalInformationChangingService.addToMetaproject(sessionToken, null,
+                new MetaprojectAssignmentsIds());
+    }
+
+    @Test(expectedExceptions = UserFailureException.class)
+    public void testAddToMetaprojectWithNullAssignments()
+    {
+        generalInformationChangingService.addToMetaproject(sessionToken,
+                new MetaprojectIdentifierId("/test/TEST_METAPROJECTS"), null);
+    }
+
+    @Test
+    public void testAddToMetaprojectWithEmptyAssignments()
+    {
+        generalInformationChangingService.addToMetaproject(sessionToken,
+                new MetaprojectIdentifierId("/test/TEST_METAPROJECTS"),
+                new MetaprojectAssignmentsIds());
+    }
+
+    @Test(expectedExceptions = UserFailureException.class)
+    public void testAddToMetaprojectThatDoesntExist()
+    {
+        generalInformationChangingService.addToMetaproject(sessionToken,
+                new MetaprojectIdentifierId("/test/METAPROJECT_THAT_DOESNT_EXIST"),
+                new MetaprojectAssignmentsIds());
+    }
+
+    @Test
+    public void testRemoveFromMetaproject()
+    {
+        Metaproject metaproject = createMetaprojectWithAssignments();
+
+        MetaprojectAssignmentsIds assignmentsToRemove = new MetaprojectAssignmentsIds();
+        assignmentsToRemove.addExperiment(new ExperimentIdentifierId("/CISD/NEMO/EXP1"));
+        assignmentsToRemove.addSample(new SampleIdentifierId("/A03"));
+        assignmentsToRemove.addDataSet(new DataSetCodeId("20081105092259000-8"));
+        assignmentsToRemove.addMaterial(new MaterialCodeAndTypeCodeId("GFP", "CONTROL"));
+
+        generalInformationChangingService.removeFromMetaproject(sessionToken,
+                new MetaprojectIdentifierId(metaproject.getIdentifier()), assignmentsToRemove);
+
+        MetaprojectAssignments assignments =
+                generalInformationService.getMetaproject(sessionToken, new MetaprojectIdentifierId(
+                        metaproject.getIdentifier()));
+
+        List<Long> experimentIds = getObjectIds(assignments.getExperiments());
+        assertEquals(2, experimentIds.size());
+        assertTrue(experimentIds.contains(22L));
+        assertTrue(experimentIds.contains(23L));
+
+        List<Long> samplesIds = getObjectIds(assignments.getSamples());
+        assertEquals(3, samplesIds.size());
+        assertTrue(samplesIds.contains(602L));
+        assertTrue(samplesIds.contains(340L));
+        assertTrue(samplesIds.contains(342L));
+
+        List<Long> dataSetIds = getObjectIds(assignments.getDataSets());
+        assertEquals(1, dataSetIds.size());
+        assertTrue(dataSetIds.contains(12L));
+
+        List<Long> materialIds = getObjectIds(assignments.getMaterials());
+        assertEquals(1, materialIds.size());
+        assertTrue(materialIds.contains(8L));
+    }
+
+    @Test(expectedExceptions = UserFailureException.class)
+    public void testRemoveFromMetaprojectOwnedBySomebodyElse()
+    {
+        generalInformationChangingService.removeFromMetaproject(sessionToken,
+                new MetaprojectIdentifierId("/test_role/TEST_METAPROJECTS"),
+                new MetaprojectAssignmentsIds());
     }
+
+    public void testRemoveFromMetaprojectAssignmentsThatDoNotExist()
+    {
+        Metaproject metaproject = createMetaprojectWithAssignments();
+        MetaprojectAssignmentsIds assignmentsToRemove = new MetaprojectAssignmentsIds();
+        assignmentsToRemove.addExperiment(new ExperimentIdentifierId("/CISD/NEMO/EXP10"));
+
+        generalInformationChangingService.removeFromMetaproject(sessionToken,
+                new MetaprojectIdentifierId(metaproject.getIdentifier()), assignmentsToRemove);
+    }
+
+    @Test(expectedExceptions = UserFailureException.class)
+    public void testRemoveFromMetaprojectWithEmptyId()
+    {
+        generalInformationChangingService.removeFromMetaproject(sessionToken, null,
+                new MetaprojectAssignmentsIds());
+    }
+
+    @Test(expectedExceptions = UserFailureException.class)
+    public void testRemoveFromMetaprojectWithNullAssignments()
+    {
+        generalInformationChangingService.removeFromMetaproject(sessionToken,
+                new MetaprojectIdentifierId("/test/TEST_METAPROJECTS"), null);
+    }
+
+    @Test
+    public void testRemoveFromMetaprojectWithEmptyAssignments()
+    {
+        generalInformationChangingService.removeFromMetaproject(sessionToken,
+                new MetaprojectIdentifierId("/test/TEST_METAPROJECTS"),
+                new MetaprojectAssignmentsIds());
+    }
+
+    @Test(expectedExceptions = UserFailureException.class)
+    public void testRemoveFromMetaprojectThatDoesntExist()
+    {
+        generalInformationChangingService.removeFromMetaproject(sessionToken,
+                new MetaprojectIdentifierId("/test/METAPROJECT_THAT_DOESNT_EXIST"),
+                new MetaprojectAssignmentsIds());
+    }
+
+    private List<String> listMetaprojectNames()
+    {
+        List<Metaproject> metaprojects = generalInformationService.listMetaprojects(sessionToken);
+        List<String> names = new ArrayList<String>();
+
+        for (Metaproject metaproject : metaprojects)
+        {
+            names.add(metaproject.getName());
+        }
+
+        return names;
+    }
+
+    private Metaproject createMetaprojectWithAssignments()
+    {
+        Metaproject metaproject =
+                generalInformationChangingService.createMetaproject(sessionToken,
+                        "BRAND_NEW_METAPROJECT", null);
+
+        MetaprojectAssignmentsIds assignmentsToAdd = new MetaprojectAssignmentsIds();
+
+        assignmentsToAdd.addExperiment(new ExperimentIdentifierId("/CISD/NEMO/EXP1")); // id: 2
+        assignmentsToAdd.addExperiment(new ExperimentPermIdId("201108050937246-1031")); // id: 22
+        assignmentsToAdd.addExperiment(new ExperimentTechIdId(23L)); // id: 23
+
+        assignmentsToAdd.addSample(new SampleIdentifierId("/A03")); // id: 647
+        assignmentsToAdd.addSample(new SampleIdentifierId("/CISD/N19")); // id: 602
+        assignmentsToAdd.addSample(new SamplePermIdId("200811050917877-346")); // id: 340
+        assignmentsToAdd.addSample(new SampleTechIdId(342L)); // id: 342
+
+        assignmentsToAdd.addDataSet(new DataSetCodeId("20081105092259000-8")); // id: 8
+        assignmentsToAdd.addDataSet(new DataSetTechIdId(12L)); // id: 12
+
+        assignmentsToAdd.addMaterial(new MaterialCodeAndTypeCodeId("GFP", "CONTROL")); // id: 18
+        assignmentsToAdd.addMaterial(new MaterialTechIdId(8L)); // id: 8
+
+        generalInformationChangingService.addToMetaproject(sessionToken, new MetaprojectTechIdId(
+                metaproject.getId()), assignmentsToAdd);
+
+        return metaproject;
+    }
+
+    private List<Long> getObjectIds(List<? extends IIdHolder> objects)
+    {
+        List<Long> ids = new ArrayList<Long>();
+
+        for (IIdHolder object : objects)
+        {
+            ids.add(object.getId());
+        }
+
+        return ids;
+    }
+
 }
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationServiceTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationServiceTest.java
index fb6306268ee..00d64e09ff3 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationServiceTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationServiceTest.java
@@ -61,7 +61,7 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchCl
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClauseAttribute;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClauseTimeAttribute;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SpaceWithProjectsAndRoleAssignments;
-import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.metaproject.MetaprojectTechIdId;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.metaproject.MetaprojectIdentifierId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IIdentifierHolder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
@@ -1026,16 +1026,19 @@ public class GeneralInformationServiceTest extends SystemTestCase
         assertEquals(2, metaprojects.size());
     }
 
+    @Test(expectedExceptions = AuthorizationFailureException.class)
+    public void testGetMetaprojectOwnedBySomebodyElse()
+    {
+        generalInformationService.getMetaproject(sessionToken, new MetaprojectIdentifierId(
+                "/test_role/TEST_METAPROJECTS"));
+    }
+
     @Test
     public void testGetMetaprojects()
     {
-        List<Metaproject> metaprojects = generalInformationService.listMetaprojects(sessionToken);
-
-        Metaproject metaproject = metaprojects.iterator().next();
-
         MetaprojectAssignments metaprojectAssignments =
-                generalInformationService.getMetaproject(sessionToken, new MetaprojectTechIdId(
-                        metaproject.getId()));
+                generalInformationService.getMetaproject(sessionToken, new MetaprojectIdentifierId(
+                        "/test/TEST_METAPROJECTS"));
 
         assertEquals(1, metaprojectAssignments.getDataSets().size());
         assertEquals(1, metaprojectAssignments.getMaterials().size());
@@ -1045,12 +1048,9 @@ public class GeneralInformationServiceTest extends SystemTestCase
         generalInformationService.logout(sessionToken);
         sessionToken = generalInformationService.tryToAuthenticateForAllServices("test_role", "a");
 
-        metaprojects = generalInformationService.listMetaprojects(sessionToken);
-        metaproject = metaprojects.iterator().next();
-
         metaprojectAssignments =
-                generalInformationService.getMetaproject(sessionToken, new MetaprojectTechIdId(
-                        metaproject.getId()));
+                generalInformationService.getMetaproject(sessionToken, new MetaprojectIdentifierId(
+                        "/test_role/TEST_METAPROJECTS"));
 
         assertEquals(1, metaprojectAssignments.getMaterials().size());
         assertEquals(1, metaprojectAssignments.getDataSets().size());
@@ -1063,4 +1063,5 @@ public class GeneralInformationServiceTest extends SystemTestCase
         assertTrue(metaprojectAssignments.getExperiments().get(0).isStub());
         assertTrue(metaprojectAssignments.getExperiments().get(0).toString().contains("STUB"));
     }
+
 }
diff --git a/openbis/sourceTest/sql/postgresql/124/043=metaprojects.tsv b/openbis/sourceTest/sql/postgresql/124/043=metaprojects.tsv
index 44b2274d4c5..23128b4687a 100644
--- a/openbis/sourceTest/sql/postgresql/124/043=metaprojects.tsv
+++ b/openbis/sourceTest/sql/postgresql/124/043=metaprojects.tsv
@@ -1,3 +1,4 @@
 1	TEST_METAPROJECTS	Example metaproject no. 1	2	t	2012-09-05 10:18:10.581+02
 2	TEST_METAPROJECTS	Example metaproject no. 2	6	t	2012-09-05 10:18:10.581+02
 3	ANOTHER_TEST_METAPROJECTS	Another example metaproject	2	t	2012-09-05 10:18:10.581+02
+4	TEST_METAPROJECTS_2	Example metaproject no. 2	6	t	2012-09-05 10:18:10.581+02
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/IGeneralInformationChangingService.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/IGeneralInformationChangingService.java
index a7c03e8e251..db64b7dd92d 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/IGeneralInformationChangingService.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/IGeneralInformationChangingService.java
@@ -19,6 +19,7 @@ package ch.systemsx.cisd.openbis.generic.shared.api.v1;
 import java.util.Map;
 
 import ch.systemsx.cisd.common.api.IRpcService;
+import ch.systemsx.cisd.common.exception.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.MetaprojectAssignmentsIds;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.NewVocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.WebAppSettings;
@@ -87,25 +88,30 @@ public interface IGeneralInformationChangingService extends IRpcService
      * Creates a new metaproject.
      * 
      * @param name Name of the metaproject
-     * @param description Description of the metaproject
+     * @param descriptionOrNull Description of the metaproject
      * @return Newly created metaproject
      * @since 1.3
      */
-    public Metaproject createMetaproject(String sessionToken, String name, String description);
+    public Metaproject createMetaproject(String sessionToken, String name, String descriptionOrNull);
 
     /**
-     * Updates existing metaproject.
+     * Updates an existing metaproject.
      * 
-     * @param metaproject Metaproject that should be updated
+     * @param metaprojectId Id of the metaproject
+     * @param name New name of the metaproject
+     * @param descriptionOrNull New description of the metaproject
      * @return Updated metaproject
+     * @throws UserFailureException when a metaproject with the specified id doesn't exist.
      * @since 1.3
      */
-    public Metaproject updateMetaproject(String sessionToken, Metaproject metaproject);
+    public Metaproject updateMetaproject(String sessionToken, IMetaprojectId metaprojectId,
+            String name, String descriptionOrNull);
 
     /**
      * Deletes existing metaproject.
      * 
      * @param metaprojectId Id of a metaproject to delete
+     * @throws UserFailureException when a metaproject with the specified id doesn't exist.
      * @since 1.3
      */
     public void deleteMetaproject(String sessionToken, IMetaprojectId metaprojectId);
@@ -115,6 +121,7 @@ public interface IGeneralInformationChangingService extends IRpcService
      * 
      * @param metaprojectId Id of a metaproject
      * @param assignmentsToAdd Assignments that should be added to the metaproject
+     * @throws UserFailureException when a metaproject with the specified id doesn't exist.
      * @since 1.3
      */
     public void addToMetaproject(String sessionToken, IMetaprojectId metaprojectId,
@@ -125,6 +132,7 @@ public interface IGeneralInformationChangingService extends IRpcService
      * 
      * @param metaprojectId Id of a metaproject
      * @param assignmentsToRemove Assignments that should be removed from the metaproject
+     * @throws UserFailureException when a metaproject with the specified id doesn't exist.
      * @since 1.3
      */
     public void removeFromMetaproject(String sessionToken, IMetaprojectId metaprojectId,
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/IGeneralInformationService.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/IGeneralInformationService.java
index d6db264582f..a01cbce0244 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/IGeneralInformationService.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/IGeneralInformationService.java
@@ -23,6 +23,7 @@ import java.util.Map;
 import java.util.Set;
 
 import ch.systemsx.cisd.common.api.IRpcService;
+import ch.systemsx.cisd.common.exception.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.ControlledVocabularyPropertyType;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet.Connections;
@@ -472,6 +473,8 @@ public interface IGeneralInformationService extends IRpcService
 
     /**
      * Returns all entities tagged with given metaproject.
+     * 
+     * @throws UserFailureException when a metaproject with the specified id doesn't exist.
      */
     public MetaprojectAssignments getMetaproject(String sessionToken, IMetaprojectId metaprojectId);
 }
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/IObjectId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/IObjectId.java
index a4807b15f6e..021013baa87 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/IObjectId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/IObjectId.java
@@ -21,6 +21,8 @@ import java.io.Serializable;
 import ch.systemsx.cisd.base.annotation.JsonObject;
 
 /**
+ * Holds information that uniquely identifies an object in openBIS.
+ * 
  * @author pkupczyk
  */
 @JsonObject("IObjectId")
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/ObjectIdentifierId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/ObjectIdentifierId.java
index 98c4080c2c5..6001a9eeab1 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/ObjectIdentifierId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/ObjectIdentifierId.java
@@ -20,6 +20,8 @@ import ch.systemsx.cisd.base.annotation.JsonObject;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
 
 /**
+ * Base class for ids that identify objects by identifier.
+ * 
  * @author pkupczyk
  */
 @JsonObject("ObjectIdentifierId")
@@ -57,4 +59,10 @@ public abstract class ObjectIdentifierId implements IObjectId
         this.identifier = identifier;
     }
 
+    @Override
+    public String toString()
+    {
+        return getIdentifier();
+    }
+
 }
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/ObjectPermIdId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/ObjectPermIdId.java
index 6d67a550798..4d81764575b 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/ObjectPermIdId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/ObjectPermIdId.java
@@ -20,6 +20,8 @@ import ch.systemsx.cisd.base.annotation.JsonObject;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
 
 /**
+ * Base class for ids that identify objects by perm id.
+ * 
  * @author pkupczyk
  */
 @JsonObject("ObjectPermIdId")
@@ -57,4 +59,10 @@ public abstract class ObjectPermIdId implements IObjectId
         this.permId = permId;
     }
 
+    @Override
+    public String toString()
+    {
+        return getPermId();
+    }
+
 }
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/ObjectTechIdId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/ObjectTechIdId.java
index b0c296f0705..d18ff2ed421 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/ObjectTechIdId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/ObjectTechIdId.java
@@ -20,6 +20,8 @@ import ch.systemsx.cisd.base.annotation.JsonObject;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
 
 /**
+ * Base class for ids that identify objects by tech id.
+ * 
  * @author pkupczyk
  */
 @JsonObject("ObjectTechIdId")
@@ -57,4 +59,10 @@ public abstract class ObjectTechIdId implements IObjectId
         this.techId = techId;
     }
 
+    @Override
+    public String toString()
+    {
+        return getTechId().toString();
+    }
+
 }
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/dataset/DataSetCodeId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/dataset/DataSetCodeId.java
index 9909ba8d5d9..c5a1ff6170f 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/dataset/DataSetCodeId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/dataset/DataSetCodeId.java
@@ -20,6 +20,8 @@ import ch.systemsx.cisd.base.annotation.JsonObject;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
 
 /**
+ * Identifies a data set by code.
+ * 
  * @author pkupczyk
  */
 @JsonObject("DataSetCodeId")
@@ -30,6 +32,9 @@ public class DataSetCodeId implements IDataSetId
 
     private String code;
 
+    /**
+     * @param code Data set code, e.g "201108050937246-1031".
+     */
     public DataSetCodeId(String code)
     {
         setCode(code);
@@ -58,4 +63,10 @@ public class DataSetCodeId implements IDataSetId
         this.code = code;
     }
 
+    @Override
+    public String toString()
+    {
+        return getCode();
+    }
+
 }
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/dataset/DataSetTechIdId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/dataset/DataSetTechIdId.java
index 79aebad4d7d..a164449ab10 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/dataset/DataSetTechIdId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/dataset/DataSetTechIdId.java
@@ -21,6 +21,8 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.ObjectTechIdId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
 
 /**
+ * Identifies a data set by tech id.
+ * 
  * @author pkupczyk
  */
 @JsonObject("DataSetTechIdId")
@@ -29,6 +31,9 @@ public class DataSetTechIdId extends ObjectTechIdId implements IDataSetId
 
     private static final long serialVersionUID = ServiceVersionHolder.VERSION;
 
+    /**
+     * @param techId Data set tech id.
+     */
     public DataSetTechIdId(Long techId)
     {
         super(techId);
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/dataset/IDataSetId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/dataset/IDataSetId.java
index 9c461a6c384..4aba633f1cb 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/dataset/IDataSetId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/dataset/IDataSetId.java
@@ -20,6 +20,8 @@ import ch.systemsx.cisd.base.annotation.JsonObject;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.IObjectId;
 
 /**
+ * Holds information that uniquely identifies a data set in openBIS.
+ * 
  * @author pkupczyk
  */
 @JsonObject("IDataSetId")
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/experiment/ExperimentIdentifierId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/experiment/ExperimentIdentifierId.java
index 479ef277b43..50a3f7102bf 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/experiment/ExperimentIdentifierId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/experiment/ExperimentIdentifierId.java
@@ -21,6 +21,8 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.ObjectIdentifierId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
 
 /**
+ * Identifies an experiment by identifier.
+ * 
  * @author pkupczyk
  */
 @JsonObject("ExperimentIdentifierId")
@@ -29,6 +31,9 @@ public class ExperimentIdentifierId extends ObjectIdentifierId implements IExper
 
     private static final long serialVersionUID = ServiceVersionHolder.VERSION;
 
+    /**
+     * @param identifier Experiment identifier, e.g. "/MY_SPACE/MY_PROJECT/MY_EXPERIMENT".
+     */
     public ExperimentIdentifierId(String identifier)
     {
         super(identifier);
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/experiment/ExperimentPermIdId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/experiment/ExperimentPermIdId.java
index da5ab99dc14..8ccf324dbed 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/experiment/ExperimentPermIdId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/experiment/ExperimentPermIdId.java
@@ -21,6 +21,8 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.ObjectPermIdId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
 
 /**
+ * Identifies an experiment by perm id.
+ * 
  * @author pkupczyk
  */
 @JsonObject("ExperimentPermIdId")
@@ -29,6 +31,9 @@ public class ExperimentPermIdId extends ObjectPermIdId implements IExperimentId
 
     private static final long serialVersionUID = ServiceVersionHolder.VERSION;
 
+    /**
+     * @param permId Experiment perm id, e.g. "201108050937246-1031".
+     */
     public ExperimentPermIdId(String permId)
     {
         super(permId);
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/experiment/ExperimentTechIdId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/experiment/ExperimentTechIdId.java
index 549ad3fd5d9..4b8f04dcc36 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/experiment/ExperimentTechIdId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/experiment/ExperimentTechIdId.java
@@ -21,6 +21,8 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.ObjectTechIdId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
 
 /**
+ * Identifies an experiment by tech id.
+ * 
  * @author pkupczyk
  */
 @JsonObject("ExperimentTechIdId")
@@ -29,6 +31,9 @@ public class ExperimentTechIdId extends ObjectTechIdId implements IExperimentId
 
     private static final long serialVersionUID = ServiceVersionHolder.VERSION;
 
+    /**
+     * @param techId Experiment tech id.
+     */
     public ExperimentTechIdId(Long techId)
     {
         super(techId);
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/experiment/IExperimentId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/experiment/IExperimentId.java
index f8986bf6313..828d6ccc627 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/experiment/IExperimentId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/experiment/IExperimentId.java
@@ -20,6 +20,8 @@ import ch.systemsx.cisd.base.annotation.JsonObject;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.IObjectId;
 
 /**
+ * Holds information that uniquely identifies an experiment in openBIS.
+ * 
  * @author pkupczyk
  */
 @JsonObject("IExperimentId")
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/material/IMaterialId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/material/IMaterialId.java
index 981a6bb880d..373f05c53ea 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/material/IMaterialId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/material/IMaterialId.java
@@ -20,6 +20,8 @@ import ch.systemsx.cisd.base.annotation.JsonObject;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.IObjectId;
 
 /**
+ * Holds information that uniquely identifies a material in openBIS.
+ * 
  * @author pkupczyk
  */
 @JsonObject("IMaterialId")
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/material/MaterialCodeAndTypeCodeId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/material/MaterialCodeAndTypeCodeId.java
index fafc1dc6616..995d31210a5 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/material/MaterialCodeAndTypeCodeId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/material/MaterialCodeAndTypeCodeId.java
@@ -20,6 +20,8 @@ import ch.systemsx.cisd.base.annotation.JsonObject;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
 
 /**
+ * Identifies a material by code and material type code.
+ * 
  * @author pkupczyk
  */
 @JsonObject("MaterialCodeAndTypeCodeId")
@@ -32,6 +34,10 @@ public class MaterialCodeAndTypeCodeId implements IMaterialId
 
     private String typeCode;
 
+    /**
+     * @param code Material code
+     * @param typeCode Material type code
+     */
     public MaterialCodeAndTypeCodeId(String code, String typeCode)
     {
         setCode(code);
@@ -75,4 +81,10 @@ public class MaterialCodeAndTypeCodeId implements IMaterialId
         this.typeCode = typeCode;
     }
 
+    @Override
+    public String toString()
+    {
+        return getCode() + " (" + getTypeCode() + ")";
+    }
+
 }
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/material/MaterialTechIdId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/material/MaterialTechIdId.java
index 4784d700add..a395edf68df 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/material/MaterialTechIdId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/material/MaterialTechIdId.java
@@ -21,6 +21,8 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.ObjectTechIdId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
 
 /**
+ * Identifies a material by tech id.
+ * 
  * @author pkupczyk
  */
 @JsonObject("MaterialTechIdId")
@@ -29,6 +31,9 @@ public class MaterialTechIdId extends ObjectTechIdId implements IMaterialId
 
     private static final long serialVersionUID = ServiceVersionHolder.VERSION;
 
+    /**
+     * @param techId Material tech id.
+     */
     public MaterialTechIdId(Long techId)
     {
         super(techId);
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/metaproject/IMetaprojectId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/metaproject/IMetaprojectId.java
index f0396b6d85e..6ccbcd3b2e1 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/metaproject/IMetaprojectId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/metaproject/IMetaprojectId.java
@@ -20,6 +20,8 @@ import ch.systemsx.cisd.base.annotation.JsonObject;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.IObjectId;
 
 /**
+ * Holds information that uniquely identifies a metaproject in openBIS.
+ * 
  * @author pkupczyk
  */
 @JsonObject("IMetaprojectId")
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/metaproject/MetaprojectIdentifierId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/metaproject/MetaprojectIdentifierId.java
index 1daab2ec1b0..f73c9393d90 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/metaproject/MetaprojectIdentifierId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/metaproject/MetaprojectIdentifierId.java
@@ -21,6 +21,8 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.ObjectIdentifierId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
 
 /**
+ * Identifies a metaproject by identifier.
+ * 
  * @author pkupczyk
  */
 @JsonObject("MetaprojectIdentifierId")
@@ -29,6 +31,9 @@ public class MetaprojectIdentifierId extends ObjectIdentifierId implements IMeta
 
     private static final long serialVersionUID = ServiceVersionHolder.VERSION;
 
+    /**
+     * @param identifier Metaproject identifier, e.e. "/MY_USER/MY_METAPROJECT".
+     */
     public MetaprojectIdentifierId(String identifier)
     {
         super(identifier);
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/metaproject/MetaprojectTechIdId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/metaproject/MetaprojectTechIdId.java
index 81bd609073d..d5122930b13 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/metaproject/MetaprojectTechIdId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/metaproject/MetaprojectTechIdId.java
@@ -21,6 +21,8 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.ObjectTechIdId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
 
 /**
+ * Identifies a metaproject by tech id.
+ * 
  * @author pkupczyk
  */
 @JsonObject("MetaprojectTechIdId")
@@ -29,6 +31,9 @@ public class MetaprojectTechIdId extends ObjectTechIdId implements IMetaprojectI
 
     private static final long serialVersionUID = ServiceVersionHolder.VERSION;
 
+    /**
+     * @param techId Metaproject tech id.
+     */
     public MetaprojectTechIdId(Long techId)
     {
         super(techId);
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/sample/ISampleId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/sample/ISampleId.java
index f1baeb8f8b3..50b3662ee08 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/sample/ISampleId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/sample/ISampleId.java
@@ -20,6 +20,8 @@ import ch.systemsx.cisd.base.annotation.JsonObject;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.IObjectId;
 
 /**
+ * Holds information that uniquely identifies a sample in openBIS.
+ * 
  * @author pkupczyk
  */
 @JsonObject("ISampleId")
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/sample/SampleIdentifierId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/sample/SampleIdentifierId.java
index 11195a88d2b..997d8914a97 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/sample/SampleIdentifierId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/sample/SampleIdentifierId.java
@@ -21,6 +21,8 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.ObjectIdentifierId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
 
 /**
+ * Identifies a sample by identifier.
+ * 
  * @author pkupczyk
  */
 @JsonObject("SampleIdentifierId")
@@ -29,6 +31,10 @@ public class SampleIdentifierId extends ObjectIdentifierId implements ISampleId
 
     private static final long serialVersionUID = ServiceVersionHolder.VERSION;
 
+    /**
+     * @param identifier Sample identifier, e.g. "/MY_SPACE/MY_SAMPLE" (space sample) or
+     *            "/MY_SAMPLE" (shared sample)
+     */
     public SampleIdentifierId(String identifier)
     {
         super(identifier);
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/sample/SamplePermIdId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/sample/SamplePermIdId.java
index c1f1dedd378..b2e2205ba30 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/sample/SamplePermIdId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/sample/SamplePermIdId.java
@@ -21,6 +21,8 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.ObjectPermIdId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
 
 /**
+ * Identifies a sample by perm id.
+ * 
  * @author pkupczyk
  */
 @JsonObject("SamplePermIdId")
@@ -29,6 +31,9 @@ public class SamplePermIdId extends ObjectPermIdId implements ISampleId
 
     private static final long serialVersionUID = ServiceVersionHolder.VERSION;
 
+    /**
+     * @param permId Sample perm id, e.g. "201108050937246-1031".
+     */
     public SamplePermIdId(String permId)
     {
         super(permId);
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/sample/SampleTechIdId.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/sample/SampleTechIdId.java
index 61b3dee41f0..e83b3cee37a 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/sample/SampleTechIdId.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/id/sample/SampleTechIdId.java
@@ -21,6 +21,8 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.ObjectTechIdId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
 
 /**
+ * Identifies a sample by tech id.
+ * 
  * @author pkupczyk
  */
 @JsonObject("SampleTechIdId")
@@ -29,6 +31,9 @@ public class SampleTechIdId extends ObjectTechIdId implements ISampleId
 
     private static final long serialVersionUID = ServiceVersionHolder.VERSION;
 
+    /**
+     * @param techId Sample tech id.
+     */
     public SampleTechIdId(Long techId)
     {
         super(techId);
diff --git a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/MetaprojectIdentifier.java b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/MetaprojectIdentifier.java
index 63099fe4478..ae156e202a2 100644
--- a/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/MetaprojectIdentifier.java
+++ b/openbis_api/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/MetaprojectIdentifier.java
@@ -53,7 +53,7 @@ public class MetaprojectIdentifier implements Serializable
 
     public String format()
     {
-        return metaprojectOwnerId + SEPARATOR + metaprojectName;
+        return SEPARATOR + metaprojectOwnerId + SEPARATOR + metaprojectName;
     }
 
     public static MetaprojectIdentifier parse(String str)
@@ -65,13 +65,13 @@ public class MetaprojectIdentifier implements Serializable
 
         String[] splitted = str.split(SEPARATOR);
 
-        if (splitted.length == 2)
+        if (splitted.length == 3)
         {
-            return new MetaprojectIdentifier(splitted[0], splitted[1]);
+            return new MetaprojectIdentifier(splitted[1], splitted[2]);
         } else
         {
-            throw new IllegalArgumentException("Metaproject identifier must have USER_ID"
-                    + SEPARATOR + "METAPROJECT_NAME format");
+            throw new IllegalArgumentException("Metaproject identifier must have " + SEPARATOR
+                    + "USER_ID" + SEPARATOR + "METAPROJECT_NAME format");
         }
     }
 
-- 
GitLab