diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DeletedExperimentPE.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DeletedExperimentPE.java
index 5e29a151258c0b57c0f0d2ff78e822585ee0ac8d..a6ac163bc02b457bf11e3f1b59d5759a5ef48822 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DeletedExperimentPE.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DeletedExperimentPE.java
@@ -134,7 +134,7 @@ public class DeletedExperimentPE extends AbstractDeletedEntityPE
     }
 
     @Private
-    void setProjectInternal(final ProjectPE project)
+    public void setProjectInternal(final ProjectPE project)
     {
         this.project = project;
     }
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBOTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBOTest.java
index d126a2870df407ffd882e2f4741e8e42b390728c..8d5fbee324383957eddd83abd68b83bb20af8124 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBOTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBOTest.java
@@ -204,6 +204,8 @@ public abstract class AbstractBOTest extends AssertJUnit
                     will(returnValue(deletionDAO));
                     allowing(daoFactory).getCorePluginDAO();
                     will(returnValue(corePluginDAO));
+                    allowing(daoFactory).getProjectDAO();
+                    will(returnValue(projectDAO));
                 }
             });
     }
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ProjectBOTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ProjectBOTest.java
index 551e5002591284ef59d710ceada48c88e7c38d09..3847ec4c15590ee4e22389c5245df4de32e6ad5c 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ProjectBOTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ProjectBOTest.java
@@ -16,28 +16,194 @@
 
 package ch.systemsx.cisd.openbis.generic.server.business.bo;
 
+import java.util.Arrays;
+import java.util.List;
+
 import org.jmock.Expectations;
 import org.testng.AssertJUnit;
 import org.testng.annotations.Test;
 
+import ch.rinn.restrictions.Friend;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.common.test.RecordingMatcher;
 import ch.systemsx.cisd.openbis.generic.server.business.ManagerTestTool;
+import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DeletedExperimentPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DeletionPE;
+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.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ProjectPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ProjectIdentifier;
+import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
 
 /**
  * Test cases for corresponding {@link ProjectBO} class.
  * 
  * @author Christian Ribeaud
  */
+@Friend(toClasses = DeletedExperimentPE.class)
 public final class ProjectBOTest extends AbstractBOTest
 {
 
+    private static final ProjectPE EXAMPLE_PROJECT = ManagerTestTool.EXAMPLE_PROJECT;
+
+    private static final TechId EXAMPLE_PROJECT_ID = new TechId(EXAMPLE_PROJECT.getId());
+
     private final ProjectBO createProjectBO()
     {
         return new ProjectBO(daoFactory, ManagerTestTool.EXAMPLE_SESSION);
     }
 
+    @Test
+    public void testDeleteProjectWithNoExperiments()
+    {
+        final RecordingMatcher<EventPE> eventRecorder = new RecordingMatcher<EventPE>();
+        prepareGetProject();
+        prepareGetUndeletedExperiments();
+        prepareGetTrashedExperiments();
+        context.checking(new Expectations()
+            {
+                {
+                    one(projectDAO).delete(EXAMPLE_PROJECT);
+
+                    one(eventDAO).persist(with(eventRecorder));
+                }
+            });
+
+        ProjectBO projectBO = createProjectBO();
+        projectBO.deleteByTechId(EXAMPLE_PROJECT_ID, "my reason");
+
+        assertEquals(EventType.DELETION, eventRecorder.recordedObject().getEventType());
+        assertEquals(EntityType.PROJECT, eventRecorder.recordedObject().getEntityType());
+        assertEquals("my reason", eventRecorder.recordedObject().getReason());
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testDeleteProjectWithOnlyUndeletedExperiments()
+    {
+        prepareGetProject();
+        ExperimentPE e1 = new ExperimentPE();
+        e1.setCode("E1");
+        ExperimentPE e2 = new ExperimentPE();
+        e2.setCode("E2");
+        prepareGetUndeletedExperiments(e1, e2);
+        prepareGetTrashedExperiments();
+
+        try
+        {
+            createProjectBO().deleteByTechId(EXAMPLE_PROJECT_ID, "my reason");
+            fail("UserFailureException expected");
+        } catch (UserFailureException ex)
+        {
+            assertEquals("Project 'MY_GREAT_PROJECT' can not be deleted because "
+                    + "the following experiments still exist: [E1, E2]", ex.getMessage());
+        }
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testDeleteProjectWithOnlyTrashedExperiments()
+    {
+        prepareGetProject();
+        prepareGetUndeletedExperiments();
+        DeletedExperimentPE e3 = new DeletedExperimentPE();
+        e3.setCode("E3");
+        e3.setProjectInternal(EXAMPLE_PROJECT);
+        DeletedExperimentPE e4 = new DeletedExperimentPE();
+        e4.setCode("E4");
+        e4.setProjectInternal(new ProjectPE());
+        prepareGetTrashedExperiments(e3, e4);
+
+        try
+        {
+            createProjectBO().deleteByTechId(EXAMPLE_PROJECT_ID, "my reason");
+            fail("UserFailureException expected");
+        } catch (UserFailureException ex)
+        {
+            assertEquals("Project 'MY_GREAT_PROJECT' can not be deleted because "
+                    + "the following experiments are in the trash can: [E3]", ex.getMessage());
+        }
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testDeleteProjectWithUndeletedAndTrashedExperiments()
+    {
+        prepareGetProject();
+        ExperimentPE e1 = new ExperimentPE();
+        e1.setCode("E1");
+        ExperimentPE e2 = new ExperimentPE();
+        e2.setCode("E2");
+        prepareGetUndeletedExperiments(e1, e2);
+        DeletedExperimentPE e3 = new DeletedExperimentPE();
+        e3.setCode("E3");
+        e3.setProjectInternal(EXAMPLE_PROJECT);
+        DeletedExperimentPE e4 = new DeletedExperimentPE();
+        e4.setCode("E4");
+        e4.setProjectInternal(new ProjectPE());
+        prepareGetTrashedExperiments(e3, e4);
+
+        try
+        {
+            createProjectBO().deleteByTechId(EXAMPLE_PROJECT_ID, "my reason");
+            fail("UserFailureException expected");
+        } catch (UserFailureException ex)
+        {
+            assertEquals("Project 'MY_GREAT_PROJECT' can not be deleted because "
+                    + "the following experiments still exist: [E1, E2]\n"
+                    + "In addition the following experiments are in the trash can: [E3]",
+                    ex.getMessage());
+        }
+        context.assertIsSatisfied();
+    }
+
+    private void prepareGetProject()
+    {
+        context.checking(new Expectations()
+            {
+                {
+                    one(projectDAO).getByTechId(EXAMPLE_PROJECT_ID);
+                    will(returnValue(EXAMPLE_PROJECT));
+
+                }
+            });
+    }
+
+    private void prepareGetUndeletedExperiments(final ExperimentPE... experiments)
+    {
+        context.checking(new Expectations()
+            {
+                {
+                    one(experimentDAO).listExperimentsWithProperties(EXAMPLE_PROJECT, false, false);
+                    will(returnValue(Arrays.asList(experiments)));
+                }
+            });
+    }
+
+    private void prepareGetTrashedExperiments(final DeletedExperimentPE... experiments)
+    {
+        context.checking(new Expectations()
+            {
+                {
+                    one(deletionDAO).listAllEntities();
+                    DeletionPE deletionPE = new DeletionPE();
+                    deletionPE.setId(4711L);
+                    will(returnValue(Arrays.asList(deletionPE)));
+
+                    one(deletionDAO).findTrashedExperimentIds(
+                            Arrays.<TechId> asList(new TechId(deletionPE.getId())));
+                    List<TechId> ids = Arrays.asList(new TechId(42));
+                    will(returnValue(ids));
+
+                    one(deletionDAO).listDeletedEntities(EntityKind.EXPERIMENT, ids);
+                    will(returnValue(Arrays.asList(experiments)));
+                }
+            });
+    }
+
     @Test
     public final void testSaveWithNullProject()
     {
@@ -90,11 +256,9 @@ public final class ProjectBOTest extends AbstractBOTest
         context.checking(new Expectations()
             {
                 {
-                    ManagerTestTool.prepareFindGroup(this, ManagerTestTool.EXAMPLE_SESSION
-                            .tryGetHomeGroupCode(), daoFactory, spaceDAO);
-
-                    one(daoFactory).getProjectDAO();
-                    will(returnValue(projectDAO));
+                    ManagerTestTool.prepareFindGroup(this,
+                            ManagerTestTool.EXAMPLE_SESSION.tryGetHomeGroupCode(), daoFactory,
+                            spaceDAO);
                 }
             });
     }
@@ -122,15 +286,17 @@ public final class ProjectBOTest extends AbstractBOTest
             fail("An UserFailureException must be thrown here.");
         } catch (final UserFailureException ex)
         {
-            assertEquals(String.format(DataAccessExceptionTranslator.UNIQUE_VIOLATION_FORMAT,
-                    String.format("Project '%s'", projIdent.getProjectCode())), ex.getMessage());
+            assertEquals(
+                    String.format(DataAccessExceptionTranslator.UNIQUE_VIOLATION_FORMAT,
+                            String.format("Project '%s'", projIdent.getProjectCode())),
+                    ex.getMessage());
         }
         context.assertIsSatisfied();
     }
 
     private ProjectIdentifier createProjectIdent()
     {
-        final ProjectPE projectDTO = ManagerTestTool.EXAMPLE_PROJECT;
+        final ProjectPE projectDTO = EXAMPLE_PROJECT;
         return new ProjectIdentifier(projectDTO.getSpace().getCode(), projectDTO.getCode());
     }
 }