From 3b6207bb5da0c0c0d6179efc5751ee2d66c99f22 Mon Sep 17 00:00:00 2001
From: izabel <izabel>
Date: Tue, 19 Oct 2010 09:13:15 +0000
Subject: [PATCH] [LMS-1818] don't allow to update dynamic properties;
 automatically add placeholder property values to dynamic properties

SVN: 18342
---
 .../AbstractExternalDataBusinessObject.java   |  25 ++-
 .../bo/AbstractMaterialBusinessObject.java    |  18 +++
 .../bo/AbstractSampleBusinessObject.java      |  50 +++---
 .../bo/EntityPropertiesConverter.java         | 150 +++++++++++++++---
 .../server/business/bo/ExperimentBO.java      |  16 +-
 .../server/business/bo/ExternalDataBO.java    |  17 +-
 .../bo/IEntityPropertiesConverter.java        |  16 +-
 .../server/business/bo/MaterialBO.java        |  12 +-
 .../server/business/bo/MaterialTable.java     |  11 +-
 .../generic/server/business/bo/SampleBO.java  |  30 ++--
 .../server/business/bo/SampleTable.java       |   5 +-
 .../experiment/PropertiesEditor.java          |  12 +-
 .../server/business/bo/ExperimentBOTest.java  |  31 ++--
 .../business/bo/ExternalDataBOTest.java       |  57 ++++---
 .../server/business/bo/SampleBOTest.java      |  29 +++-
 15 files changed, 331 insertions(+), 148 deletions(-)

diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractExternalDataBusinessObject.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractExternalDataBusinessObject.java
index df2bb470f93..da07e572cdd 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractExternalDataBusinessObject.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractExternalDataBusinessObject.java
@@ -16,13 +16,15 @@
 
 package ch.systemsx.cisd.openbis.generic.server.business.bo;
 
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetPropertyPE;
-import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetTypePE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetTypePropertyTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
@@ -65,19 +67,32 @@ public abstract class AbstractExternalDataBusinessObject extends
             List<IEntityProperty> newProperties, Set<String> set)
     {
         final Set<DataSetPropertyPE> existingProperties = externalData.getProperties();
-        final EntityTypePE type = externalData.getDataSetType();
+        final DataSetTypePE type = externalData.getDataSetType();
         final PersonPE registrator = findRegistrator();
         externalData.setProperties(entityPropertiesConverter.updateProperties(existingProperties,
-                type, newProperties, registrator, set));
+                type, newProperties, registrator, set, extractDynamicProperties(type)));
+    }
+
+    protected Set<String> extractDynamicProperties(final DataSetTypePE type)
+    {
+        Set<String> dynamicProperties = new HashSet<String>();
+        for (DataSetTypePropertyTypePE etpt : type.getDataSetTypePropertyTypes())
+        {
+            if (etpt.isDynamic())
+            {
+                dynamicProperties.add(etpt.getPropertyType().getCode());
+            }
+        }
+        return dynamicProperties;
     }
 
     protected void updateProperties(ExternalDataPE externalData, List<IEntityProperty> newProperties)
     {
         final Set<DataSetPropertyPE> existingProperties = externalData.getProperties();
-        final EntityTypePE type = externalData.getDataSetType();
+        final DataSetTypePE type = externalData.getDataSetType();
         final PersonPE registrator = findRegistrator();
         externalData.setProperties(entityPropertiesConverter.updateProperties(existingProperties,
-                type, newProperties, registrator));
+                type, newProperties, registrator, extractDynamicProperties(type)));
     }
 
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractMaterialBusinessObject.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractMaterialBusinessObject.java
index 6ee3368197d..6f7ed0cc4bb 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractMaterialBusinessObject.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractMaterialBusinessObject.java
@@ -16,10 +16,15 @@
 
 package ch.systemsx.cisd.openbis.generic.server.business.bo;
 
+import java.util.HashSet;
+import java.util.Set;
+
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialTypePE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialTypePropertyTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
 import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
 
@@ -60,4 +65,17 @@ public class AbstractMaterialBusinessObject extends AbstractBusinessObject
         return result;
     }
 
+    protected Set<String> extractDynamicProperties(final MaterialTypePE type)
+    {
+        Set<String> dynamicProperties = new HashSet<String>();
+        for (MaterialTypePropertyTypePE etpt : type.getMaterialTypePropertyTypes())
+        {
+            if (etpt.isDynamic())
+            {
+                dynamicProperties.add(etpt.getPropertyType().getCode());
+            }
+        }
+        return dynamicProperties;
+    }
+
 }
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 291c0caf046..f9582f2e637 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
@@ -43,6 +43,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePropertyPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SampleRelationshipPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SampleTypePE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.SampleTypePropertyTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifier;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifierFactory;
@@ -237,8 +238,8 @@ abstract class AbstractSampleBusinessObject extends AbstractSampleIdentifierBusi
         List<SampleRelationshipPE> oldParents = new ArrayList<SampleRelationshipPE>();
         for (SampleRelationshipPE r : child.getParentRelationships())
         {
-            if (r.getRelationship().getCode().equals(
-                    BasicConstant.PARENT_CHILD_INTERNAL_RELATIONSHIP))
+            if (r.getRelationship().getCode()
+                    .equals(BasicConstant.PARENT_CHILD_INTERNAL_RELATIONSHIP))
             {
                 oldParents.add(r);
             }
@@ -353,26 +354,26 @@ abstract class AbstractSampleBusinessObject extends AbstractSampleIdentifierBusi
     {
         if (cacheOrNull != null)
         {
-            entityPropertiesConverter.checkMandatoryProperties(sample.getProperties(), sample
-                    .getSampleType(), cacheOrNull);
+            entityPropertiesConverter.checkMandatoryProperties(sample.getProperties(),
+                    sample.getSampleType(), cacheOrNull);
         } else
         {
-            entityPropertiesConverter.checkMandatoryProperties(sample.getProperties(), sample
-                    .getSampleType());
+            entityPropertiesConverter.checkMandatoryProperties(sample.getProperties(),
+                    sample.getSampleType());
         }
         final boolean hasDatasets = hasDatasets(externalDataDAO, sample);
         if (hasDatasets && sample.getExperiment() == null)
         {
             throw UserFailureException.fromTemplate(
                     "Cannot detach the sample '%s' from the experiment "
-                            + "because there are already datasets attached to the sample.", sample
-                            .getIdentifier());
+                            + "because there are already datasets attached to the sample.",
+                    sample.getIdentifier());
         }
         if (hasDatasets && sample.getGroup() == null)
         {
             throw UserFailureException.fromTemplate("Cannot detach the sample '%s' from the space "
-                    + "because there are already datasets attached to the sample.", sample
-                    .getIdentifier());
+                    + "because there are already datasets attached to the sample.",
+                    sample.getIdentifier());
         }
         if (sample.getExperiment() != null
                 && (sample.getGroup() == null || sample.getExperiment().getProject().getGroup()
@@ -423,8 +424,8 @@ abstract class AbstractSampleBusinessObject extends AbstractSampleIdentifierBusi
         {
             throw UserFailureException.fromTemplate(
                     "Cannot detach the sample '%s' from the experiment "
-                            + "because there are already datasets attached to the sample.", sample
-                            .getIdentifier());
+                            + "because there are already datasets attached to the sample.",
+                    sample.getIdentifier());
         }
         sample.setExperiment(null);
     }
@@ -458,8 +459,8 @@ abstract class AbstractSampleBusinessObject extends AbstractSampleIdentifierBusi
         if (sample.getGroup() == null)
         {
             throw UserFailureException.fromTemplate(
-                    "It is not allowed to connect a shared sample '%s' to the experiment.", sample
-                            .getIdentifier());
+                    "It is not allowed to connect a shared sample '%s' to the experiment.",
+                    sample.getIdentifier());
         }
     }
 
@@ -470,8 +471,8 @@ abstract class AbstractSampleBusinessObject extends AbstractSampleIdentifierBusi
         {
             throw UserFailureException.fromTemplate(
                     "The sample '%s' cannot be assigned to the experiment '%s' "
-                            + "because the experiment has been invalidated.", sample
-                            .getSampleIdentifier(), identOrNull);
+                            + "because the experiment has been invalidated.",
+                    sample.getSampleIdentifier(), identOrNull);
         }
     }
 
@@ -523,8 +524,8 @@ abstract class AbstractSampleBusinessObject extends AbstractSampleIdentifierBusi
             {
                 throw UserFailureException.fromTemplate(
                         "Sample '%s' is an ancestor of Sample '%s' "
-                                + "and cannot be at the same time set as its child.", sample
-                                .getIdentifier(), parentToAdd.getIdentifier());
+                                + "and cannot be at the same time set as its child.",
+                        sample.getIdentifier(), parentToAdd.getIdentifier());
             } else
             {
                 final Set<TechId> nextToVisit = getSampleDAO().listParents(toVisit, relationship);
@@ -534,4 +535,17 @@ abstract class AbstractSampleBusinessObject extends AbstractSampleIdentifierBusi
             }
         }
     }
+
+    protected Set<String> extractDynamicProperties(final SampleTypePE type)
+    {
+        Set<String> dynamicProperties = new HashSet<String>();
+        for (SampleTypePropertyTypePE etpt : type.getSampleTypePropertyTypes())
+        {
+            if (etpt.isDynamic())
+            {
+                dynamicProperties.add(etpt.getPropertyType().getCode());
+            }
+        }
+        return dynamicProperties;
+    }
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/EntityPropertiesConverter.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/EntityPropertiesConverter.java
index 93ef5b5b0ab..49ca722bfc3 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/EntityPropertiesConverter.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/EntityPropertiesConverter.java
@@ -17,12 +17,16 @@
 package ch.systemsx.cisd.openbis.generic.server.business.bo;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.commons.lang.StringUtils;
+
 import ch.rinn.restrictions.Private;
 import ch.systemsx.cisd.common.collections.IKeyExtractor;
 import ch.systemsx.cisd.common.collections.TableMap;
@@ -31,9 +35,12 @@ import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.server.util.IPropertyValueValidator;
 import ch.systemsx.cisd.openbis.generic.server.util.KeyExtractorFactory;
 import ch.systemsx.cisd.openbis.generic.server.util.PropertyValidator;
+import ch.systemsx.cisd.openbis.generic.shared.basic.BasicConstant;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataTypeCode;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialIdentifier;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.PropertyType;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EntityPropertyPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePropertyTypePE;
@@ -65,9 +72,12 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert
 
     private TableMap<String, EntityTypePE> entityTypesByCode;
 
+    private Map<String, Set<String>> dynamicPropertiesByEntityTypeCode =
+            new HashMap<String, Set<String>>();
+
     private final TableMap<String, PropertyTypePE> propertyTypesByCode =
-            new TableMap<String, PropertyTypePE>(KeyExtractorFactory
-                    .getPropertyTypeByCodeKeyExtractor());
+            new TableMap<String, PropertyTypePE>(
+                    KeyExtractorFactory.getPropertyTypeByCodeKeyExtractor());
 
     private TableMap<PropertyTypePE, EntityTypePropertyTypePE> entityTypePropertyTypesByPropertyTypes;
 
@@ -91,14 +101,35 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert
         this.propertyValueValidator = propertyValueValidator;
     }
 
+    private final Set<String> getDynamicProperties(final EntityTypePE entityTypePE)
+    {
+        String code = entityTypePE.getCode();
+        if (dynamicPropertiesByEntityTypeCode.containsKey(code) == false)
+        {
+            HashSet<String> set = new HashSet<String>();
+            List<EntityTypePropertyTypePE> list =
+                    daoFactory.getEntityPropertyTypeDAO(entityKind).listEntityPropertyTypes(
+                            entityTypePE);
+            for (EntityTypePropertyTypePE etpt : list)
+            {
+                if (etpt.isDynamic())
+                {
+                    set.add(etpt.getPropertyType().getCode());
+                }
+            }
+            dynamicPropertiesByEntityTypeCode.put(code, set);
+        }
+        return dynamicPropertiesByEntityTypeCode.get(code);
+    }
+
     private final EntityTypePE getEntityType(final String entityTypeCode)
     {
         if (entityTypesByCode == null)
         {
             entityTypesByCode =
                     new TableMap<String, EntityTypePE>(daoFactory.getEntityTypeDAO(entityKind)
-                            .listEntityTypes(), KeyExtractorFactory
-                            .getEntityTypeByCodeKeyExtractor());
+                            .listEntityTypes(),
+                            KeyExtractorFactory.getEntityTypeByCodeKeyExtractor());
         }
         final EntityTypePE entityType = entityTypesByCode.tryGet(entityTypeCode.toUpperCase());
         if (entityType == null)
@@ -246,8 +277,14 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert
         assert registrator != null : "Unspecified registrator";
         assert properties != null : "Unspecified entity properties";
         final EntityTypePE entityTypePE = getEntityType(entityTypeCode);
+        Set<String> dynamicProperties = getDynamicProperties(entityTypePE);
+        Set<String> propertiesToUpdate = extractPropertiesToUpdate(properties);
+        checkDynamicPropertiesNotManuallyUpdated(propertiesToUpdate, dynamicProperties);
+        List<IEntityProperty> newProperties =
+                new ArrayList<IEntityProperty>(Arrays.asList(properties));
+        addDynamicPropertiesPlaceholders(newProperties, propertiesToUpdate, dynamicProperties);
         final List<T> list = new ArrayList<T>();
-        for (final IEntityProperty property : properties)
+        for (final IEntityProperty property : newProperties)
         {
             final T convertedPropertyOrNull =
                     tryConvertProperty(registrator, entityTypePE, property);
@@ -259,20 +296,33 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert
         return list;
     }
 
+    private Set<String> extractPropertiesToUpdate(IEntityProperty[] properties)
+    {
+        HashSet<String> result = new HashSet<String>();
+        for (IEntityProperty p : properties)
+        {
+            result.add(p.getPropertyType().getCode());
+        }
+        return result;
+    }
+
     public <T extends EntityPropertyPE> void checkMandatoryProperties(Collection<T> properties,
             EntityTypePE entityTypePE)
     {
         assert properties != null;
-        checkMandatoryProperties(properties, entityTypePE, daoFactory.getEntityPropertyTypeDAO(
-                entityKind).listEntityPropertyTypes(entityTypePE));
+        checkMandatoryProperties(
+                properties,
+                entityTypePE,
+                daoFactory.getEntityPropertyTypeDAO(entityKind).listEntityPropertyTypes(
+                        entityTypePE));
     }
 
     public <T extends EntityPropertyPE> void checkMandatoryProperties(Collection<T> properties,
             EntityTypePE entityTypePE, Map<EntityTypePE, List<EntityTypePropertyTypePE>> cache)
     {
         assert properties != null;
-        checkMandatoryProperties(properties, entityTypePE, getAssignedPropertiesForEntityType(
-                cache, entityTypePE));
+        checkMandatoryProperties(properties, entityTypePE,
+                getAssignedPropertiesForEntityType(cache, entityTypePE));
 
     }
 
@@ -318,8 +368,8 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert
     {
         if (entityTypPropertyType.isMandatory() && value == null)
         {
-            throw UserFailureException.fromTemplate(NO_ENTITY_PROPERTY_VALUE_FOR_S, propertyType
-                    .getCode());
+            throw UserFailureException.fromTemplate(NO_ENTITY_PROPERTY_VALUE_FOR_S,
+                    propertyType.getCode());
         }
         if (value != null)
         {
@@ -330,19 +380,22 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert
         return null;
     }
 
-    public final <T extends EntityPropertyPE> T createValidatedProperty(PropertyTypePE propertyType,
-            EntityTypePropertyTypePE entityTypPropertyType, final PersonPE registrator,
-            String validatedValue)
+    public final <T extends EntityPropertyPE> T createValidatedProperty(
+            PropertyTypePE propertyType, EntityTypePropertyTypePE entityTypPropertyType,
+            final PersonPE registrator, String validatedValue)
     {
         assert validatedValue != null;
         return createEntityProperty(registrator, propertyType, entityTypPropertyType,
                 validatedValue);
     }
 
-    public <T extends EntityPropertyPE, P extends IEntityProperty> Set<T> updateProperties(
-            Collection<T> oldProperties, EntityTypePE entityType, List<P> newProperties,
-            PersonPE registrator)
+    public <T extends EntityPropertyPE> Set<T> updateProperties(Collection<T> oldProperties,
+            EntityTypePE entityType, List<IEntityProperty> newProperties, PersonPE registrator,
+            Set<String> dynamicProperties)
     {
+        Set<String> propertiesToUpdate = extractPropertiesToUpdate(newProperties);
+        checkDynamicPropertiesNotManuallyUpdated(propertiesToUpdate, dynamicProperties);
+        addDynamicPropertiesPlaceholders(newProperties, propertiesToUpdate, dynamicProperties);
         final List<T> convertedProperties =
                 convertProperties(newProperties, entityType.getCode(), registrator);
         final Set<T> set = new HashSet<T>();
@@ -351,8 +404,8 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert
             T existingProperty = tryFind(oldProperties, newProperty);
             if (existingProperty != null)
             {
-                existingProperty.setUntypedValue(newProperty.getValue(), newProperty
-                        .getVocabularyTerm(), newProperty.getMaterialValue());
+                existingProperty.setUntypedValue(newProperty.getValue(),
+                        newProperty.getVocabularyTerm(), newProperty.getMaterialValue());
                 set.add(existingProperty);
             } else
             {
@@ -362,12 +415,26 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert
         return set;
     }
 
-    public <T extends EntityPropertyPE, P extends IEntityProperty> Set<T> updateProperties(
-            Collection<T> oldProperties, EntityTypePE entityType, List<P> newProperties,
-            PersonPE registrator, Set<String> propertiesToUpdate)
+    private <P extends IEntityProperty> Set<String> extractPropertiesToUpdate(List<P> newProperties)
     {
+        Set<String> propertiesToUpdate = new HashSet<String>();
+        for (P np : newProperties)
+        {
+            propertiesToUpdate.add(np.getPropertyType().getCode());
+        }
+        return propertiesToUpdate;
+    }
+
+    public <T extends EntityPropertyPE> Set<T> updateProperties(Collection<T> oldProperties,
+            EntityTypePE entityType, List<IEntityProperty> newProperties, PersonPE registrator,
+            Set<String> propertiesToUpdate, Set<String> dynamicProperties)
+    {
+        checkDynamicPropertiesNotManuallyUpdated(propertiesToUpdate, dynamicProperties);
+        addDynamicPropertiesPlaceholders(newProperties, propertiesToUpdate, dynamicProperties);
         // all new properties should be among propertiesToUpdate (no need to check it)
-        final Set<T> set = updateProperties(oldProperties, entityType, newProperties, registrator);
+        final Set<T> set =
+                updateProperties(oldProperties, entityType, newProperties, registrator,
+                        dynamicProperties);
         // add old properties that are not among propertiesToUpdate (preserve those properties)
         for (T oldProperty : oldProperties)
         {
@@ -381,12 +448,45 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert
         return set;
     }
 
+    private void addDynamicPropertiesPlaceholders(List<IEntityProperty> newProperties,
+            Set<String> propertiesToUpdate, Set<String> dynamicProperties)
+    {
+        propertiesToUpdate.addAll(dynamicProperties);
+        for (String dp : dynamicProperties)
+        {
+            final IEntityProperty entityProperty = new EntityProperty();
+            entityProperty.setValue(BasicConstant.PLACEHOLDER_PROPERTY_VALUE);
+            PropertyType propertyType = new PropertyType();
+            propertyType.setCode(dp);
+            entityProperty.setPropertyType(propertyType);
+            newProperties.add(entityProperty);
+        }
+    }
+
+    private void checkDynamicPropertiesNotManuallyUpdated(Set<String> propertiesToUpdate,
+            Set<String> dynamicProperties)
+    {
+        HashSet<String> allPropertiesToUpdate = new HashSet<String>();
+        for (String p : propertiesToUpdate)
+        {
+            allPropertiesToUpdate.add(p.toUpperCase());
+        }
+        HashSet<String> dynamicPropertiesToUpdate = new HashSet<String>(dynamicProperties);
+        dynamicPropertiesToUpdate.retainAll(allPropertiesToUpdate);
+        if (dynamicPropertiesToUpdate.isEmpty() == false)
+        {
+            throw new UserFailureException(String.format(
+                    "Dynamic properties cannot be updated manually [%s]",
+                    StringUtils.join(dynamicPropertiesToUpdate, ",")));
+        }
+    }
+
     private static <T extends EntityPropertyPE> T tryFind(Collection<T> oldProperties, T p)
     {
         for (T oldProperty : oldProperties)
         {
-            if (oldProperty.getEntityTypePropertyType().getPropertyType().equals(
-                    p.getEntityTypePropertyType().getPropertyType()))
+            if (oldProperty.getEntityTypePropertyType().getPropertyType()
+                    .equals(p.getEntityTypePropertyType().getPropertyType()))
             {
                 return oldProperty;
             }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBO.java
index 10f7a1fbb23..c1cc3eb88b4 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBO.java
@@ -45,6 +45,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.EventType;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPropertyPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentTypePE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentTypePropertyTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentUpdatesDTO;
 import ch.systemsx.cisd.openbis.generic.shared.dto.GroupPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE;
@@ -561,7 +562,7 @@ public final class ExperimentBO extends AbstractBusinessObject implements IExper
         final ExperimentTypePE type = experiment.getExperimentType();
         final PersonPE registrator = findRegistrator();
         experiment.setProperties(propertiesConverter.updateProperties(existingProperties, type,
-                properties, registrator));
+                properties, registrator, extractDynamicProperties(type)));
     }
 
     public void setGeneratedCode()
@@ -569,4 +570,17 @@ public final class ExperimentBO extends AbstractBusinessObject implements IExper
         final String code = createCode(EntityKind.EXPERIMENT);
         experiment.setCode(code);
     }
+
+    protected Set<String> extractDynamicProperties(final ExperimentTypePE type)
+    {
+        Set<String> dynamicProperties = new HashSet<String>();
+        for (ExperimentTypePropertyTypePE etpt : type.getExperimentTypePropertyTypes())
+        {
+            if (etpt.isDynamic())
+            {
+                dynamicProperties.add(etpt.getPropertyType().getCode());
+            }
+        }
+        return dynamicProperties;
+    }
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBO.java
index 33eaeba898e..ac130c810ac 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBO.java
@@ -44,7 +44,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetPropertyPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetUpdatesDTO;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataStorePE;
-import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.FileFormatTypePE;
@@ -212,8 +211,8 @@ public class ExternalDataBO extends AbstractExternalDataBusinessObject implement
         externalData.setRegistrator(tryToGetRegistrator(data));
         DataStorePE dataStore = getDataStoreDAO().tryToFindDataStoreByCode(data.getDataStoreCode());
         externalData.setDataStore(dataStore);
-        defineDataSetProperties(externalData, convertToDataSetProperties(data
-                .getDataSetProperties()));
+        defineDataSetProperties(externalData,
+                convertToDataSetProperties(data.getDataSetProperties()));
         externalData.setDerived(sourceType == SourceType.DERIVED);
         return dataStore;
     }
@@ -353,11 +352,11 @@ public class ExternalDataBO extends AbstractExternalDataBusinessObject implement
                 extractPropertyCodesToUpdate(properties, existingProperties);
         List<NewProperty> propertyUpdates =
                 extractNewPropertiesToUpdate(properties, propertyUpdatesCodes);
-        final EntityTypePE type = externalData.getDataSetType();
+        final DataSetTypePE type = externalData.getDataSetType();
         final PersonPE registrator = findRegistrator();
         externalData.setProperties(entityPropertiesConverter.updateProperties(existingProperties,
                 type, Arrays.asList(convertToDataSetProperties(propertyUpdates)), registrator,
-                propertyUpdatesCodes));
+                propertyUpdatesCodes, extractDynamicProperties(type)));
     }
 
     private List<NewProperty> extractNewPropertiesToUpdate(List<NewProperty> properties,
@@ -487,8 +486,8 @@ public class ExternalDataBO extends AbstractExternalDataBusinessObject implement
             {
                 throw UserFailureException.fromTemplate(
                         "Data Set '%s' is an ancestor of Data Set '%s' "
-                                + "and cannot be at the same time set as its child.", externalData
-                                .getCode(), parentToAdd.getCode());
+                                + "and cannot be at the same time set as its child.",
+                        externalData.getCode(), parentToAdd.getCode());
             } else
             {
                 final Set<TechId> nextToVisit = findParentIds(toVisit);
@@ -553,8 +552,8 @@ public class ExternalDataBO extends AbstractExternalDataBusinessObject implement
         if (missingDataSetCodes.size() > 0)
         {
             throw UserFailureException.fromTemplate(
-                    "Data Sets with following codes do not exist: '%s'.", CollectionUtils
-                            .abbreviate(missingDataSetCodes, 10));
+                    "Data Sets with following codes do not exist: '%s'.",
+                    CollectionUtils.abbreviate(missingDataSetCodes, 10));
         } else
         {
             return dataSets;
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IEntityPropertiesConverter.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IEntityPropertiesConverter.java
index 77a06a71c5b..0ed42020c17 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IEntityPropertiesConverter.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IEntityPropertiesConverter.java
@@ -47,8 +47,8 @@ public interface IEntityPropertiesConverter
             final PersonPE registrator);
 
     /**
-     * Returns given value validated and converted for given property type and entity type.
-     * Result may be null if given value is null.
+     * Returns given value validated and converted for given property type and entity type. Result
+     * may be null if given value is null.
      */
     public String tryCreateValidatedPropertyValue(PropertyTypePE propertyType,
             EntityTypePropertyTypePE entityTypPropertyType, String value);
@@ -61,17 +61,17 @@ public interface IEntityPropertiesConverter
             String validatedValue);
 
     /** Updates Set<T> of properties. */
-    public <T extends EntityPropertyPE, P extends IEntityProperty> Set<T> updateProperties(
-            Collection<T> oldProperties, EntityTypePE entityType, List<P> newProperties,
-            PersonPE registrator);
+    public <T extends EntityPropertyPE> Set<T> updateProperties(Collection<T> oldProperties,
+            EntityTypePE entityType, List<IEntityProperty> newProperties, PersonPE registrator,
+            Set<String> dynamicProperties);
 
     /**
      * Updates Set<T> of properties but preserve those old properties which codes are not among
      * <var>propertiesToUpdate</var>.
      */
-    public <T extends EntityPropertyPE, P extends IEntityProperty> Set<T> updateProperties(
-            Collection<T> oldProperties, EntityTypePE entityType, List<P> newProperties,
-            PersonPE registrator, Set<String> propertiesToUpdate);
+    public <T extends EntityPropertyPE> Set<T> updateProperties(Collection<T> oldProperties,
+            EntityTypePE entityType, List<IEntityProperty> newProperties, PersonPE registrator,
+            Set<String> propertiesToUpdate, Set<String> dynamicProperties);
 
     /**
      * Checks whether all mandatory properties are provided.
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialBO.java
index d0dbd1d1819..0062a3e491b 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialBO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialBO.java
@@ -26,14 +26,14 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
-import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EventPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.EventPE.EntityType;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EventType;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialPropertyPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
-import ch.systemsx.cisd.openbis.generic.shared.dto.EventPE.EntityType;
 import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils;
 
@@ -85,8 +85,8 @@ public final class MaterialBO extends AbstractMaterialBusinessObject implements
 
     private void checkBusinessRules()
     {
-        entityPropertiesConverter.checkMandatoryProperties(material.getProperties(), material
-                .getMaterialType());
+        entityPropertiesConverter.checkMandatoryProperties(material.getProperties(),
+                material.getMaterialType());
     }
 
     public void update(MaterialUpdateDTO materialUpdate)
@@ -103,10 +103,10 @@ public final class MaterialBO extends AbstractMaterialBusinessObject implements
     private void updateProperties(List<IEntityProperty> properties)
     {
         final Set<MaterialPropertyPE> existingProperties = material.getProperties();
-        final EntityTypePE type = material.getMaterialType();
+        final MaterialTypePE type = material.getMaterialType();
         final PersonPE registrator = findRegistrator();
         material.setProperties(entityPropertiesConverter.updateProperties(existingProperties, type,
-                properties, registrator));
+                properties, registrator, extractDynamicProperties(type)));
     }
 
     public MaterialPE getMaterial()
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialTable.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialTable.java
index 09159836f71..be82676078e 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialTable.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialTable.java
@@ -108,8 +108,8 @@ public final class MaterialTable extends AbstractMaterialBusinessObject implemen
                 new HashMap<EntityTypePE, List<EntityTypePropertyTypePE>>();
         for (MaterialPE m : materials)
         {
-            entityPropertiesConverter.checkMandatoryProperties(m.getProperties(), m
-                    .getMaterialType(), cache);
+            entityPropertiesConverter.checkMandatoryProperties(m.getProperties(),
+                    m.getMaterialType(), cache);
         }
     }
 
@@ -177,17 +177,18 @@ public final class MaterialTable extends AbstractMaterialBusinessObject implemen
             List<IEntityProperty> propertiesToUpdate, boolean deleteUntouchedProperties)
     {
         final Set<MaterialPropertyPE> existingProperties = material.getProperties();
-        final EntityTypePE type = material.getMaterialType();
+        final MaterialTypePE type = material.getMaterialType();
         final PersonPE registrator = findRegistrator();
         if (deleteUntouchedProperties)
         {
             return entityPropertiesConverter.updateProperties(existingProperties, type,
-                    propertiesToUpdate, registrator);
+                    propertiesToUpdate, registrator, extractDynamicProperties(type));
         } else
         {
             Set<String> propertiesToUpdateNames = extractCodes(propertiesToUpdate);
             return entityPropertiesConverter.updateProperties(existingProperties, type,
-                    propertiesToUpdate, registrator, propertiesToUpdateNames);
+                    propertiesToUpdate, registrator, propertiesToUpdateNames,
+                    extractDynamicProperties(type));
         }
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java
index 156fdbea070..ca9b60f6ee7 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java
@@ -32,11 +32,11 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewAttachment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample;
 import ch.systemsx.cisd.openbis.generic.shared.dto.AttachmentPE;
-import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePropertyPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.SampleTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SampleUpdatesDTO;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.IdentifierHelper;
@@ -162,8 +162,10 @@ public final class SampleBO extends AbstractSampleBusinessObject implements ISam
                 } catch (final DataAccessException e)
                 {
                     final String fileName = property.getFileName();
-                    throwException(e, String.format("Filename '%s' for sample '%s'", fileName,
-                            sample.getSampleIdentifier()));
+                    throwException(
+                            e,
+                            String.format("Filename '%s' for sample '%s'", fileName,
+                                    sample.getSampleIdentifier()));
                 }
             }
             attachments.clear();
@@ -186,8 +188,8 @@ public final class SampleBO extends AbstractSampleBusinessObject implements ISam
             getSampleDAO().updateSample(sample);
         } catch (final DataAccessException ex)
         {
-            throwException(ex, String.format("Couldn't update sample '%s'", sample
-                    .getSampleIdentifier()));
+            throwException(ex,
+                    String.format("Couldn't update sample '%s'", sample.getSampleIdentifier()));
         }
         dataChanged = false;
     }
@@ -196,8 +198,8 @@ public final class SampleBO extends AbstractSampleBusinessObject implements ISam
     {
         if (sample.getInvalidation() != null)
         {
-            throw UserFailureException.fromTemplate("Given sample '%s' is invalid.", sample
-                    .getSampleIdentifier());
+            throw UserFailureException.fromTemplate("Given sample '%s' is invalid.",
+                    sample.getSampleIdentifier());
         }
     }
 
@@ -212,9 +214,9 @@ public final class SampleBO extends AbstractSampleBusinessObject implements ISam
         if (experiment != null && experiment.getInvalidation() == null)
         {
             throw UserFailureException.fromTemplate(
-                    "Given sample code '%s' already registered for experiment '%s'.", sample
-                            .getSampleIdentifier(), IdentifierHelper
-                            .createExperimentIdentifier(experiment));
+                    "Given sample code '%s' already registered for experiment '%s'.",
+                    sample.getSampleIdentifier(),
+                    IdentifierHelper.createExperimentIdentifier(experiment));
         }
     }
 
@@ -223,8 +225,8 @@ public final class SampleBO extends AbstractSampleBusinessObject implements ISam
         if (sample.getGroup() == null)
         {
             throw UserFailureException.fromTemplate(
-                    "The sample '%s' is shared and cannot be assigned to any experiment.", sample
-                            .getSampleIdentifier());
+                    "The sample '%s' is shared and cannot be assigned to any experiment.",
+                    sample.getSampleIdentifier());
         }
     }
 
@@ -250,10 +252,10 @@ public final class SampleBO extends AbstractSampleBusinessObject implements ISam
     private void updateProperties(List<IEntityProperty> properties)
     {
         final Set<SamplePropertyPE> existingProperties = sample.getProperties();
-        final EntityTypePE type = sample.getSampleType();
+        final SampleTypePE type = sample.getSampleType();
         final PersonPE registrator = findRegistrator();
         sample.setProperties(entityPropertiesConverter.updateProperties(existingProperties, type,
-                properties, registrator));
+                properties, registrator, extractDynamicProperties(type)));
     }
 
     private void updateParents(SampleUpdatesDTO updates)
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 2be1156625d..4f186dd7e80 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
@@ -254,10 +254,11 @@ public final class SampleTable extends AbstractSampleBusinessObject implements I
             Set<String> propertiesToUpdate)
     {
         final Set<SamplePropertyPE> existingProperties = sample.getProperties();
-        final EntityTypePE type = sample.getSampleType();
+        final SampleTypePE type = sample.getSampleType();
         final PersonPE registrator = findRegistrator();
+        Set<String> dynamicProperties = extractDynamicProperties(type);
         sample.setProperties(entityPropertiesConverter.updateProperties(existingProperties, type,
-                properties, registrator, propertiesToUpdate));
+                properties, registrator, propertiesToUpdate, dynamicProperties));
     }
 
     public void prepareForUpdate(List<SampleBatchUpdatesDTO> updates)
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/experiment/PropertiesEditor.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/experiment/PropertiesEditor.java
index 9bdc91e1342..49c5c7f2d3e 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/experiment/PropertiesEditor.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/experiment/PropertiesEditor.java
@@ -34,7 +34,6 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.field.P
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.widget.FieldUtil;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.util.GWTUtils;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.util.lang.StringEscapeUtils;
-import ch.systemsx.cisd.openbis.generic.shared.basic.BasicConstant;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityTypePropertyType;
@@ -166,16 +165,7 @@ abstract public class PropertiesEditor<T extends EntityType, S extends EntityTyp
             Object value = field.get().getValue();
             final S etpt = field.get().getData(ETPT); // null for section labels
 
-            // dynamic property should always have a placeholder value
-            // that will be filled later by by calculator
-            if (etpt != null && etpt.isDynamic())
-            {
-                final IEntityProperty entityProperty = createEntityProperty();
-                entityProperty.setValue(BasicConstant.PLACEHOLDER_PROPERTY_VALUE);
-                entityProperty.setPropertyType(etpt.getPropertyType());
-                properties.add(entityProperty);
-            } else if (etpt != null && value != null
-                    && PropertyFieldFactory.valueToString(value) != null)
+            if (etpt != null && value != null && PropertyFieldFactory.valueToString(value) != null)
             {
                 final IEntityProperty entityProperty = createEntityProperty();
                 entityProperty.setValue(PropertyFieldFactory.valueToString(value));
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBOTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBOTest.java
index f0d6adaa245..4033ba6771d 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBOTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBOTest.java
@@ -41,6 +41,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewExperiment;
 import ch.systemsx.cisd.openbis.generic.shared.dto.AttachmentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePropertyTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EventPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPropertyPE;
@@ -101,7 +102,7 @@ public final class ExperimentBOTest extends AbstractBOTest
             {
                 {
                     one(propertiesConverter).updateProperties(oldProperties, entityType,
-                            newProperties, registrator);
+                            newProperties, registrator, new HashSet<String>());
                     will(returnValue(new HashSet<ExperimentPropertyPE>(updated)));
 
                 }
@@ -167,12 +168,16 @@ public final class ExperimentBOTest extends AbstractBOTest
         expBO.loadByExperimentIdentifier(identifier);
 
         // Get first attachment
-        AssertJUnit.assertEquals(attachment1, expBO.getExperimentFileAttachment(attachment1
-                .getFileName(), attachment1.getVersion()));
+        AssertJUnit.assertEquals(
+                attachment1,
+                expBO.getExperimentFileAttachment(attachment1.getFileName(),
+                        attachment1.getVersion()));
 
         // Get another version of attachment
-        AssertJUnit.assertEquals(attachment2, expBO.getExperimentFileAttachment(attachment2
-                .getFileName(), attachment2.getVersion()));
+        AssertJUnit.assertEquals(
+                attachment2,
+                expBO.getExperimentFileAttachment(attachment2.getFileName(),
+                        attachment2.getVersion()));
 
         // Try find not existing version of attachment
         testThrowingExceptionOnUnknownFileVersion(attachment2, expBO);
@@ -291,6 +296,10 @@ public final class ExperimentBOTest extends AbstractBOTest
 
                     one(projectDAO).tryFindProject(dbCode, groupCode, projectCode);
                     will(returnValue(null));
+
+                    one(entityPropertyTypeDAO).listEntityPropertyTypes(type);
+                    will(returnValue(new ArrayList<EntityTypePropertyTypePE>()));
+
                 }
             });
         final ExperimentBO experimentBO = createExperimentBO();
@@ -302,8 +311,8 @@ public final class ExperimentBOTest extends AbstractBOTest
         {
             exceptionThrown = true;
             assertTrue(e.getMessage().indexOf(
-                    String.format(ExperimentBO.ERR_PROJECT_NOT_FOUND, createIdentifier(dbCode,
-                            groupCode, projectCode, expCode))) > -1);
+                    String.format(ExperimentBO.ERR_PROJECT_NOT_FOUND,
+                            createIdentifier(dbCode, groupCode, projectCode, expCode))) > -1);
         }
         assertTrue(exceptionThrown);
 
@@ -417,8 +426,8 @@ public final class ExperimentBOTest extends AbstractBOTest
 
         final List<IEntityProperty> newProperties = createDummyProperties();
         prepareUpdateProperties(exp.getProperties(), newProperties, experimentType,
-                ManagerTestTool.EXAMPLE_SESSION.tryGetPerson(), Arrays.asList(changedProperty,
-                        addedProperty));
+                ManagerTestTool.EXAMPLE_SESSION.tryGetPerson(),
+                Arrays.asList(changedProperty, addedProperty));
         bo.updateProperties(newProperties);
 
         assertTrue(bo.getExperiment().getProperties().contains(changedProperty));
@@ -735,9 +744,7 @@ public final class ExperimentBOTest extends AbstractBOTest
         exceptionThrown = false;
         try
         {
-            expBO
-                    .getExperimentFileAttachment("nonexistentAttachment.txt", attachment2
-                            .getVersion());
+            expBO.getExperimentFileAttachment("nonexistentAttachment.txt", attachment2.getVersion());
         } catch (UserFailureException e)
         {
             exceptionThrown = true;
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBOTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBOTest.java
index bfbc5abf8b6..9218af07cce 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBOTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBOTest.java
@@ -23,6 +23,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
 
 import org.hamcrest.BaseMatcher;
@@ -43,6 +44,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SourceType;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetPropertyPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetTypePE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetTypePropertyTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetUpdatesDTO;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataStorePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE;
@@ -76,16 +78,15 @@ public class ExternalDataBOTest extends AbstractBOTest
     private static final DatabaseInstanceIdentifier DATABASE_INSTANCE_IDENTIFIER =
             new DatabaseInstanceIdentifier(ManagerTestTool.EXAMPLE_DATABASE_INSTANCE.getCode());
 
-    private static final GroupIdentifier GROUP_IDENTIFIER =
-            new GroupIdentifier(DATABASE_INSTANCE_IDENTIFIER, ManagerTestTool.EXAMPLE_GROUP
-                    .getCode());
+    private static final GroupIdentifier GROUP_IDENTIFIER = new GroupIdentifier(
+            DATABASE_INSTANCE_IDENTIFIER, ManagerTestTool.EXAMPLE_GROUP.getCode());
 
-    private static final SampleIdentifier SAMPLE_IDENTIFIER =
-            new SampleIdentifier(GROUP_IDENTIFIER, "EXAMPLE_SAMPLE");
+    private static final SampleIdentifier SAMPLE_IDENTIFIER = new SampleIdentifier(
+            GROUP_IDENTIFIER, "EXAMPLE_SAMPLE");
 
-    private static final ExperimentIdentifier EXPERIMENT_IDENTIFIER =
-            new ExperimentIdentifier(new ProjectIdentifier(GROUP_IDENTIFIER,
-                    ManagerTestTool.EXAMPLE_PROJECT.getCode()), "EXPERIMENT_CODE");
+    private static final ExperimentIdentifier EXPERIMENT_IDENTIFIER = new ExperimentIdentifier(
+            new ProjectIdentifier(GROUP_IDENTIFIER, ManagerTestTool.EXAMPLE_PROJECT.getCode()),
+            "EXPERIMENT_CODE");
 
     private static final String DATA_STORE_CODE = "dss1";
 
@@ -160,7 +161,7 @@ public class ExternalDataBOTest extends AbstractBOTest
         assertEquals(null, externalData.getRegistrator());
         context.assertIsSatisfied();
     }
-    
+
     @Test
     public void testDefineWithUserID()
     {
@@ -186,11 +187,11 @@ public class ExternalDataBOTest extends AbstractBOTest
                     will(returnValue(registrator));
                 }
             });
-        
+
         IExternalDataBO dataBO = createExternalDataBO();
         dataBO.define(data, sample, SourceType.DERIVED);
         ExternalDataPE externalData = dataBO.getExternalData();
-        
+
         assertEquals(DATA_SET_CODE, externalData.getCode());
         assertEquals(BooleanOrUnknown.U, externalData.getComplete());
         assertEquals(DATA_PRODUCER_CODE, externalData.getDataProducerCode());
@@ -208,7 +209,7 @@ public class ExternalDataBOTest extends AbstractBOTest
         assertSame(registrator, externalData.getRegistrator());
         context.assertIsSatisfied();
     }
-    
+
     @Test
     public void testDefineWithUserEMail()
     {
@@ -229,17 +230,17 @@ public class ExternalDataBOTest extends AbstractBOTest
         final PersonPE registrator = new PersonPE();
         registrator.setEmail(data.getUserEMail());
         context.checking(new Expectations()
-        {
             {
-                one(personDAO).listPersons();
-                will(returnValue(Arrays.asList(new PersonPE(), registrator)));
-            }
-        });
-        
+                {
+                    one(personDAO).listPersons();
+                    will(returnValue(Arrays.asList(new PersonPE(), registrator)));
+                }
+            });
+
         IExternalDataBO dataBO = createExternalDataBO();
         dataBO.define(data, sample, SourceType.DERIVED);
         ExternalDataPE externalData = dataBO.getExternalData();
-        
+
         assertEquals(DATA_SET_CODE, externalData.getCode());
         assertEquals(BooleanOrUnknown.U, externalData.getComplete());
         assertEquals(DATA_PRODUCER_CODE, externalData.getDataProducerCode());
@@ -424,7 +425,7 @@ public class ExternalDataBOTest extends AbstractBOTest
                     will(returnValue(fileFormatTypePE));
 
                     one(propertiesConverter).checkMandatoryProperties(
-                            Collections.<DataSetPropertyPE> emptySet(), null);
+                            Collections.<DataSetPropertyPE> emptySet(), dataSet.getDataSetType());
 
                     one(externalDataDAO).validateAndSaveUpdatedEntity(dataSet);
                 }
@@ -432,7 +433,6 @@ public class ExternalDataBOTest extends AbstractBOTest
 
         IExternalDataBO dataBO = createExternalDataBO();
         dataBO.update(dataSetUpdatesDTO);
-
         context.assertIsSatisfied();
     }
 
@@ -491,8 +491,8 @@ public class ExternalDataBOTest extends AbstractBOTest
             fail("UserFailureException expected");
         } catch (UserFailureException e)
         {
-            assertEquals("Data Sets with following codes do not exist: '[" + PARENT_CODE + "]'.", e
-                    .getMessage());
+            assertEquals("Data Sets with following codes do not exist: '[" + PARENT_CODE + "]'.",
+                    e.getMessage());
         }
 
         context.assertIsSatisfied();
@@ -508,8 +508,8 @@ public class ExternalDataBOTest extends AbstractBOTest
                     will(returnValue(dataSet));
 
                     one(propertiesConverter).updateProperties(
-                            Collections.<DataSetPropertyPE> emptySet(), null, null,
-                            ManagerTestTool.EXAMPLE_PERSON);
+                            Collections.<DataSetPropertyPE> emptySet(), dataSet.getDataSetType(),
+                            null, ManagerTestTool.EXAMPLE_PERSON, Collections.<String> emptySet());
                     will(returnValue(Collections.emptySet()));
 
                     one(databaseInstanceDAO).tryFindDatabaseInstanceByCode(
@@ -576,6 +576,13 @@ public class ExternalDataBOTest extends AbstractBOTest
         dataSet.setModificationDate(PRODUCTION_DATE);
         dataSet.setSample(sampleOrNull);
         dataSet.setExperiment(experimentOrNull);
+        DataSetTypePE dataSetType = new DataSetTypePE();
+        dataSetType.setCode(DATA_SET_TYPE.getCode());
+        DatabaseInstancePE databaseInstance = new DatabaseInstancePE();
+        databaseInstance.setCode("db");
+        dataSetType.setDatabaseInstance(databaseInstance);
+        dataSetType.setDataSetTypePropertyTypes(new HashSet<DataSetTypePropertyTypePE>());
+        dataSet.setDataSetType(dataSetType);
         return dataSet;
     }
 
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBOTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBOTest.java
index 67f6dc3cd53..419d06b3c34 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBOTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBOTest.java
@@ -26,6 +26,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -71,6 +72,8 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifierFa
  */
 public final class SampleBOTest extends AbstractBOTest
 {
+    private static final String SAMPLE_TYPE = "sample-type";
+
     private static final TechId SAMPLE_TECH_ID = CommonTestUtils.TECH_ID;
 
     private static final String DB = "DB";
@@ -91,18 +94,28 @@ public final class SampleBOTest extends AbstractBOTest
         final SamplePE sample = new SamplePE();
         sample.setCode(DEFAULT_SAMPLE_CODE);
         sample.setModificationDate(new Date());
+        sample.setSampleType(createSampleTypePE(SAMPLE_TYPE));
         return sample;
     }
 
+    private static SampleTypePE createSampleTypePE(String typeCode)
+    {
+        SampleTypePE sampleType = new SampleTypePE();
+        sampleType.setCode(typeCode);
+        DatabaseInstancePE databaseInstance = new DatabaseInstancePE();
+        databaseInstance.setCode(DB);
+        sampleType.setDatabaseInstance(databaseInstance);
+        sampleType.setSampleTypePropertyTypes(new HashSet<SampleTypePropertyTypePE>());
+        return sampleType;
+    }
+
     private static SamplePE createSample(final SampleIdentifier sampleIdentifier,
             final String sampleTypeCode)
     {
         final SamplePE sample = new SamplePE();
         sample.setCode(sampleIdentifier.getSampleCode());
         sample.setRegistrator(EXAMPLE_PERSON);
-        final SampleTypePE sampleTypeDTO = new SampleTypePE();
-        sampleTypeDTO.setCode(sampleTypeCode);
-        sample.setSampleType(sampleTypeDTO);
+        sample.setSampleType(createSampleTypePE(sampleTypeCode));
         return sample;
     }
 
@@ -380,6 +393,7 @@ public final class SampleBOTest extends AbstractBOTest
         sample.setCode("sampleCode");
         sample.setExperiment(sampleExperiment);
         sample.setGroup(EXAMPLE_GROUP);
+        sample.setSampleType(createSampleTypePE(SAMPLE_TYPE));
 
         Date now = new Date();
         sample.setModificationDate(now);
@@ -578,6 +592,7 @@ public final class SampleBOTest extends AbstractBOTest
         sample.setId(SAMPLE_TECH_ID.getId());
         sample.setCode(code);
         sample.setGroup(group);
+        sample.setSampleType(createSampleTypePE(SAMPLE_TYPE));
         return sample;
     }
 
@@ -714,8 +729,8 @@ public final class SampleBOTest extends AbstractBOTest
         try
         {
             SampleUpdatesDTO updates =
-                    new SampleUpdatesDTO(SAMPLE_TECH_ID, null, null, Collections
-                            .<NewAttachment> emptyList(), now, null, null, null);
+                    new SampleUpdatesDTO(SAMPLE_TECH_ID, null, null,
+                            Collections.<NewAttachment> emptyList(), now, null, null, null);
             createSampleBO().update(updates);
         } catch (UserFailureException e)
         {
@@ -972,8 +987,8 @@ public final class SampleBOTest extends AbstractBOTest
         context.checking(new Expectations()
             {
                 {
-                    one(propertiesConverter).updateProperties(sample.getProperties(), null, null,
-                            EXAMPLE_PERSON);
+                    one(propertiesConverter).updateProperties(sample.getProperties(),
+                            sample.getSampleType(), null, EXAMPLE_PERSON, new HashSet<String>());
                 }
             });
     }
-- 
GitLab