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 222e3838a189019fb96ec21af185ea3a0fce7098..4790cd58c9005b03129d3994ba881946d5a0f0a3 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
@@ -21,6 +21,7 @@ import java.util.List;
 
 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.dto.MaterialProperty;
@@ -47,14 +48,19 @@ public final class MaterialTable extends AbstractBusinessObject implements IMate
 
     public MaterialTable(final IDAOFactory daoFactory, final Session session)
     {
-        this(daoFactory, session, new EntityPropertiesConverter(EntityKind.MATERIAL, daoFactory));
+        this(daoFactory, session, new EntityPropertiesConverter(EntityKind.MATERIAL, daoFactory,
+                null), null, false);
     }
 
+    @Private
     MaterialTable(final IDAOFactory daoFactory, final Session session,
-            final IEntityPropertiesConverter entityPropertiesConverter)
+            final IEntityPropertiesConverter entityPropertiesConverter, List<MaterialPE> materials,
+            boolean dataChanged)
     {
         super(daoFactory, session);
         this.entityPropertiesConverter = entityPropertiesConverter;
+        this.materials = materials;
+        this.dataChanged = dataChanged;
     }
 
     public final void load(final String materialTypeCode)
@@ -126,7 +132,10 @@ public final class MaterialTable extends AbstractBusinessObject implements IMate
         material.setRegistrator(findRegistrator());
         material.setMaterialType(materialTypePE);
         material.setDatabaseInstance(getHomeDatabaseInstance());
-        defineMaterialProperties(material, newMaterial.getProperties());
+        if (newMaterial.getProperties().length > 0)
+        {
+            defineMaterialProperties(material, newMaterial.getProperties());
+        }
         return material;
     }
 
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialTableTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialTableTest.java
index 4f801a2c20244f42f371716d9ade8712769059ed..a9c30a001c7599b79e2a41b53e40ea7c01401e35 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialTableTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/MaterialTableTest.java
@@ -16,13 +16,18 @@
 
 package ch.systemsx.cisd.openbis.generic.server.business.bo;
 
+import static ch.systemsx.cisd.openbis.generic.server.business.ManagerTestTool.EXAMPLE_SESSION;
+
 import java.util.ArrayList;
+import java.util.List;
 
 import org.jmock.Expectations;
 import org.testng.annotations.Test;
 
+import ch.rinn.restrictions.Friend;
 import ch.systemsx.cisd.openbis.generic.server.business.ManagerTestTool;
 import ch.systemsx.cisd.openbis.generic.shared.CommonTestUtils;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewMaterial;
 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.properties.EntityKind;
@@ -32,11 +37,18 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
  * 
  * @author Izabela Adamczyk
  */
+@Friend(toClasses = MaterialTable.class)
 public final class MaterialTableTest extends AbstractBOTest
 {
+    private final MaterialTable createMaterialTable(List<MaterialPE> materials, boolean dataChanged)
+    {
+        return new MaterialTable(daoFactory, ManagerTestTool.EXAMPLE_SESSION, propertiesConverter,
+                materials, dataChanged);
+    }
+
     private final MaterialTable createMaterialTable()
     {
-        return new MaterialTable(daoFactory, ManagerTestTool.EXAMPLE_SESSION);
+        return createMaterialTable(null, false);
     }
 
     @Test
@@ -62,4 +74,59 @@ public final class MaterialTableTest extends AbstractBOTest
         createMaterialTable().load(materialType.getCode());
         context.assertIsSatisfied();
     }
+
+    @Test
+    public void testAddMaterials() throws Exception
+    {
+        final MaterialTypePE materialType = CommonTestUtils.createMaterialType();
+        List<NewMaterial> newMaterials = new ArrayList<NewMaterial>();
+        newMaterials.add(createNewMaterial("BRAND_NEW_MATERIAL"));
+        context.checking(new Expectations()
+            {
+                {
+                    one(daoFactory).getHomeDatabaseInstance();
+                    will(returnValue(CommonTestUtils
+                            .createDatabaseInstance(CommonTestUtils.HOME_DATABASE_INSTANCE_CODE)));
+                }
+            });
+        createMaterialTable().add(newMaterials, materialType);
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testSave() throws Exception
+    {
+        final MaterialTypePE materialType = CommonTestUtils.createMaterialType();
+        final ArrayList<MaterialPE> materials = new ArrayList<MaterialPE>();
+        materials.add(createMaterial(materialType, "BRAND_NEW_MATERIAL"));
+        context.checking(new Expectations()
+            {
+                {
+                    allowing(daoFactory).getMaterialDAO();
+                    will(returnValue(materialDAO));
+
+                    one(materialDAO).createMaterials(materials);
+                }
+            });
+        createMaterialTable(materials, true).save();
+        context.assertIsSatisfied();
+    }
+
+    private MaterialPE createMaterial(MaterialTypePE materialType, String code)
+    {
+        final MaterialPE material = new MaterialPE();
+        material.setCode(code);
+        material.setMaterialType(materialType);
+        material.setDatabaseInstance(CommonTestUtils
+                .createDatabaseInstance(CommonTestUtils.HOME_DATABASE_INSTANCE_CODE));
+        material.setRegistrator(EXAMPLE_SESSION.tryGetPerson());
+        return material;
+    }
+
+    private NewMaterial createNewMaterial(String code)
+    {
+        final NewMaterial material = new NewMaterial();
+        material.setCode(code);
+        return material;
+    }
 }
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/MaterialDAOTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/MaterialDAOTest.java
index 3a8f2492d2659db49697831ae3c5c6149ea14995..879db437a7bbb0b5012f8d69bed94e7f5a0f964a 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/MaterialDAOTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/MaterialDAOTest.java
@@ -16,11 +16,14 @@
 
 package ch.systemsx.cisd.openbis.generic.server.dataaccess.db;
 
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 
 import junit.framework.Assert;
 
+import org.springframework.dao.DataIntegrityViolationException;
 import org.testng.annotations.Test;
 
 import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialPE;
@@ -36,17 +39,83 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
     { "db", "material" })
 public final class MaterialDAOTest extends AbstractDAOTest
 {
+    private static final String BACTERIUM = "BACTERIUM";
+    private static final String BRAND_NEW_BACTERIUM = "BRAND_NEW_BACTERIUM";
+    final int NUMBER_OF_BACTERIA = 4;
 
     @Test
     public void testListMaterials() throws Exception
     {
         MaterialTypePE type =
                 (MaterialTypePE) daoFactory.getEntityTypeDAO(EntityKind.MATERIAL)
-                        .tryToFindEntityTypeByCode("BACTERIUM");
-        List<MaterialPE> list = daoFactory.getMaterialDAO().listMaterialsWithPropertiesAndInhibitor(type);
-        Assert.assertEquals(4, list.size());
+                        .tryToFindEntityTypeByCode(BACTERIUM);
+        List<MaterialPE> list =
+                daoFactory.getMaterialDAO().listMaterialsWithPropertiesAndInhibitor(type);
+        Assert.assertEquals(NUMBER_OF_BACTERIA, list.size());
         Collections.sort(list);
         Assert.assertEquals(list.get(0).getCode(), "BACTERIUM-X");
     }
 
+    @Test
+    public void testCreateMaterials() throws Exception
+    {
+        MaterialTypePE type =
+                (MaterialTypePE) daoFactory.getEntityTypeDAO(EntityKind.MATERIAL)
+                        .tryToFindEntityTypeByCode(BACTERIUM);
+        List<MaterialPE> bacteria_before =
+                daoFactory.getMaterialDAO().listMaterialsWithPropertiesAndInhibitor(type);
+        Assert.assertEquals(NUMBER_OF_BACTERIA, bacteria_before.size());
+        List<MaterialPE> newMaterials = new ArrayList<MaterialPE>();
+        newMaterials.add(createMaterial(type, "BRAND_NEW_BACTERIUM_1"));
+        newMaterials.add(createMaterial(type, "BRAND_NEW_BACTERIUM_2"));
+        Collections.sort(newMaterials);
+        daoFactory.getMaterialDAO().createMaterials(newMaterials);
+        List<MaterialPE> bacteria_after =
+                daoFactory.getMaterialDAO().listMaterialsWithPropertiesAndInhibitor(type);
+        Assert.assertEquals(NUMBER_OF_BACTERIA + newMaterials.size(), bacteria_after.size());
+        bacteria_after.removeAll(bacteria_before);
+        Collections.sort(bacteria_after);
+        for (int i = 0; i < newMaterials.size(); i++)
+        {
+            Assert.assertEquals(newMaterials.get(i), bacteria_after.get(i));
+        }
+    }
+
+    @Test(expectedExceptions = DataIntegrityViolationException.class)
+    public void testFailCreateMaterialsWithTheSameCode() throws Exception
+    {
+        MaterialTypePE type =
+                (MaterialTypePE) daoFactory.getEntityTypeDAO(EntityKind.MATERIAL)
+                        .tryToFindEntityTypeByCode(BACTERIUM);
+        List<MaterialPE> newMaterials = new ArrayList<MaterialPE>();
+        newMaterials.add(createMaterial(type, BRAND_NEW_BACTERIUM));
+        newMaterials.add(createMaterial(type, BRAND_NEW_BACTERIUM));
+        daoFactory.getMaterialDAO().createMaterials(newMaterials);
+    }
+
+    @Test(expectedExceptions = DataIntegrityViolationException.class)
+    public void testFailCreateMaterialsWithExistingCode() throws Exception
+    {
+        MaterialTypePE type =
+                (MaterialTypePE) daoFactory.getEntityTypeDAO(EntityKind.MATERIAL)
+                        .tryToFindEntityTypeByCode(BACTERIUM);
+        List<MaterialPE> bacteria_before =
+                daoFactory.getMaterialDAO().listMaterialsWithPropertiesAndInhibitor(type);
+        String existingBacteriumCode = bacteria_before.get(0).getCode();
+        List<MaterialPE> newMaterials = new ArrayList<MaterialPE>();
+        newMaterials.add(createMaterial(type, existingBacteriumCode));
+        daoFactory.getMaterialDAO().createMaterials(newMaterials);
+    }
+
+    private MaterialPE createMaterial(MaterialTypePE type, String code)
+    {
+        final MaterialPE material = new MaterialPE();
+        material.setCode(code);
+        material.setMaterialType(type);
+        material.setRegistrationDate(new Date());
+        material.setRegistrator(getSystemPerson());
+        material.setDatabaseInstance(daoFactory.getHomeDatabaseInstance());
+        return material;
+    }
+
 }
\ No newline at end of file
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 602ec8184683e8ae9381e7b85d8f23163b9621b3..be472bcf8a1f869fe811fa51a2e4275bbd204708 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
@@ -29,10 +29,12 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.shared.AbstractServerTestCase;
 import ch.systemsx.cisd.openbis.generic.shared.CommonTestUtils;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewExperiment;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewMaterial;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleType;
 import ch.systemsx.cisd.openbis.generic.shared.dto.AttachmentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ProcedurePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SampleGenerationDTO;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
@@ -40,6 +42,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SampleTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifier;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifierFactory;
+import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
 import ch.systemsx.cisd.openbis.plugin.ISampleTypeSlaveServerPlugin;
 import ch.systemsx.cisd.openbis.plugin.generic.shared.IGenericServer;
 
@@ -68,6 +71,13 @@ public final class GenericServerTest extends AbstractServerTestCase
         return newSample;
     }
 
+    private final NewMaterial createNewMaterial(final String code)
+    {
+        final NewMaterial material = new NewMaterial();
+        material.setCode(code);
+        return material;
+    }
+
     //
     // AbstractServerTestCase
     //
@@ -179,9 +189,8 @@ public final class GenericServerTest extends AbstractServerTestCase
 
                 }
             });
-        assertEquals(attachmentPE, createServer().getExperimentFileAttachment(
-                SESSION_TOKEN, experimentIdentifier, attachmentPE.getFileName(),
-                attachmentPE.getVersion()));
+        assertEquals(attachmentPE, createServer().getExperimentFileAttachment(SESSION_TOKEN,
+                experimentIdentifier, attachmentPE.getFileName(), attachmentPE.getVersion()));
         context.assertIsSatisfied();
     }
 
@@ -378,4 +387,33 @@ public final class GenericServerTest extends AbstractServerTestCase
         context.assertIsSatisfied();
     }
 
+    @Test
+    public final void testRegisterMaterials()
+    {
+        prepareGetSession();
+        final MaterialTypePE materialTypePE = CommonTestUtils.createMaterialType();
+        final List<NewMaterial> newMaterials = new ArrayList<NewMaterial>();
+        newMaterials.add(createNewMaterial("one"));
+        newMaterials.add(createNewMaterial("two"));
+        final String typeCode = materialTypePE.getCode();
+        context.checking(new Expectations()
+            {
+                {
+                    one(daoFactory).getEntityTypeDAO(EntityKind.MATERIAL);
+                    will(returnValue(entityTypeDAO));
+
+                    one(entityTypeDAO).tryToFindEntityTypeByCode(typeCode);
+                    will(returnValue(materialTypePE));
+
+                    one(genericBusinessObjectFactory).createMaterialTable(SESSION);
+                    will(returnValue(materialTable));
+
+                    one(materialTable).add(newMaterials, materialTypePE);
+                    one(materialTable).save();
+                }
+            });
+        createServer().registerMaterials(SESSION_TOKEN, typeCode, newMaterials);
+        context.assertIsSatisfied();
+    }
+
 }