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 20db614b3cd84f8ef0051d06429793b8aa27ecf7..1f3be2df8d235b043edef01d1f35ed824366fd5e 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
@@ -1576,7 +1576,7 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
                     columns.add(NewSample.IDENTIFIER_COLUMN);
                 }
                 columns.add(NewSample.CONTAINER);
-                columns.add(NewSample.PARENT);
+                columns.add(NewSample.PARENTS);
                 if (withExperiments)
                     columns.add(NewSample.EXPERIMENT);
                 addProperties(columns, ((SampleTypePE) entityType).getSampleTypePropertyTypes());
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractSampleBusinessObject.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractSampleBusinessObject.java
index 0fa57e1cb497d584126a93a1927032e53c872e6c..bd31cad1aa7c6e9c70ef8b759b40ad8832ddba20 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractSampleBusinessObject.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractSampleBusinessObject.java
@@ -206,7 +206,7 @@ abstract class AbstractSampleBusinessObject extends AbstractSampleIdentifierBusi
             final SamplePE samplePE, String parentIdentifier)
     {
         final Set<SamplePE> newParents = new HashSet<SamplePE>();
-        final SamplePE parentOrNull = tryGetValidSample(parentIdentifier, sampleIdentifier);
+        final SamplePE parentOrNull = tryGetValidParentSample(parentIdentifier, sampleIdentifier);
         if (parentOrNull != null)
         {
             newParents.add(parentOrNull);
@@ -230,6 +230,7 @@ abstract class AbstractSampleBusinessObject extends AbstractSampleIdentifierBusi
         final Set<SamplePE> parentPEs = new HashSet<SamplePE>();
         for (SampleIdentifier si : parentIdentifiers)
         {
+            // TODO 2010-11-10, Piotr Buczek: use cache
             SamplePE parent = getSampleByIdentifier(si);
             parentPEs.add(parent);
         }
@@ -238,6 +239,10 @@ abstract class AbstractSampleBusinessObject extends AbstractSampleIdentifierBusi
 
     private void replaceParents(SamplePE child, Set<SamplePE> newParents)
     {
+        for (SamplePE parent : newParents)
+        {
+            checkParentInvalidation(parent, child.getSampleIdentifier());
+        }
         List<SampleRelationshipPE> oldParents = new ArrayList<SampleRelationshipPE>();
         for (SampleRelationshipPE r : child.getParentRelationships())
         {
@@ -280,8 +285,8 @@ abstract class AbstractSampleBusinessObject extends AbstractSampleIdentifierBusi
         return result;
     }
 
-    private SamplePE tryGetValidSample(final String parentIdentifierOrNull,
-            final SampleIdentifier sampleIdentifier)
+    private SamplePE tryGetValidParentSample(final String parentIdentifierOrNull,
+            final SampleIdentifier childIdentifier)
     {
         if (parentIdentifierOrNull == null)
         {
@@ -289,19 +294,24 @@ abstract class AbstractSampleBusinessObject extends AbstractSampleIdentifierBusi
         }
         final SamplePE parentPE =
                 getSampleByIdentifier(SampleIdentifierFactory.parse(parentIdentifierOrNull));
+        checkParentInvalidation(parentPE, childIdentifier);
+        return parentPE;
+    }
+
+    private void checkParentInvalidation(final SamplePE parentPE, final SampleIdentifier child)
+    {
         if (parentPE.getInvalidation() != null)
         {
             throw UserFailureException.fromTemplate(
-                    "Cannot register sample '%s': parent '%s' has been invalidated.",
-                    sampleIdentifier, parentIdentifierOrNull);
+                    "Sample '%s' has been invalidated and can't become a parent of sample '%s'.",
+                    parentPE.getIdentifier(), child);
         }
-        return parentPE;
     }
 
     private SamplePE tryGetValidNotContainedSample(final String parentIdentifierOrNull,
             final SampleIdentifier sampleIdentifier)
     {
-        SamplePE sample = tryGetValidSample(parentIdentifierOrNull, sampleIdentifier);
+        SamplePE sample = tryGetValidParentSample(parentIdentifierOrNull, sampleIdentifier);
         if (sample != null && sample.getContainer() != null)
         {
             throw UserFailureException.fromTemplate(
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractSampleIdentifierBusinessObject.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractSampleIdentifierBusinessObject.java
index 767854451664157a48de29c90bc4183e8e07c281..b504e0c0ffba7fbc15a414dca287bf4b24008f2f 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractSampleIdentifierBusinessObject.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractSampleIdentifierBusinessObject.java
@@ -70,6 +70,7 @@ abstract class AbstractSampleIdentifierBusinessObject extends AbstractBusinessOb
 
     protected SamplePE tryToGetSampleByIdentifier(final SampleIdentifier sampleIdentifier)
     {
+        // TODO 2010-11-10, Piotr Buczek: use cache
         assert sampleIdentifier != null : "Sample identifier unspecified.";
         final SampleOwner sampleOwner = sampleOwnerFinder.figureSampleOwner(sampleIdentifier);
         final String sampleCode = sampleIdentifier.getSampleCode();
@@ -78,8 +79,8 @@ abstract class AbstractSampleIdentifierBusinessObject extends AbstractBusinessOb
         if (sampleOwner.isDatabaseInstanceLevel())
         {
             sample =
-                    sampleDAO.tryFindByCodeAndDatabaseInstance(sampleCode, sampleOwner
-                            .tryGetDatabaseInstance());
+                    sampleDAO.tryFindByCodeAndDatabaseInstance(sampleCode,
+                            sampleOwner.tryGetDatabaseInstance());
         } else
         {
             assert sampleOwner.isGroupLevel() : "Must be of space level.";
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleTable.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleTable.java
index 7b3b58b875ba85f46cf8187eaf8e5abc42df2c48..50aacbf294a13c578df3150146dd32fb2068be13 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleTable.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleTable.java
@@ -251,6 +251,14 @@ public final class SampleTable extends AbstractSampleBusinessObject implements I
             setGeneratedFrom(updates.getSampleIdentifier(), sample,
                     updates.getParentIdentifierOrNull());
         }
+        if (details.isParentsUpdateRequested())
+        {
+            final String[] parents = updates.getModifiedParentCodesOrNull();
+            if (parents != null)
+            {
+                setParents(sample, parents);
+            }
+        }
         if (details.isContainerUpdateRequested())
         {
             setContainer(updates.getSampleIdentifier(), sample,
@@ -258,7 +266,8 @@ public final class SampleTable extends AbstractSampleBusinessObject implements I
         }
         // NOTE: Checking business rules with relationships is expensive.
         // Don't perform them unless relevant data were changed.
-        if (details.isExperimentUpdateRequested() || details.isParentUpdateRequested())
+        if (details.isExperimentUpdateRequested() || details.isParentUpdateRequested()
+                || details.isParentsUpdateRequested())
         {
             checkParentBusinessRules(sample);
         }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/NewSample.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/NewSample.java
index 4fd95b58100507e36fdfa890e2d8b26ceb6fd621..39dad9bdc5ff89cb4722f157ba4621eff7c8d988 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/NewSample.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/NewSample.java
@@ -39,6 +39,8 @@ public class NewSample extends Identifier<NewSample> implements Comparable<NewSa
 
     public static final String PARENT = "parent";
 
+    public static final String PARENTS = "parents";
+
     public static final String EXPERIMENT = "experiment";
 
     private SampleType sampleType;
@@ -97,11 +99,12 @@ public class NewSample extends Identifier<NewSample> implements Comparable<NewSa
     }
 
     public NewSample(final String identifier, SampleType sampleType, String containerIdentifier,
-            String parentIdentifier, String experimentIdentifier, IEntityProperty[] properties,
-            List<NewAttachment> attachments)
+            String parentIdentifier, String[] parentsOrNull, String experimentIdentifier,
+            IEntityProperty[] properties, List<NewAttachment> attachments)
     {
         this(identifier, sampleType, containerIdentifier);
         this.parentIdentifier = parentIdentifier;
+        this.parentsOrNull = parentsOrNull;
         this.experimentIdentifier = experimentIdentifier;
         this.properties = properties;
         this.attachments = attachments;
@@ -137,6 +140,19 @@ public class NewSample extends Identifier<NewSample> implements Comparable<NewSa
         this.parentsOrNull = parents;
     }
 
+    @BeanProperty(label = PARENTS, optional = true)
+    public void setParents(String parents)
+    {
+        if (parents != null)
+        {
+            String[] split = parents.split(",");
+            setParents(split);
+        } else
+        {
+            setParents((String[]) null);
+        }
+    }
+
     public final String getParentIdentifier()
     {
         return parentIdentifier;
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/RelationshipType.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/RelationshipType.java
new file mode 100644
index 0000000000000000000000000000000000000000..478f951659a9d3840b09d37360c043404d2baa5d
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/RelationshipType.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.generic.shared.basic.dto;
+
+import java.io.Serializable;
+
+import com.google.gwt.user.client.rpc.IsSerializable;
+
+/**
+ * The <i>GWT</i> version of RelationshipTypePE.
+ * 
+ * @author Piotr Buczek
+ */
+public class RelationshipType extends Code<PropertyType> implements IsSerializable, Serializable
+{
+    private static final long serialVersionUID = ServiceVersionHolder.VERSION;
+
+    private Long id;
+
+    /**
+     * Only used for displaying/viewing. With <code>managedInternally</code> is unambiguous (meaning
+     * that <code>simpleCode</code> alone could be not unique).
+     * <p>
+     * We have to use it, partly because <i>Javascript</i> handle '.' in an object-oriented way.
+     * </p>
+     */
+    private String simpleCode;
+
+    private boolean internalNamespace;
+
+    private boolean managedInternally;
+
+    private String description;
+
+    private String label;
+
+    private DatabaseInstance databaseInstance;
+
+    public Long getId()
+    {
+        return id;
+    }
+
+    public void setId(Long id)
+    {
+        this.id = id;
+    }
+
+    public String getSimpleCode()
+    {
+        return simpleCode;
+    }
+
+    public void setSimpleCode(String simpleCode)
+    {
+        this.simpleCode = simpleCode;
+    }
+
+    public boolean isInternalNamespace()
+    {
+        return internalNamespace;
+    }
+
+    public void setInternalNamespace(boolean internalNamespace)
+    {
+        this.internalNamespace = internalNamespace;
+    }
+
+    public boolean isManagedInternally()
+    {
+        return managedInternally;
+    }
+
+    public void setManagedInternally(boolean managedInternally)
+    {
+        this.managedInternally = managedInternally;
+    }
+
+    public String getDescription()
+    {
+        return description;
+    }
+
+    public void setDescription(String description)
+    {
+        this.description = description;
+    }
+
+    public String getLabel()
+    {
+        return label;
+    }
+
+    public void setLabel(String label)
+    {
+        this.label = label;
+    }
+
+    public DatabaseInstance getDatabaseInstance()
+    {
+        return databaseInstance;
+    }
+
+    public void setDatabaseInstance(DatabaseInstance databaseInstance)
+    {
+        this.databaseInstance = databaseInstance;
+    }
+
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/SampleBatchUpdateDetails.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/SampleBatchUpdateDetails.java
index a67c64b290d373761158a9e6d748980705122c84..246ba61e934dd6d7329e6c2f620377d174a8efb9 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/SampleBatchUpdateDetails.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/SampleBatchUpdateDetails.java
@@ -34,6 +34,8 @@ public class SampleBatchUpdateDetails implements IsSerializable, Serializable
 
     private boolean parentUpdateRequested;
 
+    private boolean parentsUpdateRequested;
+
     private boolean containerUpdateRequested;
 
     private Set<String> propertiesToUpdate; // codes of properties to update
@@ -43,10 +45,11 @@ public class SampleBatchUpdateDetails implements IsSerializable, Serializable
     }
 
     public SampleBatchUpdateDetails(boolean updateExperiment, boolean updateParent,
-            boolean updateContainer, Set<String> propertiesToUpdate)
+            boolean updateParents, boolean updateContainer, Set<String> propertiesToUpdate)
     {
         this.experimentUpdateRequested = updateExperiment;
         this.parentUpdateRequested = updateParent;
+        this.parentsUpdateRequested = updateParents;
         this.containerUpdateRequested = updateContainer;
         this.propertiesToUpdate = propertiesToUpdate;
     }
@@ -61,6 +64,11 @@ public class SampleBatchUpdateDetails implements IsSerializable, Serializable
         return parentUpdateRequested;
     }
 
+    public boolean isParentsUpdateRequested()
+    {
+        return parentsUpdateRequested;
+    }
+
     public boolean isContainerUpdateRequested()
     {
         return containerUpdateRequested;
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/UpdatedSample.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/UpdatedSample.java
index c028671370816ef1b291ebb5b65e6ba67fbd5c80..6a531d9baa29f0c93429636b79328348577e3cbf 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/UpdatedSample.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/UpdatedSample.java
@@ -40,8 +40,9 @@ public final class UpdatedSample extends NewSample
     public UpdatedSample(NewSample newSample, SampleBatchUpdateDetails batchUpdateDetails)
     {
         super(newSample.getIdentifier(), newSample.getSampleType(), newSample
-                .getContainerIdentifier(), newSample.getParentIdentifier(), newSample
-                .getExperimentIdentifier(), newSample.getProperties(), newSample.getAttachments());
+                .getContainerIdentifier(), newSample.getParentIdentifier(), newSample.getParents(),
+                newSample.getExperimentIdentifier(), newSample.getProperties(), newSample
+                        .getAttachments());
         this.batchUpdateDetails = batchUpdateDetails;
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/server/parser/UpdatedSampleParserObjectFactory.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/server/parser/UpdatedSampleParserObjectFactory.java
index 17558aa8b83083ed2a16380e086301cd16dd2057..51a94ce4e0940617bb3b3a4cf0209dbe51285de1 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/server/parser/UpdatedSampleParserObjectFactory.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/server/parser/UpdatedSampleParserObjectFactory.java
@@ -54,9 +54,10 @@ final class UpdatedSampleParserObjectFactory extends NewSampleParserObjectFactor
     {
         boolean updateExperiment = isColumnAvailable(UpdatedSample.EXPERIMENT);
         boolean updateParent = isColumnAvailable(UpdatedSample.PARENT);
+        boolean updateParents = isColumnAvailable(UpdatedSample.PARENTS);
         boolean updateContainer = isColumnAvailable(UpdatedSample.CONTAINER);
-        return new SampleBatchUpdateDetails(updateExperiment, updateParent, updateContainer,
-                getUnmatchedProperties());
+        return new SampleBatchUpdateDetails(updateExperiment, updateParent, updateParents,
+                updateContainer, getUnmatchedProperties());
     }
 
     //
@@ -88,6 +89,9 @@ final class UpdatedSampleParserObjectFactory extends NewSampleParserObjectFactor
         final boolean updateParent =
                 basicBatchUpdateDetails.isParentUpdateRequested()
                         && isNotEmpty(newSample.getParentIdentifier());
+        final boolean updateParents =
+                basicBatchUpdateDetails.isParentsUpdateRequested()
+                        && isNotEmpty(newSample.getParents());
         final boolean updateContainer =
                 basicBatchUpdateDetails.isContainerUpdateRequested()
                         && isNotEmpty(newSample.getContainerIdentifier());
@@ -98,8 +102,13 @@ final class UpdatedSampleParserObjectFactory extends NewSampleParserObjectFactor
             propertiesToUpdate.add(property.getPropertyType().getCode());
         }
 
-        return new SampleBatchUpdateDetails(updateExperiment, updateParent, updateContainer,
-                propertiesToUpdate);
+        return new SampleBatchUpdateDetails(updateExperiment, updateParent, updateParents,
+                updateContainer, propertiesToUpdate);
+    }
+
+    private boolean isNotEmpty(String[] parents)
+    {
+        return parents != null && parents.length > 0 && isNotEmpty(parents[0]);
     }
 
     /** Cleans properties and connections of the specified sample that are marked for deletion. */
@@ -113,6 +122,10 @@ final class UpdatedSampleParserObjectFactory extends NewSampleParserObjectFactor
         {
             newSample.setParentIdentifier(null);
         }
+        if (newSample.getParents() != null && isDeletionMark(newSample.getParents()[0]))
+        {
+            newSample.setParents(new String[0]);
+        }
         if (isDeletionMark(newSample.getContainerIdentifier()))
         {
             newSample.setContainerIdentifier(null);
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServer.java
index e7c8a5ba4e62936dfbbec6a9de0c8f9ad3829805..3e71b8aaba57fa3a2f48b874d59e5a8a668db29a 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServer.java
@@ -481,7 +481,7 @@ public final class GenericServer extends AbstractServer<IGenericServer> implemen
             }
             final String parentIdentifierOrNull = updatedSample.getParentIdentifier();
             final String[] parentsOrNull =
-                    (parentIdentifierOrNull == null) ? new String[0] : new String[]
+                    (parentIdentifierOrNull == null) ? updatedSample.getParents() : new String[]
                         { parentIdentifierOrNull };
             final String containerIdentifierOrNull = updatedSample.getContainerIdentifier();
             final SampleBatchUpdateDetails batchUpdateDetails =
@@ -508,7 +508,7 @@ public final class GenericServer extends AbstractServer<IGenericServer> implemen
                 propertyCodes.add(p.getPropertyType().getCode());
             }
             SampleBatchUpdateDetails result =
-                    new SampleBatchUpdateDetails(false, false, false, propertyCodes);
+                    new SampleBatchUpdateDetails(false, false, false, false, propertyCodes);
             return result;
         }
     }