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
new file mode 100644
index 0000000000000000000000000000000000000000..6ee3368197d30644a6906aa88e9945d078ab26a4
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractMaterialBusinessObject.java
@@ -0,0 +1,63 @@
+/*
+ * 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.server.business.bo;
+
+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.Session;
+import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
+
+/**
+ * Generic operations on materials.
+ * 
+ * @author Tomasz Pylak
+ */
+public class AbstractMaterialBusinessObject extends AbstractBusinessObject
+{
+    protected final IEntityPropertiesConverter entityPropertiesConverter;
+
+    protected AbstractMaterialBusinessObject(final IDAOFactory daoFactory, final Session session)
+    {
+        this(daoFactory, session, new EntityPropertiesConverter(EntityKind.MATERIAL, daoFactory));
+    }
+
+    protected AbstractMaterialBusinessObject(final IDAOFactory daoFactory, final Session session,
+            final IEntityPropertiesConverter entityPropertiesConverter)
+    {
+        super(daoFactory, session);
+        this.entityPropertiesConverter = entityPropertiesConverter;
+    }
+
+    private static final String PROPERTY_TYPES = "materialType.materialTypePropertyTypesInternal";
+
+    protected MaterialPE getMaterialById(final TechId materialId)
+    {
+        assert materialId != null : "Material technical id unspecified.";
+        String[] connections =
+            { PROPERTY_TYPES };
+        final MaterialPE result = getMaterialDAO().tryGetByTechId(materialId, connections);
+        if (result == null)
+        {
+            throw new UserFailureException(String.format("Material with ID '%s' does not exist.",
+                    materialId));
+        }
+        return result;
+    }
+
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IMaterialBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IMaterialBO.java
index 0d2948fd7ff836a211236b2ffab7eb7d0f764f7b..c9037533ca4e048da086a5d1b435cef7c98afe04 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IMaterialBO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IMaterialBO.java
@@ -16,13 +16,9 @@
 
 package ch.systemsx.cisd.openbis.generic.server.business.bo;
 
-import java.util.Date;
-import java.util.List;
 
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
-import ch.systemsx.cisd.common.utilities.AbstractHashable;
 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.MaterialPE;
 
 /**
@@ -38,14 +34,6 @@ public interface IMaterialBO extends IEntityBusinessObject
     /** Adds properties */
     public void enrichWithProperties();
 
-    /**
-     * Changes given materials. Currently allowed changes: properties.
-     * 
-     * @param deleteUntouchedProperties if true all old properties which have not been mentioned in
-     *            the update list will be deleted.
-     */
-    public void update(List<MaterialUpdateDTO> materialsUpdate, boolean deleteUntouchedProperties);
-
     /**
      * Changes given material. Currently allowed changes: properties.
      */
@@ -59,36 +47,4 @@ public interface IMaterialBO extends IEntityBusinessObject
      */
     void deleteByTechId(TechId materialId, String reason);
 
-    /** Describes the material update operation, currently only properties can be changed. */
-    public static class MaterialUpdateDTO extends AbstractHashable
-    {
-        private final TechId materialId;
-
-        private final List<IEntityProperty> properties;
-
-        private final Date version;
-
-        public MaterialUpdateDTO(TechId materialId, List<IEntityProperty> properties, Date version)
-        {
-            this.materialId = materialId;
-            this.properties = properties;
-            this.version = version;
-        }
-
-        public TechId getMaterialId()
-        {
-            return materialId;
-        }
-
-        public List<IEntityProperty> getProperties()
-        {
-            return properties;
-        }
-
-        public Date getVersion()
-        {
-            return version;
-        }
-    }
-
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IMaterialTable.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IMaterialTable.java
index 32317e068ba47e1afbe0b9cf372f77526a7016d3..17ab3918da37e5072eaac3ca2b9df45f6fe34884 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IMaterialTable.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IMaterialTable.java
@@ -34,10 +34,20 @@ public interface IMaterialTable
     public List<MaterialPE> getMaterials();
 
     /**
-     * Defines new materials of specified type.
+     * Defines new materials of specified type.<br>
+     * Calls of this method cannot be mixed with calls to {@link #update}.
      */
     public void add(List<NewMaterial> newMaterials, MaterialTypePE materialTypePE);
 
+    /**
+     * Changes given materials. Currently allowed changes: properties.<br>
+     * Calls of this method cannot be mixed with calls to {@link #add}.
+     * 
+     * @param deleteUntouchedProperties if true all old properties which have not been mentioned in
+     *            the update list will be deleted.
+     */
+    public void update(List<MaterialUpdateDTO> materialsUpdate, boolean deleteUntouchedProperties);
+
     /**
      * Saves new materials in the database.
      */
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 d00bfa40147ab49fcd0b83875e1fa8f7d0dc7b3d..d0dbd1d181986ac94335cba119b1da3a9b174b28 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
@@ -17,13 +17,11 @@
 package ch.systemsx.cisd.openbis.generic.server.business.bo;
 
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
 import org.springframework.dao.DataAccessException;
 
-import ch.rinn.restrictions.Private;
 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;
@@ -44,26 +42,15 @@ import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils;
  * 
  * @author Izabela Adamczyk
  */
-public final class MaterialBO extends AbstractBusinessObject implements IMaterialBO
+public final class MaterialBO extends AbstractMaterialBusinessObject implements IMaterialBO
 {
-
-    private final IEntityPropertiesConverter propertiesConverter;
-
     private MaterialPE material;
 
     private boolean dataChanged;
 
     public MaterialBO(final IDAOFactory daoFactory, final Session session)
-    {
-        this(daoFactory, session, new EntityPropertiesConverter(EntityKind.MATERIAL, daoFactory));
-    }
-
-    @Private
-    MaterialBO(final IDAOFactory daoFactory, final Session session,
-            final IEntityPropertiesConverter entityPropertiesConverter)
     {
         super(daoFactory, session);
-        propertiesConverter = entityPropertiesConverter;
     }
 
     public void loadDataByTechId(TechId materialId)
@@ -72,22 +59,6 @@ public final class MaterialBO extends AbstractBusinessObject implements IMateria
         dataChanged = false;
     }
 
-    private static final String PROPERTY_TYPES = "materialType.materialTypePropertyTypesInternal";
-
-    private MaterialPE getMaterialById(final TechId materialId)
-    {
-        assert materialId != null : "Material technical id unspecified.";
-        String[] connections =
-            { PROPERTY_TYPES };
-        final MaterialPE result = getMaterialDAO().tryGetByTechId(materialId, connections);
-        if (result == null)
-        {
-            throw new UserFailureException(String.format("Material with ID '%s' does not exist.",
-                    materialId));
-        }
-        return result;
-    }
-
     public final void enrichWithProperties()
     {
         if (material != null)
@@ -114,71 +85,28 @@ public final class MaterialBO extends AbstractBusinessObject implements IMateria
 
     private void checkBusinessRules()
     {
-        propertiesConverter.checkMandatoryProperties(material.getProperties(), material
+        entityPropertiesConverter.checkMandatoryProperties(material.getProperties(), material
                 .getMaterialType());
     }
 
     public void update(MaterialUpdateDTO materialUpdate)
-    {
-        update(materialUpdate, true);
-        dataChanged = true;
-    }
-
-    public void update(List<MaterialUpdateDTO> materialsUpdate, boolean deleteUntouchedProperties)
-    {
-        setBatchUpdateMode(true);
-        for (MaterialUpdateDTO materialUpdate : materialsUpdate)
-        {
-            update(materialUpdate, deleteUntouchedProperties);
-        }
-        setBatchUpdateMode(false);
-        dataChanged = true;
-    }
-
-    private void update(MaterialUpdateDTO materialUpdate, boolean deleteUntouchedProperties)
     {
         loadDataByTechId(materialUpdate.getMaterialId());
         if (materialUpdate.getVersion().equals(material.getModificationDate()) == false)
         {
             throwModifiedEntityException("Material");
         }
-        updateProperties(materialUpdate.getProperties(), deleteUntouchedProperties);
-    }
-
-    private void updateProperties(List<IEntityProperty> properties,
-            boolean deleteUntouchedProperties)
-    {
-        Set<MaterialPropertyPE> newProperties =
-                calculateNewProperties(properties, deleteUntouchedProperties);
-        material.setProperties(newProperties);
+        updateProperties(materialUpdate.getProperties());
+        dataChanged = true;
     }
 
-    private Set<MaterialPropertyPE> calculateNewProperties(
-            List<IEntityProperty> propertiesToUpdate, boolean deleteUntouchedProperties)
+    private void updateProperties(List<IEntityProperty> properties)
     {
         final Set<MaterialPropertyPE> existingProperties = material.getProperties();
         final EntityTypePE type = material.getMaterialType();
         final PersonPE registrator = findRegistrator();
-        if (deleteUntouchedProperties)
-        {
-            return propertiesConverter.updateProperties(existingProperties, type,
-                    propertiesToUpdate, registrator);
-        } else
-        {
-            Set<String> propertiesToUpdateNames = extractCodes(propertiesToUpdate);
-            return propertiesConverter.updateProperties(existingProperties, type,
-                    propertiesToUpdate, registrator, propertiesToUpdateNames);
-        }
-    }
-
-    private static Set<String> extractCodes(List<IEntityProperty> propertiesToUpdate)
-    {
-        Set<String> names = new HashSet<String>();
-        for (IEntityProperty p : propertiesToUpdate)
-        {
-            names.add(p.getPropertyType().getCode());
-        }
-        return names;
+        material.setProperties(entityPropertiesConverter.updateProperties(existingProperties, type,
+                properties, registrator));
     }
 
     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 16a38f35aced010de8756d97a58c1f1eb395bbb8..09159836f7120b71a2c043b3c3e7ecf22078ff9b 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
@@ -18,8 +18,10 @@ package ch.systemsx.cisd.openbis.generic.server.business.bo;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.springframework.dao.DataAccessException;
 
@@ -32,35 +34,32 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePropertyTypePE;
 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.properties.EntityKind;
 
 /**
  * The only productive implementation of {@link IMaterialTable}.
  * 
  * @author Izabela Adamczyk
  */
-public final class MaterialTable extends AbstractBusinessObject implements IMaterialTable
+public final class MaterialTable extends AbstractMaterialBusinessObject implements IMaterialTable
 {
     private List<MaterialPE> materials;
 
     private boolean dataChanged;
 
-    private final IEntityPropertiesConverter entityPropertiesConverter;
-
     public MaterialTable(final IDAOFactory daoFactory, final Session session)
     {
-        this(daoFactory, session, new EntityPropertiesConverter(EntityKind.MATERIAL, daoFactory),
-                null, false);
+        super(daoFactory, session);
     }
 
     @Private
+    // for tests only
     MaterialTable(final IDAOFactory daoFactory, final Session session,
             final IEntityPropertiesConverter entityPropertiesConverter, List<MaterialPE> materials,
             boolean dataChanged)
     {
-        super(daoFactory, session);
-        this.entityPropertiesConverter = entityPropertiesConverter;
+        super(daoFactory, session, entityPropertiesConverter);
         this.materials = materials;
         this.dataChanged = dataChanged;
     }
@@ -140,4 +139,65 @@ public final class MaterialTable extends AbstractBusinessObject implements IMate
             material.addProperty(materialProperty);
         }
     }
+
+    // -----------------
+
+    public void update(List<MaterialUpdateDTO> materialsUpdate, boolean deleteUntouchedProperties)
+    {
+        if (materials == null)
+        {
+            materials = new ArrayList<MaterialPE>();
+        }
+        setBatchUpdateMode(true);
+        for (MaterialUpdateDTO materialUpdate : materialsUpdate)
+        {
+            MaterialPE materialPE = update(materialUpdate, deleteUntouchedProperties);
+            materials.add(materialPE);
+        }
+        setBatchUpdateMode(false);
+
+        dataChanged = true;
+    }
+
+    private MaterialPE update(MaterialUpdateDTO materialUpdate, boolean deleteUntouchedProperties)
+    {
+        MaterialPE material = getMaterialById(materialUpdate.getMaterialId());
+        if (materialUpdate.getVersion().equals(material.getModificationDate()) == false)
+        {
+            throwModifiedEntityException("Material");
+        }
+        Set<MaterialPropertyPE> newProperties =
+                calculateNewProperties(material, materialUpdate.getProperties(),
+                        deleteUntouchedProperties);
+        material.setProperties(newProperties);
+        return material;
+    }
+
+    private Set<MaterialPropertyPE> calculateNewProperties(MaterialPE material,
+            List<IEntityProperty> propertiesToUpdate, boolean deleteUntouchedProperties)
+    {
+        final Set<MaterialPropertyPE> existingProperties = material.getProperties();
+        final EntityTypePE type = material.getMaterialType();
+        final PersonPE registrator = findRegistrator();
+        if (deleteUntouchedProperties)
+        {
+            return entityPropertiesConverter.updateProperties(existingProperties, type,
+                    propertiesToUpdate, registrator);
+        } else
+        {
+            Set<String> propertiesToUpdateNames = extractCodes(propertiesToUpdate);
+            return entityPropertiesConverter.updateProperties(existingProperties, type,
+                    propertiesToUpdate, registrator, propertiesToUpdateNames);
+        }
+    }
+
+    private static Set<String> extractCodes(List<IEntityProperty> propertiesToUpdate)
+    {
+        Set<String> names = new HashSet<String>();
+        for (IEntityProperty p : propertiesToUpdate)
+        {
+            names.add(p.getPropertyType().getCode());
+        }
+        return names;
+    }
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialUpdateDTO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialUpdateDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a51894c3f97680c34c1b456ce7459cbadca0f8b
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialUpdateDTO.java
@@ -0,0 +1,44 @@
+package ch.systemsx.cisd.openbis.generic.server.business.bo;
+
+import java.util.Date;
+import java.util.List;
+
+import ch.systemsx.cisd.common.utilities.AbstractHashable;
+import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
+
+/**
+ * Describes the material update operation, currently only properties can be changed.
+ * 
+ * @author Tomasz Pylak
+ */
+public class MaterialUpdateDTO extends AbstractHashable
+{
+    private final TechId materialId;
+
+    private final List<IEntityProperty> properties;
+
+    private final Date version;
+
+    public MaterialUpdateDTO(TechId materialId, List<IEntityProperty> properties, Date version)
+    {
+        this.materialId = materialId;
+        this.properties = properties;
+        this.version = version;
+    }
+
+    public TechId getMaterialId()
+    {
+        return materialId;
+    }
+
+    public List<IEntityProperty> getProperties()
+    {
+        return properties;
+    }
+
+    public Date getVersion()
+    {
+        return version;
+    }
+}
\ No newline at end of file
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 0701d10fbf14a1983735e633f1c2cf236e9779cc..dd938ccd1c9eff3e52aab87a0edb821a46e6dea6 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
@@ -44,7 +44,7 @@ import ch.systemsx.cisd.openbis.generic.server.business.bo.IMaterialBO;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IMaterialTable;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IProjectBO;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.ISampleBO;
-import ch.systemsx.cisd.openbis.generic.server.business.bo.IMaterialBO.MaterialUpdateDTO;
+import ch.systemsx.cisd.openbis.generic.server.business.bo.MaterialUpdateDTO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.server.plugin.IDataSetTypeSlaveServerPlugin;
 import ch.systemsx.cisd.openbis.generic.server.plugin.ISampleTypeSlaveServerPlugin;
@@ -640,10 +640,14 @@ public final class GenericServer extends AbstractServer<IGenericServer> implemen
 
     private void updateMaterials(String sessionToken, List<MaterialUpdateDTO> updates)
     {
+        if (updates.size() == 0)
+        {
+            return;
+        }
         final Session session = getSession(sessionToken);
-        final IMaterialBO materialBO = businessObjectFactory.createMaterialBO(session);
-        materialBO.update(updates, false);
-        materialBO.save();
+        IMaterialTable materialTable = businessObjectFactory.createMaterialTable(session);
+        materialTable.update(updates, false);
+        materialTable.save();
     }
 
     private Map<String/* code */, Material> listMaterials(String sessionToken,
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServerTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServerTest.java
index b06dc4862d2b111ab7695250b55e7d3bf83c5ff2..585cdfbaeb5f8c00dde16844b674590011eef624 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServerTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServerTest.java
@@ -28,7 +28,7 @@ import org.testng.annotations.Test;
 
 import ch.rinn.restrictions.Friend;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
-import ch.systemsx.cisd.openbis.generic.server.business.bo.IMaterialBO.MaterialUpdateDTO;
+import ch.systemsx.cisd.openbis.generic.server.business.bo.MaterialUpdateDTO;
 import ch.systemsx.cisd.openbis.generic.server.plugin.IDataSetTypeSlaveServerPlugin;
 import ch.systemsx.cisd.openbis.generic.server.plugin.ISampleTypeSlaveServerPlugin;
 import ch.systemsx.cisd.openbis.generic.shared.AbstractServerTestCase;