diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/operation/store/OperationExecutionDBStoreDAO.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/operation/store/OperationExecutionDBStoreDAO.java
index bab7d5355a0bb58a9a56596ec616cac552493d9e..0bc1c87840016c6930650bbc1fb2752386500050 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/operation/store/OperationExecutionDBStoreDAO.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/operation/store/OperationExecutionDBStoreDAO.java
@@ -64,7 +64,8 @@ public class OperationExecutionDBStoreDAO implements IOperationExecutionDBStoreD
                         "update OperationExecutionPE set summaryProgress = :progress where code = :code and state in (:states) and summaryAvailability = 'AVAILABLE'");
         query.setParameter("code", code);
         query.setParameter("progress", progress);
-        query.setParameterList("states", Arrays.asList(OperationExecutionState.RUNNING, OperationExecutionState.FAILED));
+        query.setParameterList("states",
+                Arrays.asList(OperationExecutionState.RUNNING, OperationExecutionState.FAILED, OperationExecutionState.FINISHED));
         query.executeUpdate();
     }
 
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/operation/AsynchronousOperationExecutorTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/operation/AsynchronousOperationExecutorTest.java
index ae7731230114759d5acd4ed7ecb04e211ad3675e..9df0cad2b517dca46f45bd4a3e4152d50edf57d5 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/operation/AsynchronousOperationExecutorTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/operation/AsynchronousOperationExecutorTest.java
@@ -38,7 +38,7 @@ public class AsynchronousOperationExecutorTest
 
     private IOperationExecutionStore executionStore;
 
-    private IOperationsExecutor operationsExecutor;
+    private IAsynchronousOperationThreadPoolExecutor poolExecutor;
 
     private IOperationContext context1;
 
@@ -71,7 +71,7 @@ public class AsynchronousOperationExecutorTest
         executionConfig = mockery.mock(IOperationExecutionConfig.class);
         executionIdFactory = mockery.mock(IOperationExecutionIdFactory.class);
         executionStore = mockery.mock(IOperationExecutionStore.class);
-        operationsExecutor = mockery.mock(IOperationsExecutor.class);
+        poolExecutor = mockery.mock(IAsynchronousOperationThreadPoolExecutor.class);
 
         context1 = mockery.mock(IOperationContext.class, "context1");
         context2 = mockery.mock(IOperationContext.class, "context2");
@@ -118,7 +118,7 @@ public class AsynchronousOperationExecutorTest
                     oneOf(executionStore).executionScheduled(context1, executionId);
                     oneOf(executionStore).executionRunning(context1, executionId);
 
-                    oneOf(operationsExecutor).execute(context1, operations1);
+                    oneOf(poolExecutor).execute(context1, executionId, operations1);
                     will(returnValue(operationResults1));
 
                     oneOf(executionStore).executionFinished(context1, executionId, operationResults1);
@@ -150,7 +150,7 @@ public class AsynchronousOperationExecutorTest
                     oneOf(executionStore).executionScheduled(context1, executionId1);
                     oneOf(executionStore).executionRunning(context1, executionId1);
 
-                    oneOf(operationsExecutor).execute(context1, operations1);
+                    oneOf(poolExecutor).execute(context1, executionId1, operations1);
                     will(throwException(exception));
 
                     oneOf(executionStore).executionFailed(context1, executionId1, new OperationExecutionError(exception));
@@ -265,7 +265,7 @@ public class AsynchronousOperationExecutorTest
                     oneOf(executionStore).executionScheduled(context1, executionId1);
                     oneOf(executionStore).executionRunning(context1, executionId1);
 
-                    oneOf(operationsExecutor).execute(context1, operations1);
+                    oneOf(poolExecutor).execute(context1, executionId1, operations1);
                     will(returnValue(operationResults1));
 
                     oneOf(executionStore).executionFinished(context1, executionId1, operationResults1);
@@ -333,7 +333,7 @@ public class AsynchronousOperationExecutorTest
                     oneOf(executionStore).executionRunning(context1, executionId1);
                     will(new SendChannelMessageAction(executionChannel, "1_running"));
 
-                    oneOf(operationsExecutor).execute(context1, operations1);
+                    oneOf(poolExecutor).execute(context1, executionId1, operations1);
                     will(new TestAction()
                         {
                             @Override
@@ -366,7 +366,7 @@ public class AsynchronousOperationExecutorTest
                     oneOf(executionStore).executionRunning(context2, executionId2);
                     will(new SendChannelMessageAction(executionChannel, "2_running"));
 
-                    oneOf(operationsExecutor).execute(context2, operations2);
+                    oneOf(poolExecutor).execute(context2, executionId2, operations2);
                     will(new TestAction()
                         {
                             @Override
@@ -439,7 +439,7 @@ public class AsynchronousOperationExecutorTest
                     oneOf(executionStore).executionRunning(context1, executionId1);
                     will(new SendChannelMessageAction(executionChannel, "1_running"));
 
-                    oneOf(operationsExecutor).execute(context1, operations1);
+                    oneOf(poolExecutor).execute(context1, executionId1, operations1);
                     will(new TestAction()
                         {
                             @Override
@@ -472,7 +472,7 @@ public class AsynchronousOperationExecutorTest
                     oneOf(executionStore).executionRunning(context2, executionId2);
                     will(new SendChannelMessageAction(executionChannel, "2_running"));
 
-                    oneOf(operationsExecutor).execute(context2, operations2);
+                    oneOf(poolExecutor).execute(context2, executionId2, operations2);
                     will(new TestAction()
                         {
                             @Override
@@ -537,7 +537,6 @@ public class AsynchronousOperationExecutorTest
 
     private AsynchronousOperationExecutor createExecutor()
     {
-        AsynchronousOperationThreadPoolExecutor poolExecutor = new AsynchronousOperationThreadPoolExecutor(operationsExecutor);
         AsynchronousOperationExecutor executor = new AsynchronousOperationExecutor(executionConfig, executionIdFactory, executionStore, poolExecutor);
         executors.add(executor);
         return executor;
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetOperationExecutionTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetOperationExecutionTest.java
index a917c7a38c96274cdd5ba80d6323c68358738031..c1b5cf07516bcbd3ab9fbce3d188be940244ec3e 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetOperationExecutionTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetOperationExecutionTest.java
@@ -33,6 +33,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.operation.IOperationExecu
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.operation.IOperationResult;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.operation.OperationExecution;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.operation.OperationExecutionDetails;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.operation.OperationExecutionEmailNotification;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.operation.OperationExecutionState;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.operation.OperationExecutionSummary;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.operation.SynchronousOperationExecutionOptions;
@@ -187,7 +188,7 @@ public class GetOperationExecutionTest extends AbstractOperationExecutionTest
     }
 
     @Test
-    public void testGetWithSummary()
+    public void testGetWithSummary() throws Exception
     {
         String sessionToken = v3api.login(TEST_USER, PASSWORD);
 
@@ -196,31 +197,34 @@ public class GetOperationExecutionTest extends AbstractOperationExecutionTest
         List<? extends IOperation> operations = Arrays.asList(new CreateSpacesOperation(creation));
 
         SynchronousOperationExecutionOptions options = new SynchronousOperationExecutionOptions();
+        options.setNotification(new OperationExecutionEmailNotification("test1@email.com", "test2@email.com"));
         options.setExecutionId(new OperationExecutionPermId());
 
         v3api.executeOperations(sessionToken, operations, options);
 
         OperationExecutionFetchOptions fo = new OperationExecutionFetchOptions();
+        fo.withNotification();
         fo.withSummary();
         fo.withSummary().withOperations();
         fo.withSummary().withProgress();
         fo.withSummary().withError();
         fo.withSummary().withResults();
 
+        // wait to make sure the progress information gets updated
+        Thread.sleep(config.getProgressInterval() * 1000);
+
         OperationExecution execution = getExecution(sessionToken, options.getExecutionId(), fo);
 
         assertNotNull(execution);
 
+        assertEquals(((OperationExecutionEmailNotification) execution.getNotification()).getEmails(),
+                ((OperationExecutionEmailNotification) options.getNotification()).getEmails());
+
         OperationExecutionSummary summary = execution.getSummary();
 
         assertEquals(summary.getOperations().size(), 1);
         assertEquals(summary.getOperations().get(0), "CreateSpacesOperation 1 creation(s)");
-
-        // reported by a separate thread
-        if (summary.getProgress() != null)
-        {
-            AssertionUtil.assertContains("checking access (1/1)", summary.getProgress());
-        }
+        AssertionUtil.assertContains("checking access (1/1)", summary.getProgress());
         assertEquals(summary.getError(), null);
 
         assertEquals(summary.getResults().size(), 1);
@@ -243,6 +247,7 @@ public class GetOperationExecutionTest extends AbstractOperationExecutionTest
         List<? extends IOperation> operationsBefore = Arrays.asList(operationBefore);
 
         SynchronousOperationExecutionOptions options = new SynchronousOperationExecutionOptions();
+        options.setNotification(new OperationExecutionEmailNotification("test1@email.com", "test2@email.com"));
         options.setExecutionId(new OperationExecutionPermId());
 
         // executeOperations
@@ -254,6 +259,7 @@ public class GetOperationExecutionTest extends AbstractOperationExecutionTest
         // getExecution
 
         OperationExecutionFetchOptions fo = new OperationExecutionFetchOptions();
+        fo.withNotification();
         fo.withDetails();
         fo.withDetails().withOperations();
         fo.withDetails().withProgress();
@@ -261,13 +267,15 @@ public class GetOperationExecutionTest extends AbstractOperationExecutionTest
         fo.withDetails().withResults();
 
         // wait to make sure the progress information gets updated
-
         Thread.sleep(config.getProgressInterval() * 1000);
 
         OperationExecution execution = getExecution(sessionToken, options.getExecutionId(), fo);
 
         assertNotNull(execution);
 
+        assertEquals(((OperationExecutionEmailNotification) execution.getNotification()).getEmails(),
+                ((OperationExecutionEmailNotification) options.getNotification()).getEmails());
+
         OperationExecutionDetails details = execution.getDetails();
 
         // check operations
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/FetchOptionsToStringBuilder.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/FetchOptionsToStringBuilder.java
index 4fcca361fa23632b626c02c93b05e1146762c8ce..f3322af6623a1c31a2234dff205308e1f3afc819 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/FetchOptionsToStringBuilder.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/FetchOptionsToStringBuilder.java
@@ -16,6 +16,7 @@
 
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions;
 
+import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -24,8 +25,10 @@ import java.util.Set;
 /**
  * @author jakubs
  */
-public class FetchOptionsToStringBuilder
+public class FetchOptionsToStringBuilder implements Serializable
 {
+    private static final long serialVersionUID = 1L;
+
     private String name;
 
     private FetchOptions<?> mainFetchOptions;
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/search/SearchCriteriaToStringBuilder.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/search/SearchCriteriaToStringBuilder.java
index 7901c32b3d7b32b03c0e7461400fc5dac4dc5af1..62bdb0393f81d052c169b0a2b3a07a22012205db 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/search/SearchCriteriaToStringBuilder.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/search/SearchCriteriaToStringBuilder.java
@@ -16,14 +16,17 @@
 
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search;
 
+import java.io.Serializable;
 import java.util.Collection;
 
 /**
  * @author pkupczyk
  */
-public class SearchCriteriaToStringBuilder
+public class SearchCriteriaToStringBuilder implements Serializable
 {
 
+    private static final long serialVersionUID = 1L;
+
     private String name;
 
     private SearchOperator operator;
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/deletion/DeletedObject.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/deletion/DeletedObject.java
index 33f5c204a87cdb8417c129a887dd89de79657f9e..3d1f0ccfb439335a511077705892f6590dee2b43 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/deletion/DeletedObject.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/deletion/DeletedObject.java
@@ -16,6 +16,8 @@
 
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.deletion;
 
+import java.io.Serializable;
+
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.IObjectId;
 import ch.systemsx.cisd.base.annotation.JsonObject;
 
@@ -23,9 +25,11 @@ import ch.systemsx.cisd.base.annotation.JsonObject;
  * @author pkupczyk
  */
 @JsonObject("as.dto.deletion.DeletedObject")
-public class DeletedObject
+public class DeletedObject implements Serializable
 {
 
+    private static final long serialVersionUID = 1L;
+
     private IObjectId id;
 
     public IObjectId getId()
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/plugin/service/context/CustomASServiceContext.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/plugin/service/context/CustomASServiceContext.java
index 59a97d5f22cc82b0c3ad5b32ec963fa33eb115df..2dee0183c31b701d25d78f58b91dbcd8d1f808db 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/plugin/service/context/CustomASServiceContext.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/plugin/service/context/CustomASServiceContext.java
@@ -22,4 +22,6 @@ package ch.ethz.sis.openbis.generic.asapi.v3.plugin.service.context;
 public class CustomASServiceContext extends ServiceContext
 {
 
+    private static final long serialVersionUID = 1L;
+
 }
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/plugin/service/context/ServiceContext.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/plugin/service/context/ServiceContext.java
index f1a60ff804f22ad754efbc391e9f865bbde1d5f8..fda65edee1767149fd0c88b027954327709923b8 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/plugin/service/context/ServiceContext.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/plugin/service/context/ServiceContext.java
@@ -16,11 +16,15 @@
 
 package ch.ethz.sis.openbis.generic.asapi.v3.plugin.service.context;
 
+import java.io.Serializable;
+
 /**
  * @author Franz-Josef Elmer
  */
-public class ServiceContext
+public class ServiceContext implements Serializable
 {
+    private static final long serialVersionUID = 1L;
+
     private String sessionToken;
 
     public String getSessionToken()
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/dssapi/v3/dto/datasetfile/download/DataSetFileDownload.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/dssapi/v3/dto/datasetfile/download/DataSetFileDownload.java
index c6176cd025e0f77bd19e172b6ef005eada48b502..fabc432efa33900eb851c29c2a21c0e4b7146f05 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/dssapi/v3/dto/datasetfile/download/DataSetFileDownload.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/dssapi/v3/dto/datasetfile/download/DataSetFileDownload.java
@@ -17,15 +17,18 @@
 package ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.download;
 
 import java.io.InputStream;
+import java.io.Serializable;
 
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.DataSetFile;
 
 /**
  * @author pkupczyk
  */
-public class DataSetFileDownload
+public class DataSetFileDownload implements Serializable
 {
 
+    private static final long serialVersionUID = 1L;
+
     private DataSetFile dataSetFile;
 
     private InputStream inputStream;
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/dssapi/v3/dto/datasetfile/download/DataSetFileDownloadReader.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/dssapi/v3/dto/datasetfile/download/DataSetFileDownloadReader.java
index 04c45e74e4da10a385e4d1b73db786689460a1ee..ef26df691c37ebd14d43144a2915f91175d8596d 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/dssapi/v3/dto/datasetfile/download/DataSetFileDownloadReader.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/dssapi/v3/dto/datasetfile/download/DataSetFileDownloadReader.java
@@ -22,15 +22,18 @@ import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.ObjectInputStream;
+import java.io.Serializable;
 
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.DataSetFile;
 
 /**
  * @author pkupczyk
  */
-public class DataSetFileDownloadReader
+public class DataSetFileDownloadReader implements Serializable
 {
 
+    private static final long serialVersionUID = 1L;
+
     private InputStream in;
 
     private DataSetFileDownload lastDownload;
diff --git a/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/EnglishCheck.java b/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/EnglishCheck.java
index 2490bf1f69347f3a4d75fc726631b5367e2821d8..a105934f52bcd96acb5e07eeb65d136fe72d3ed5 100644
--- a/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/EnglishCheck.java
+++ b/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/EnglishCheck.java
@@ -16,6 +16,7 @@
 
 package ch.ethz.sis.openbis.generic.sharedapi.v3;
 
+import java.io.Serializable;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -24,6 +25,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -101,13 +103,25 @@ public class EnglishCheck
             });
         Collection<String> uniqueClassNames = new TreeSet<String>(nonInnerClassesAndTestClasses);
         Collection<Class<?>> uniqueClasses = ImmutableSet.copyOf(ReflectionUtils.forNames(uniqueClassNames));
+        Set<Class<?>> nonSerializableConcreteClasses = new HashSet<Class<?>>();
 
         for (Class<?> uniqueClass : uniqueClasses)
         {
             System.out.println("Found V3 public class:\t" + uniqueClass.getName());
+
+            if (false == Modifier.isAbstract(uniqueClass.getModifiers()) && false == Serializable.class.isAssignableFrom(uniqueClass))
+            {
+                nonSerializableConcreteClasses.add(uniqueClass);
+            }
         }
+
         System.out.println();
 
+        if (false == nonSerializableConcreteClasses.isEmpty())
+        {
+            Assert.fail("Non serializable classes found:\n" + StringUtils.join(nonSerializableConcreteClasses, ",\n"));
+        }
+
         return uniqueClasses;
     }