You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2018/12/07 16:35:52 UTC

[syncope] branch 2_0_X updated: [SYNCOPE-1409] Avoid double read either before and afterwards

This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch 2_0_X
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/2_0_X by this push:
     new 3145d6e  [SYNCOPE-1409] Avoid double read either before and afterwards
3145d6e is described below

commit 3145d6e1ab4d7f370e6a9cf5b919e894ece65308
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Fri Dec 7 16:50:55 2018 +0100

    [SYNCOPE-1409] Avoid double read either before and afterwards
---
 .../org/apache/syncope/core/logic/RealmLogic.java  | 14 ++--
 .../org/apache/syncope/core/logic/TaskLogic.java   | 16 ++++-
 .../syncope/core/logic/UserWorkflowLogic.java      | 10 +--
 .../api/propagation/PropagationManager.java        | 15 ++--
 .../api/propagation/PropagationReporter.java       |  9 ++-
 .../api/propagation/PropagationTaskCallable.java   |  3 +-
 .../api/propagation/PropagationTaskExecutor.java   | 12 ++--
 ...nTaskCallable.java => PropagationTaskInfo.java} | 27 ++++++--
 .../java/DefaultAnyObjectProvisioningManager.java  | 22 +++---
 .../java/DefaultGroupProvisioningManager.java      | 27 ++++----
 .../java/DefaultUserProvisioningManager.java       | 38 +++++-----
 .../AbstractPropagationTaskExecutor.java           | 65 +++++++++--------
 .../propagation/DefaultPropagationReporter.java    | 21 +++---
 .../DefaultPropagationTaskCallable.java            | 14 ++--
 .../PriorityPropagationTaskExecutor.java           | 45 ++++++------
 .../java/propagation/PropagationManagerImpl.java   | 36 +++++-----
 .../java/pushpull/AbstractPushResultHandler.java   | 81 ++++++++++++++--------
 .../pushpull/DefaultRealmPullResultHandler.java    | 14 ++--
 .../pushpull/DefaultRealmPushResultHandler.java    | 66 +++++++++++-------
 .../producer/ConfirmPasswordResetProducer.java     |  7 +-
 .../camel/producer/CreateProducer.java             | 22 +++---
 .../camel/producer/DeleteProducer.java             | 24 +++----
 .../camel/producer/DeprovisionProducer.java        | 18 +++--
 .../camel/producer/ProvisionProducer.java          | 18 +++--
 .../camel/producer/StatusProducer.java             |  9 ++-
 .../camel/producer/SuspendProducer.java            |  7 +-
 .../camel/producer/UpdateProducer.java             | 23 +++---
 27 files changed, 363 insertions(+), 300 deletions(-)

diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java
index dcb5494..092d44d 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java
@@ -28,7 +28,6 @@ import org.apache.commons.collections4.Transformer;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.to.RealmTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
@@ -48,6 +47,7 @@ import org.apache.syncope.core.provisioning.api.data.RealmDataBinder;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -128,8 +128,8 @@ public class RealmLogic extends AbstractTransactionalLogic<RealmTO> {
 
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.addAll(ResourceOperation.CREATE, realm.getResourceKeys());
-        List<PropagationTaskTO> tasks = propagationManager.createTasks(realm, propByRes, null);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, false);
+        List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, false);
 
         ProvisioningResult<RealmTO> result = new ProvisioningResult<>();
         result.setEntity(binder.getRealmTO(realm, true));
@@ -150,8 +150,8 @@ public class RealmLogic extends AbstractTransactionalLogic<RealmTO> {
         PropagationByResource propByRes = binder.update(realm, realmTO);
         realm = realmDAO.save(realm);
 
-        List<PropagationTaskTO> tasks = propagationManager.createTasks(realm, propByRes, null);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, false);
+        List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, false);
 
         ProvisioningResult<RealmTO> result = new ProvisioningResult<>();
         result.setEntity(binder.getRealmTO(realm, true));
@@ -191,8 +191,8 @@ public class RealmLogic extends AbstractTransactionalLogic<RealmTO> {
 
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.addAll(ResourceOperation.DELETE, realm.getResourceKeys());
-        List<PropagationTaskTO> tasks = propagationManager.createTasks(realm, propByRes, null);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, false);
+        List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, false);
 
         ProvisioningResult<RealmTO> result = new ProvisioningResult<>();
         result.setEntity(binder.getRealmTO(realm, true));
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
index 6d2edbf..8e0bda9 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
@@ -58,6 +58,7 @@ import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
 import org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.provisioning.java.job.TaskJob;
 import org.quartz.JobDataMap;
 import org.quartz.JobKey;
@@ -234,7 +235,20 @@ public class TaskLogic extends AbstractExecutableLogic<TaskTO> {
         ExecTO result = null;
         switch (taskUtil.getType()) {
             case PROPAGATION:
-                TaskExec propExec = taskExecutor.execute(binder.<PropagationTaskTO>getTaskTO(task, taskUtil, false));
+                PropagationTaskTO taskTO = binder.<PropagationTaskTO>getTaskTO(task, taskUtil, false);
+                PropagationTaskInfo taskInfo = new PropagationTaskInfo();
+                taskInfo.setKey(taskTO.getKey());
+                taskInfo.setOperation(taskTO.getOperation());
+                taskInfo.setConnObjectKey(taskTO.getConnObjectKey());
+                taskInfo.setOldConnObjectKey(taskTO.getOldConnObjectKey());
+                taskInfo.setAttributes(taskTO.getAttributes());
+                taskInfo.setResource(taskTO.getResource());
+                taskInfo.setObjectClassName(taskTO.getObjectClassName());
+                taskInfo.setAnyTypeKind(taskTO.getAnyTypeKind());
+                taskInfo.setAnyType(taskTO.getAnyType());
+                taskInfo.setEntityKey(taskTO.getEntityKey());
+
+                TaskExec propExec = taskExecutor.execute(taskInfo);
                 result = binder.getExecTO(propExec);
                 break;
 
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/UserWorkflowLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/UserWorkflowLogic.java
index 4fb0c64..3c33887 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/UserWorkflowLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/UserWorkflowLogic.java
@@ -23,7 +23,6 @@ import java.util.List;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.AnyPatch;
 import org.apache.syncope.common.lib.patch.UserPatch;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.to.WorkflowFormTO;
 import org.apache.syncope.common.lib.to.WorkflowTaskTO;
@@ -36,6 +35,7 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecu
 import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Component;
@@ -76,12 +76,12 @@ public class UserWorkflowLogic extends AbstractTransactionalLogic<WorkflowFormTO
         UserPatch userPatch = new UserPatch();
         userPatch.setKey(userTO.getKey());
 
-        List<PropagationTaskTO> tasks = propagationManager.getUserUpdateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(
                 new WorkflowResult<>(
                         Pair.<UserPatch, Boolean>of(userPatch, null),
                         updated.getPropByRes(), updated.getPerformedTasks()));
 
-        taskExecutor.execute(tasks, false);
+        taskExecutor.execute(taskInfos, false);
 
         return binder.getUserTO(updated.getResult());
     }
@@ -120,13 +120,13 @@ public class UserWorkflowLogic extends AbstractTransactionalLogic<WorkflowFormTO
         if (updated.getResult() instanceof UserPatch
                 && updated.getPropByRes() != null && !updated.getPropByRes().isEmpty()) {
 
-            List<PropagationTaskTO> tasks = propagationManager.getUserUpdateTasks(
+            List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(
                     new WorkflowResult<>(
                             Pair.of((UserPatch) updated.getResult(), Boolean.TRUE),
                             updated.getPropByRes(),
                             updated.getPerformedTasks()));
 
-            taskExecutor.execute(tasks, false);
+            taskExecutor.execute(taskInfos, false);
         }
 
         return binder.getUserTO(updated.getResult().getKey());
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java
index 00c311b..3ca159d 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java
@@ -23,7 +23,6 @@ import java.util.List;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.UserPatch;
 import org.apache.syncope.common.lib.to.AttrTO;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.core.persistence.api.entity.Realm;
@@ -42,7 +41,7 @@ public interface PropagationManager {
      * @param noPropResourceKeys external resources performing not to be considered for propagation
      * @return list of propagation tasks
      */
-    List<PropagationTaskTO> getCreateTasks(
+    List<PropagationTaskInfo> getCreateTasks(
             AnyTypeKind kind,
             String key,
             Boolean enable,
@@ -61,7 +60,7 @@ public interface PropagationManager {
      * @param noPropResourceKeys external resources not to be considered for propagation
      * @return list of propagation tasks
      */
-    List<PropagationTaskTO> getUserCreateTasks(
+    List<PropagationTaskInfo> getUserCreateTasks(
             String key,
             String password,
             Boolean enable,
@@ -81,7 +80,7 @@ public interface PropagationManager {
      * @param noPropResourceKeys external resource keys not to be considered for propagation
      * @return list of propagation tasks
      */
-    List<PropagationTaskTO> getUpdateTasks(
+    List<PropagationTaskInfo> getUpdateTasks(
             AnyTypeKind kind,
             String key,
             boolean changePwd,
@@ -98,7 +97,7 @@ public interface PropagationManager {
      * @param noPropResourceKeys external resources not to be considered for propagation
      * @return list of propagation tasks
      */
-    List<PropagationTaskTO> getUserUpdateTasks(
+    List<PropagationTaskInfo> getUserUpdateTasks(
             WorkflowResult<Pair<UserPatch, Boolean>> wfResult,
             boolean changePwd,
             Collection<String> noPropResourceKeys);
@@ -110,7 +109,7 @@ public interface PropagationManager {
      * @param wfResult user to be propagated (and info associated), as per result from workflow
      * @return list of propagation tasks
      */
-    List<PropagationTaskTO> getUserUpdateTasks(WorkflowResult<Pair<UserPatch, Boolean>> wfResult);
+    List<PropagationTaskInfo> getUserUpdateTasks(WorkflowResult<Pair<UserPatch, Boolean>> wfResult);
 
     /**
      * Create the delete tasks for the any object from each resource associated, unless in {@code noPropResourceKeys}.
@@ -121,7 +120,7 @@ public interface PropagationManager {
      * @param noPropResourceKeys external resource keys not to be considered for propagation
      * @return list of propagation tasks
      */
-    List<PropagationTaskTO> getDeleteTasks(
+    List<PropagationTaskInfo> getDeleteTasks(
             AnyTypeKind kind,
             String key,
             PropagationByResource propByRes,
@@ -135,7 +134,7 @@ public interface PropagationManager {
      * @param noPropResourceKeys external resource keys not to be considered for propagation
      * @return list of propagation tasks
      */
-    List<PropagationTaskTO> createTasks(
+    List<PropagationTaskInfo> createTasks(
             Realm realm,
             PropagationByResource propByRes,
             Collection<String> noPropResourceKeys);
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationReporter.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationReporter.java
index a27f02a..9b33c1f 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationReporter.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationReporter.java
@@ -21,7 +21,6 @@ package org.apache.syncope.core.provisioning.api.propagation;
 import java.util.Collection;
 import java.util.List;
 import org.apache.syncope.common.lib.to.PropagationStatus;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
 
@@ -34,22 +33,22 @@ public interface PropagationReporter {
      * Report propagation status after executions in case blocking failure (e.g. on priority resources).
      *
      * @param failingResource failing resource name
-     * @param tasks propagation tasks performed before failure
+     * @param taskInfos propagation tasks performed before failure
      */
-    void onPriorityResourceFailure(String failingResource, Collection<PropagationTaskTO> tasks);
+    void onPriorityResourceFailure(String failingResource, Collection<PropagationTaskInfo> taskInfos);
 
     /**
      * Report propagation status after executions in case of success or non-blocking failure
      * (e.g. on non-priority resources).
      *
-     * @param propagationTask propagation task
+     * @param taskInfo propagation task
      * @param execStatus propagation execution status
      * @param failureReason propagation execution failure message
      * @param beforeObj retrieved connector object before operation execution
      * @param afterObj retrieved connector object after operation execution
      */
     void onSuccessOrNonPriorityResourceFailures(
-            PropagationTaskTO propagationTask,
+            PropagationTaskInfo taskInfo,
             PropagationTaskExecStatus execStatus,
             String failureReason,
             ConnectorObject beforeObj,
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java
index 9c4f107..6dd3de8 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java
@@ -19,12 +19,11 @@
 package org.apache.syncope.core.provisioning.api.propagation;
 
 import java.util.concurrent.Callable;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 
 public interface PropagationTaskCallable extends Callable<TaskExec> {
 
-    void setTaskTO(PropagationTaskTO taskTO);
+    void setTaskInfo(PropagationTaskInfo taskInfo);
 
     void setReporter(PropagationReporter reporter);
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java
index e331aa7..a000d78 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java
@@ -44,28 +44,28 @@ public interface PropagationTaskExecutor {
     /**
      * Execute the given PropagationTask and returns the generated {@link TaskExec}.
      *
-     * @param task to be executed
+     * @param taskInfo to be executed
      * @return the generated TaskExec
      */
-    TaskExec execute(PropagationTaskTO task);
+    TaskExec execute(PropagationTaskInfo taskInfo);
 
     /**
      * Execute the given PropagationTask and returns the generated {@link TaskExec}.
      *
-     * @param task to be executed
+     * @param taskInfo to be executed
      * @param reporter to report propagation execution status
      * @return the generated TaskExec
      */
-    TaskExec execute(PropagationTaskTO task, PropagationReporter reporter);
+    TaskExec execute(PropagationTaskInfo taskInfo, PropagationReporter reporter);
 
     /**
      * Execute a collection of PropagationTask objects.
      * The process is interrupted as soon as the result of the communication with a resource with non-null priority is
      * in error.
      *
-     * @param tasks to be execute, in given order
+     * @param taskInfos to be execute, in given order
      * @param nullPriorityAsync asynchronously executes tasks related to resources with no priority
      * @return reporter to report propagation execution status
      */
-    PropagationReporter execute(Collection<PropagationTaskTO> tasks, boolean nullPriorityAsync);
+    PropagationReporter execute(Collection<PropagationTaskInfo> taskInfos, boolean nullPriorityAsync);
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java
similarity index 61%
copy from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java
copy to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java
index 9c4f107..4f18a50 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskCallable.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java
@@ -18,13 +18,30 @@
  */
 package org.apache.syncope.core.provisioning.api.propagation;
 
-import java.util.concurrent.Callable;
 import org.apache.syncope.common.lib.to.PropagationTaskTO;
-import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
 
-public interface PropagationTaskCallable extends Callable<TaskExec> {
+public class PropagationTaskInfo extends PropagationTaskTO {
 
-    void setTaskTO(PropagationTaskTO taskTO);
+    private static final long serialVersionUID = -2879861567335503099L;
 
-    void setReporter(PropagationReporter reporter);
+    private boolean read;
+
+    private ConnectorObject beforeObj;
+
+    public boolean isRead() {
+        return read;
+    }
+
+    public void setRead(final boolean read) {
+        this.read = read;
+    }
+
+    public ConnectorObject getBeforeObj() {
+        return beforeObj;
+    }
+
+    public void setBeforeObj(final ConnectorObject beforeObj) {
+        this.beforeObj = beforeObj;
+    }
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
index a1b8b3f..917b8d7 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
@@ -27,7 +27,6 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.AnyObjectPatch;
 import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.common.lib.types.ResourceOperation;
@@ -39,6 +38,7 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.workflow.api.AnyObjectWorkflowAdapter;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Propagation;
@@ -75,14 +75,14 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
 
         WorkflowResult<String> created = awfAdapter.create(anyObjectTO);
 
-        List<PropagationTaskTO> tasks = propagationManager.getCreateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getCreateTasks(
                 AnyTypeKind.ANY_OBJECT,
                 created.getResult(),
                 null,
                 created.getPropByRes(),
                 anyObjectTO.getVirAttrs(),
                 excludedResources);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return Pair.of(created.getResult(), propagationReporter.getStatuses());
     }
@@ -101,7 +101,7 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
 
         WorkflowResult<AnyObjectPatch> updated = awfAdapter.update(anyObjectPatch);
 
-        List<PropagationTaskTO> tasks = propagationManager.getUpdateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUpdateTasks(
                 AnyTypeKind.ANY_OBJECT,
                 updated.getResult().getKey(),
                 false,
@@ -109,7 +109,7 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
                 updated.getPropByRes(),
                 anyObjectPatch.getVirAttrs(),
                 excludedResources);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return Pair.of(updated.getResult(), propagationReporter.getStatuses());
     }
@@ -132,12 +132,12 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
         // information could only be available after awfAdapter.delete(), which
         // will also effectively remove user from db, thus making virtually
         // impossible by NotificationManager to fetch required user information
-        List<PropagationTaskTO> tasks = propagationManager.getDeleteTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getDeleteTasks(
                 AnyTypeKind.ANY_OBJECT,
                 key,
                 propByRes,
                 excludedResources);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         try {
             awfAdapter.delete(key);
@@ -165,7 +165,7 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.addAll(ResourceOperation.UPDATE, resources);
 
-        List<PropagationTaskTO> tasks = propagationManager.getUpdateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUpdateTasks(
                 AnyTypeKind.ANY_OBJECT,
                 key,
                 false,
@@ -173,7 +173,7 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
                 propByRes,
                 null,
                 null);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return propagationReporter.getStatuses();
     }
@@ -185,12 +185,12 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.addAll(ResourceOperation.DELETE, resources);
 
-        List<PropagationTaskTO> tasks = propagationManager.getDeleteTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getDeleteTasks(
                 AnyTypeKind.ANY_OBJECT,
                 key,
                 propByRes,
                 CollectionUtils.removeAll(anyObjectDAO.findAllResourceKeys(key), resources));
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return propagationReporter.getStatuses();
     }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java
index 941b588..fd6218e 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java
@@ -32,7 +32,6 @@ import org.apache.syncope.common.lib.patch.GroupPatch;
 import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.to.GroupTO;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
@@ -44,6 +43,7 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
@@ -72,7 +72,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
     public Pair<String, List<PropagationStatus>> create(final GroupTO groupTO, final boolean nullPriorityAsync) {
         WorkflowResult<String> created = gwfAdapter.create(groupTO);
 
-        List<PropagationTaskTO> tasks = propagationManager.getCreateTasks(
+        List<PropagationTaskInfo> tasks = propagationManager.getCreateTasks(
                 AnyTypeKind.GROUP,
                 created.getResult(),
                 null,
@@ -100,7 +100,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
             groupOwnerMap.put(created.getResult(), groupOwner.getValues().iterator().next());
         }
 
-        List<PropagationTaskTO> tasks = propagationManager.getCreateTasks(
+        List<PropagationTaskInfo> tasks = propagationManager.getCreateTasks(
                 AnyTypeKind.GROUP,
                 created.getResult(),
                 null,
@@ -126,7 +126,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
 
         WorkflowResult<GroupPatch> updated = gwfAdapter.update(groupPatch);
 
-        List<PropagationTaskTO> tasks = propagationManager.getUpdateTasks(
+        List<PropagationTaskInfo> tasks = propagationManager.getUpdateTasks(
                 AnyTypeKind.GROUP,
                 updated.getResult().getKey(),
                 false,
@@ -149,14 +149,14 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
     public List<PropagationStatus> delete(
             final String key, final Set<String> excludedResources, final boolean nullPriorityAsync) {
 
-        List<PropagationTaskTO> tasks = new ArrayList<>();
+        List<PropagationTaskInfo> taskInfos = new ArrayList<>();
 
         // Generate propagation tasks for deleting users and any objects from group resources, 
         // if they are on those resources only because of the reason being deleted (see SYNCOPE-357)
         for (Map.Entry<String, PropagationByResource> entry
                 : groupDataBinder.findUsersWithTransitiveResources(key).entrySet()) {
 
-            tasks.addAll(propagationManager.getDeleteTasks(
+            taskInfos.addAll(propagationManager.getDeleteTasks(
                     AnyTypeKind.USER,
                     entry.getKey(),
                     entry.getValue(),
@@ -165,7 +165,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
         for (Map.Entry<String, PropagationByResource> entry
                 : groupDataBinder.findAnyObjectsWithTransitiveResources(key).entrySet()) {
 
-            tasks.addAll(propagationManager.getDeleteTasks(
+            taskInfos.addAll(propagationManager.getDeleteTasks(
                     AnyTypeKind.ANY_OBJECT,
                     entry.getKey(),
                     entry.getValue(),
@@ -173,13 +173,13 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
         }
 
         // Generate propagation tasks for deleting this group from resources
-        tasks.addAll(propagationManager.getDeleteTasks(
+        taskInfos.addAll(propagationManager.getDeleteTasks(
                 AnyTypeKind.GROUP,
                 key,
                 null,
                 null));
 
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         gwfAdapter.delete(key);
 
@@ -198,7 +198,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.addAll(ResourceOperation.UPDATE, resources);
 
-        List<PropagationTaskTO> tasks = propagationManager.getUpdateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUpdateTasks(
                 AnyTypeKind.GROUP,
                 key,
                 false,
@@ -206,7 +206,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
                 propByRes,
                 null,
                 null);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return propagationReporter.getStatuses();
     }
@@ -218,12 +218,12 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.addAll(ResourceOperation.DELETE, resources);
 
-        List<PropagationTaskTO> tasks = propagationManager.getDeleteTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getDeleteTasks(
                 AnyTypeKind.GROUP,
                 key,
                 propByRes,
                 CollectionUtils.removeAll(groupDAO.findAllResourceKeys(key), resources));
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return propagationReporter.getStatuses();
     }
@@ -232,5 +232,4 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
     public String link(final GroupPatch groupPatch) {
         return gwfAdapter.update(groupPatch).getResult().getKey();
     }
-
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
index 802ccfb..7bb1a6a 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
@@ -32,7 +32,6 @@ import org.apache.syncope.common.lib.patch.StatusPatch;
 import org.apache.syncope.common.lib.patch.StringPatchItem;
 import org.apache.syncope.common.lib.patch.UserPatch;
 import org.apache.syncope.common.lib.to.PropagationStatus;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.PatchOperation;
@@ -48,6 +47,7 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 import org.apache.syncope.core.provisioning.api.VirAttrHandler;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
 import org.slf4j.Logger;
@@ -100,14 +100,14 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         WorkflowResult<Pair<String, Boolean>> created =
                 uwfAdapter.create(userTO, disablePwdPolicyCheck, enabled, storePassword);
 
-        List<PropagationTaskTO> tasks = propagationManager.getUserCreateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUserCreateTasks(
                 created.getResult().getLeft(),
                 userTO.getPassword(),
                 created.getResult().getRight(),
                 created.getPropByRes(),
                 userTO.getVirAttrs(),
                 excludedResources);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return Pair.of(created.getResult().getLeft(), propagationReporter.getStatuses());
     }
@@ -116,8 +116,8 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
     public Pair<UserPatch, List<PropagationStatus>> update(final UserPatch userPatch, final boolean nullPriorityAsync) {
         WorkflowResult<Pair<UserPatch, Boolean>> updated = uwfAdapter.update(userPatch);
 
-        List<PropagationTaskTO> tasks = propagationManager.getUserUpdateTasks(updated);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(updated);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return Pair.of(updated.getResult().getLeft(), propagationReporter.getStatuses());
     }
@@ -174,9 +174,9 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
             }
         }
 
-        List<PropagationTaskTO> tasks = propagationManager.getUserUpdateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(
                 updated, updated.getResult().getLeft().getPassword() != null, excludedResources);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return Pair.of(updated.getResult().getLeft(), propagationReporter.getStatuses());
     }
@@ -199,12 +199,12 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         // information could only be available after uwfAdapter.delete(), which
         // will also effectively remove user from db, thus making virtually
         // impossible by NotificationManager to fetch required user information
-        List<PropagationTaskTO> tasks = propagationManager.getDeleteTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getDeleteTasks(
                 AnyTypeKind.USER,
                 key,
                 propByRes,
                 excludedResources);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         try {
             uwfAdapter.delete(key);
@@ -264,7 +264,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
 
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.addAll(ResourceOperation.UPDATE, statusPatch.getResources());
-        List<PropagationTaskTO> tasks = propagationManager.getUpdateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUpdateTasks(
                 AnyTypeKind.USER,
                 statusPatch.getKey(),
                 false,
@@ -272,7 +272,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
                 propByRes,
                 null,
                 null);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return propagationReporter.getStatuses();
     }
@@ -286,11 +286,11 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
             UserPatch userPatch = new UserPatch();
             userPatch.setKey(updated.getLeft().getResult());
 
-            List<PropagationTaskTO> tasks = propagationManager.getUserUpdateTasks(
+            List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(
                     new WorkflowResult<Pair<UserPatch, Boolean>>(
                             Pair.of(userPatch, Boolean.FALSE),
                             updated.getLeft().getPropByRes(), updated.getLeft().getPerformedTasks()));
-            taskExecutor.execute(tasks, false);
+            taskExecutor.execute(taskInfos, false);
         }
     }
 
@@ -327,8 +327,8 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         WorkflowResult<Pair<UserPatch, Boolean>> wfResult = new WorkflowResult<Pair<UserPatch, Boolean>>(
                 ImmutablePair.of(userPatch, (Boolean) null), propByRes, "update");
 
-        List<PropagationTaskTO> tasks = propagationManager.getUserUpdateTasks(wfResult, changePwd, null);
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(wfResult, changePwd, null);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return propagationReporter.getStatuses();
     }
@@ -340,12 +340,12 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.set(ResourceOperation.DELETE, resources);
 
-        List<PropagationTaskTO> tasks = propagationManager.getDeleteTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getDeleteTasks(
                 AnyTypeKind.USER,
                 key,
                 propByRes,
                 CollectionUtils.removeAll(userDAO.findAllResourceKeys(key), resources));
-        PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
+        PropagationReporter propagationReporter = taskExecutor.execute(taskInfos, nullPriorityAsync);
 
         return propagationReporter.getStatuses();
     }
@@ -359,8 +359,8 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
     public void confirmPasswordReset(final String key, final String token, final String password) {
         WorkflowResult<Pair<UserPatch, Boolean>> updated = uwfAdapter.confirmPasswordReset(key, token, password);
 
-        List<PropagationTaskTO> tasks = propagationManager.getUserUpdateTasks(updated);
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUserUpdateTasks(updated);
 
-        taskExecutor.execute(tasks, false);
+        taskExecutor.execute(taskInfos, false);
     }
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
index 64d3f10..8facc39 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
@@ -32,7 +32,6 @@ import java.util.concurrent.atomic.AtomicReference;
 import org.apache.commons.collections4.IteratorUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.to.ExecTO;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.common.lib.types.AuditElements.Result;
 import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
@@ -69,6 +68,7 @@ import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheValue;
 import org.apache.syncope.core.provisioning.api.data.TaskDataBinder;
 import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
@@ -362,29 +362,29 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
     }
 
     @Override
-    public TaskExec execute(final PropagationTaskTO task) {
-        return execute(task, null);
+    public TaskExec execute(final PropagationTaskInfo taskInfo) {
+        return execute(taskInfo, null);
     }
 
     @Override
-    public TaskExec execute(final PropagationTaskTO taskTO, final PropagationReporter reporter) {
+    public TaskExec execute(final PropagationTaskInfo taskInfo, final PropagationReporter reporter) {
         PropagationTask task;
-        if (taskTO.getKey() == null) {
+        if (taskInfo.getKey() == null) {
             task = entityFactory.newEntity(PropagationTask.class);
-            task.setResource(resourceDAO.find(taskTO.getResource()));
-            task.setObjectClassName(taskTO.getObjectClassName());
-            task.setAnyTypeKind(taskTO.getAnyTypeKind());
-            task.setAnyType(taskTO.getAnyType());
-            task.setEntityKey(taskTO.getEntityKey());
-            task.setOperation(taskTO.getOperation());
-            task.setConnObjectKey(taskTO.getConnObjectKey());
-            task.setOldConnObjectKey(taskTO.getOldConnObjectKey());
+            task.setResource(resourceDAO.find(taskInfo.getResource()));
+            task.setObjectClassName(taskInfo.getObjectClassName());
+            task.setAnyTypeKind(taskInfo.getAnyTypeKind());
+            task.setAnyType(taskInfo.getAnyType());
+            task.setEntityKey(taskInfo.getEntityKey());
+            task.setOperation(taskInfo.getOperation());
+            task.setConnObjectKey(taskInfo.getConnObjectKey());
+            task.setOldConnObjectKey(taskInfo.getOldConnObjectKey());
         } else {
-            task = taskDAO.find(taskTO.getKey());
+            task = taskDAO.find(taskInfo.getKey());
         }
         Set<Attribute> attributes = new HashSet<>();
-        if (StringUtils.isNotBlank(taskTO.getAttributes())) {
-            attributes.addAll(Arrays.asList(POJOHelper.deserialize(taskTO.getAttributes(), Attribute[].class)));
+        if (StringUtils.isNotBlank(taskInfo.getAttributes())) {
+            attributes.addAll(Arrays.asList(POJOHelper.deserialize(taskInfo.getAttributes(), Attribute[].class)));
         }
         task.setAttributes(attributes);
 
@@ -416,12 +416,16 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
             orgUnit = task.getResource().getOrgUnit();
             connector = connFactory.getConnector(task.getResource());
 
-            // Try to read remote object BEFORE any actual operation
-            beforeObj = provision == null && orgUnit == null
-                    ? null
-                    : orgUnit == null
-                            ? getRemoteObject(task, connector, provision, false)
-                            : getRemoteObject(task, connector, orgUnit, false);
+            if (!taskInfo.isRead()) {
+                // Try to read remote object BEFORE any actual operation
+                beforeObj = provision == null && orgUnit == null
+                        ? null
+                        : orgUnit == null
+                                ? getRemoteObject(task, connector, provision, false)
+                                : getRemoteObject(task, connector, orgUnit, false);
+            } else {
+                beforeObj = taskInfo.getBeforeObj();
+            }
 
             for (PropagationActions action : actions) {
                 action.before(task, beforeObj);
@@ -522,7 +526,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
 
             if (reporter != null) {
                 reporter.onSuccessOrNonPriorityResourceFailures(
-                        taskTO,
+                        taskInfo,
                         PropagationTaskExecStatus.valueOf(execution.getStatus()),
                         failureReason,
                         beforeObj,
@@ -552,7 +556,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
                     result,
                     beforeObj,
                     new Object[] { execTO, afterObj },
-                    taskTO);
+                    taskInfo);
 
             auditManager.audit(
                     AuthContextUtils.getUsername(),
@@ -563,23 +567,26 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
                     result,
                     beforeObj,
                     new Object[] { execTO, afterObj },
-                    taskTO);
+                    taskInfo);
         }
 
         return execution;
     }
 
     protected abstract void doExecute(
-            Collection<PropagationTaskTO> tasks, PropagationReporter reporter, boolean nullPriorityAsync);
+            Collection<PropagationTaskInfo> taskInfos, PropagationReporter reporter, boolean nullPriorityAsync);
 
     @Override
-    public PropagationReporter execute(final Collection<PropagationTaskTO> tasks, final boolean nullPriorityAsync) {
+    public PropagationReporter execute(
+            final Collection<PropagationTaskInfo> taskInfos,
+            final boolean nullPriorityAsync) {
+
         PropagationReporter reporter = new DefaultPropagationReporter();
         try {
-            doExecute(tasks, reporter, nullPriorityAsync);
+            doExecute(taskInfos, reporter, nullPriorityAsync);
         } catch (PropagationException e) {
             LOG.error("Error propagation priority resource", e);
-            reporter.onPriorityResourceFailure(e.getResourceName(), tasks);
+            reporter.onPriorityResourceFailure(e.getResourceName(), taskInfos);
         }
 
         return reporter;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
index 28adcc1..3d10139 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
@@ -25,10 +25,10 @@ import java.util.List;
 import org.apache.commons.collections4.IterableUtils;
 import org.apache.commons.collections4.Predicate;
 import org.apache.syncope.common.lib.to.PropagationStatus;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
 import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
 import org.slf4j.Logger;
@@ -54,14 +54,14 @@ public class DefaultPropagationReporter implements PropagationReporter {
 
     @Override
     public void onSuccessOrNonPriorityResourceFailures(
-            final PropagationTaskTO taskTO,
+            final PropagationTaskInfo taskInfo,
             final PropagationTaskExecStatus executionStatus,
             final String failureReason,
             final ConnectorObject beforeObj,
             final ConnectorObject afterObj) {
 
         PropagationStatus status = new PropagationStatus();
-        status.setResource(taskTO.getResource());
+        status.setResource(taskInfo.getResource());
         status.setStatus(executionStatus);
         status.setFailureReason(failureReason);
 
@@ -77,22 +77,25 @@ public class DefaultPropagationReporter implements PropagationReporter {
     }
 
     @Override
-    public void onPriorityResourceFailure(final String failingResource, final Collection<PropagationTaskTO> tasks) {
+    public void onPriorityResourceFailure(
+            final String failingResource,
+            final Collection<PropagationTaskInfo> taskInfos) {
+
         LOG.debug("Propagation error: {} priority resource failed to propagate", failingResource);
 
-        final PropagationTaskTO taskTO = IterableUtils.find(tasks, new Predicate<PropagationTaskTO>() {
+        final PropagationTaskInfo taskInfo = IterableUtils.find(taskInfos, new Predicate<PropagationTaskInfo>() {
 
             @Override
-            public boolean evaluate(final PropagationTaskTO taskTO) {
-                return taskTO.getResource().equals(failingResource);
+            public boolean evaluate(final PropagationTaskInfo taskInfo) {
+                return taskInfo.getResource().equals(failingResource);
             }
         });
 
-        if (taskTO == null) {
+        if (taskInfo == null) {
             LOG.error("Could not find {} for {}", PropagationTask.class.getName(), failingResource);
         } else {
             PropagationStatus status = new PropagationStatus();
-            status.setResource(taskTO.getResource());
+            status.setResource(taskInfo.getResource());
             status.setStatus(PropagationTaskExecStatus.FAILURE);
             status.setFailureReason(
                     "Propagation error: " + failingResource + " priority resource failed to propagate.");
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationTaskCallable.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationTaskCallable.java
index 9f7aff4..da9b97a 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationTaskCallable.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationTaskCallable.java
@@ -19,13 +19,13 @@
 package org.apache.syncope.core.provisioning.java.propagation;
 
 import java.util.Collection;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskCallable;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -48,7 +48,7 @@ public class DefaultPropagationTaskCallable implements PropagationTaskCallable {
 
     protected final Collection<? extends GrantedAuthority> authorities;
 
-    protected PropagationTaskTO taskTO;
+    protected PropagationTaskInfo taskInfo;
 
     protected PropagationReporter reporter;
 
@@ -60,8 +60,8 @@ public class DefaultPropagationTaskCallable implements PropagationTaskCallable {
     }
 
     @Override
-    public void setTaskTO(final PropagationTaskTO taskTO) {
-        this.taskTO = taskTO;
+    public void setTaskInfo(final PropagationTaskInfo taskInfo) {
+        this.taskInfo = taskInfo;
     }
 
     @Override
@@ -77,11 +77,11 @@ public class DefaultPropagationTaskCallable implements PropagationTaskCallable {
         auth.setDetails(new SyncopeAuthenticationDetails(domain));
         SecurityContextHolder.getContext().setAuthentication(auth);
 
-        LOG.debug("Execution started for {}", taskTO);
+        LOG.debug("Execution started for {}", taskInfo);
 
-        TaskExec execution = taskExecutor.execute(taskTO, reporter);
+        TaskExec execution = taskExecutor.execute(taskInfo, reporter);
 
-        LOG.debug("Execution completed for {}, {}", taskTO, execution);
+        LOG.debug("Execution completed for {}, {}", taskInfo, execution);
 
         return execution;
     }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
index 9fc95ca..7a7d47c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
@@ -34,7 +34,6 @@ import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import javax.annotation.Resource;
 import org.apache.commons.collections4.CollectionUtils;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
@@ -42,6 +41,7 @@ import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskCallable;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 
@@ -61,17 +61,17 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
      * Creates new instances of {@link PropagationTaskCallable} for usage with
      * {@link java.util.concurrent.CompletionService}.
      *
-     * @param taskTO to be executed
+     * @param taskInfo to be executed
      * @param reporter to report propagation execution status
      * @return new {@link PropagationTaskCallable} instance for usage with
      * {@link java.util.concurrent.CompletionService}
      */
     protected PropagationTaskCallable newPropagationTaskCallable(
-            final PropagationTaskTO taskTO, final PropagationReporter reporter) {
+            final PropagationTaskInfo taskInfo, final PropagationReporter reporter) {
 
         PropagationTaskCallable callable = (PropagationTaskCallable) ApplicationContextProvider.getBeanFactory().
                 createBean(DefaultPropagationTaskCallable.class, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
-        callable.setTaskTO(taskTO);
+        callable.setTaskInfo(taskInfo);
         callable.setReporter(reporter);
 
         return callable;
@@ -79,19 +79,19 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
 
     @Override
     protected void doExecute(
-            final Collection<PropagationTaskTO> tasks,
+            final Collection<PropagationTaskInfo> taskInfos,
             final PropagationReporter reporter,
             final boolean nullPriorityAsync) {
 
-        final Map<PropagationTaskTO, ExternalResource> taskToResource = new HashMap<>(tasks.size());
-        List<PropagationTaskTO> prioritizedTasks = new ArrayList<>();
+        final Map<PropagationTaskInfo, ExternalResource> taskToResource = new HashMap<>(taskInfos.size());
+        List<PropagationTaskInfo> prioritizedTasks = new ArrayList<>();
         int connRequestTimeout = 60;
-        for (PropagationTaskTO task : tasks) {
-            ExternalResource resource = resourceDAO.find(task.getResource());
-            taskToResource.put(task, resource);
+        for (PropagationTaskInfo taskInfo : taskInfos) {
+            ExternalResource resource = resourceDAO.find(taskInfo.getResource());
+            taskToResource.put(taskInfo, resource);
 
             if (resource.getPropagationPriority() != null) {
-                prioritizedTasks.add(task);
+                prioritizedTasks.add(taskInfo);
 
                 if (resource.getConnector().getConnRequestTimeout() != null
                         && connRequestTimeout < resource.getConnector().getConnRequestTimeout()) {
@@ -104,11 +104,11 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
         Collections.sort(prioritizedTasks, new PriorityComparator(taskToResource));
         LOG.debug("Propagation tasks sorted by priority, for serial execution: {}", prioritizedTasks);
 
-        Collection<PropagationTaskTO> concurrentTasks = CollectionUtils.subtract(tasks, prioritizedTasks);
+        Collection<PropagationTaskInfo> concurrentTasks = CollectionUtils.subtract(taskInfos, prioritizedTasks);
         LOG.debug("Propagation tasks for concurrent execution: {}", concurrentTasks);
 
         // first process priority resources sequentially and fail as soon as any propagation failure is reported
-        for (PropagationTaskTO task : prioritizedTasks) {
+        for (PropagationTaskInfo task : prioritizedTasks) {
             TaskExec execution = null;
             PropagationTaskExecStatus execStatus;
             try {
@@ -125,12 +125,12 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
 
         // then process non-priority resources concurrently...
         final CompletionService<TaskExec> completionService = new ExecutorCompletionService<>(executor);
-        Map<PropagationTaskTO, Future<TaskExec>> nullPriority = new HashMap<>(concurrentTasks.size());
-        for (PropagationTaskTO task : concurrentTasks) {
+        Map<PropagationTaskInfo, Future<TaskExec>> nullPriority = new HashMap<>(concurrentTasks.size());
+        for (PropagationTaskInfo taskInfo : concurrentTasks) {
             try {
                 nullPriority.put(
-                        task,
-                        completionService.submit(newPropagationTaskCallable(task, reporter)));
+                        taskInfo,
+                        completionService.submit(newPropagationTaskCallable(taskInfo, reporter)));
             } catch (Exception e) {
                 LOG.error("Unexpected exception", e);
             }
@@ -138,7 +138,7 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
         // ...waiting for all callables to complete, if async processing was not required
         if (!nullPriority.isEmpty()) {
             if (nullPriorityAsync) {
-                for (Map.Entry<PropagationTaskTO, Future<TaskExec>> entry : nullPriority.entrySet()) {
+                for (Map.Entry<PropagationTaskInfo, Future<TaskExec>> entry : nullPriority.entrySet()) {
                     reporter.onSuccessOrNonPriorityResourceFailures(
                             entry.getKey(), PropagationTaskExecStatus.CREATED, null, null, null);
                 }
@@ -174,18 +174,18 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
     /**
      * Compare propagation tasks according to related ExternalResource's priority.
      */
-    protected static class PriorityComparator implements Comparator<PropagationTaskTO>, Serializable {
+    protected static class PriorityComparator implements Comparator<PropagationTaskInfo>, Serializable {
 
         private static final long serialVersionUID = -1969355670784448878L;
 
-        private final Map<PropagationTaskTO, ExternalResource> taskToResource;
+        private final Map<PropagationTaskInfo, ExternalResource> taskToResource;
 
-        public PriorityComparator(final Map<PropagationTaskTO, ExternalResource> taskToResource) {
+        public PriorityComparator(final Map<PropagationTaskInfo, ExternalResource> taskToResource) {
             this.taskToResource = taskToResource;
         }
 
         @Override
-        public int compare(final PropagationTaskTO task1, final PropagationTaskTO task2) {
+        public int compare(final PropagationTaskInfo task1, final PropagationTaskInfo task2) {
             int prop1 = taskToResource.get(task1).getPropagationPriority();
             int prop2 = taskToResource.get(task2).getPropagationPriority();
 
@@ -196,5 +196,4 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
                             : -1;
         }
     }
-
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
index 1e5ab43..0111e2a 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
@@ -33,7 +33,6 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.StringPatchItem;
 import org.apache.syncope.common.lib.patch.UserPatch;
 import org.apache.syncope.common.lib.to.AttrTO;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.common.lib.types.ResourceOperation;
@@ -58,6 +57,7 @@ import org.apache.syncope.core.persistence.api.entity.resource.Item;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.provisioning.api.MappingManager;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
 import org.identityconnectors.framework.common.objects.Attribute;
@@ -135,7 +135,7 @@ public class PropagationManagerImpl implements PropagationManager {
     }
 
     @Override
-    public List<PropagationTaskTO> getCreateTasks(
+    public List<PropagationTaskInfo> getCreateTasks(
             final AnyTypeKind kind,
             final String key,
             final Boolean enable,
@@ -147,7 +147,7 @@ public class PropagationManagerImpl implements PropagationManager {
     }
 
     @Override
-    public List<PropagationTaskTO> getUserCreateTasks(
+    public List<PropagationTaskInfo> getUserCreateTasks(
             final String key,
             final String password,
             final Boolean enable,
@@ -158,7 +158,7 @@ public class PropagationManagerImpl implements PropagationManager {
         return getCreateTasks(userDAO.authFind(key), password, enable, propByRes, vAttrs, noPropResourceKeys);
     }
 
-    protected List<PropagationTaskTO> getCreateTasks(
+    protected List<PropagationTaskInfo> getCreateTasks(
             final Any<?> any,
             final String password,
             final Boolean enable,
@@ -167,7 +167,7 @@ public class PropagationManagerImpl implements PropagationManager {
             final Collection<String> noPropResourceKeys) {
 
         if (propByRes == null || propByRes.isEmpty()) {
-            return Collections.<PropagationTaskTO>emptyList();
+            return Collections.<PropagationTaskInfo>emptyList();
         }
 
         if (noPropResourceKeys != null) {
@@ -178,7 +178,7 @@ public class PropagationManagerImpl implements PropagationManager {
     }
 
     @Override
-    public List<PropagationTaskTO> getUpdateTasks(
+    public List<PropagationTaskInfo> getUpdateTasks(
             final AnyTypeKind kind,
             final String key,
             final boolean changePwd,
@@ -191,7 +191,7 @@ public class PropagationManagerImpl implements PropagationManager {
     }
 
     @Override
-    public List<PropagationTaskTO> getUserUpdateTasks(
+    public List<PropagationTaskInfo> getUserUpdateTasks(
             final WorkflowResult<Pair<UserPatch, Boolean>> wfResult,
             final boolean changePwd,
             final Collection<String> noPropResourceKeys) {
@@ -209,11 +209,11 @@ public class PropagationManagerImpl implements PropagationManager {
     }
 
     @Override
-    public List<PropagationTaskTO> getUserUpdateTasks(final WorkflowResult<Pair<UserPatch, Boolean>> wfResult) {
+    public List<PropagationTaskInfo> getUserUpdateTasks(final WorkflowResult<Pair<UserPatch, Boolean>> wfResult) {
         UserPatch userPatch = wfResult.getResult().getKey();
 
         // Propagate password update only to requested resources
-        List<PropagationTaskTO> tasks = new ArrayList<>();
+        List<PropagationTaskInfo> tasks = new ArrayList<>();
         if (userPatch.getPassword() == null) {
             // a. no specific password propagation request: generate propagation tasks for any resource associated
             tasks = getUserUpdateTasks(wfResult, false, null);
@@ -256,7 +256,7 @@ public class PropagationManagerImpl implements PropagationManager {
         return tasks;
     }
 
-    protected List<PropagationTaskTO> getUpdateTasks(
+    protected List<PropagationTaskInfo> getUpdateTasks(
             final Any<?> any,
             final String password,
             final boolean changePwd,
@@ -280,7 +280,7 @@ public class PropagationManagerImpl implements PropagationManager {
     }
 
     @Override
-    public List<PropagationTaskTO> getDeleteTasks(
+    public List<PropagationTaskInfo> getDeleteTasks(
             final AnyTypeKind kind,
             final String key,
             final PropagationByResource propByRes,
@@ -303,7 +303,7 @@ public class PropagationManagerImpl implements PropagationManager {
         return getDeleteTasks(any, localPropByRes, noPropResourceKeys);
     }
 
-    protected List<PropagationTaskTO> getDeleteTasks(
+    protected List<PropagationTaskInfo> getDeleteTasks(
             final Any<?> any,
             final PropagationByResource propByRes,
             final Collection<String> noPropResourceKeys) {
@@ -323,7 +323,7 @@ public class PropagationManagerImpl implements PropagationManager {
      * @param vAttrs virtual attributes to be set
      * @return list of propagation tasks created
      */
-    protected List<PropagationTaskTO> createTasks(final Any<?> any,
+    protected List<PropagationTaskInfo> createTasks(final Any<?> any,
             final String password, final boolean changePwd,
             final Boolean enable, final boolean deleteOnResource, final PropagationByResource propByRes,
             final Collection<AttrTO> vAttrs) {
@@ -367,7 +367,7 @@ public class PropagationManagerImpl implements PropagationManager {
         }
         LOG.debug("With virtual attributes {}:\n{}\n{}", any, propByRes, vAttrMap);
 
-        List<PropagationTaskTO> tasks = new ArrayList<>();
+        List<PropagationTaskInfo> tasks = new ArrayList<>();
 
         for (Map.Entry<String, ResourceOperation> entry : propByRes.asMap().entrySet()) {
             ExternalResource resource = resourceDAO.find(entry.getKey());
@@ -385,7 +385,7 @@ public class PropagationManagerImpl implements PropagationManager {
                 LOG.warn("Requesting propagation for {} but no propagation mapping provided for {}",
                         any.getType(), resource);
             } else {
-                PropagationTaskTO task = new PropagationTaskTO();
+                PropagationTaskInfo task = new PropagationTaskInfo();
                 task.setResource(resource.getKey());
                 task.setObjectClassName(provision.getObjectClass().getObjectClassValue());
                 task.setAnyTypeKind(any.getType().getKind());
@@ -441,7 +441,7 @@ public class PropagationManagerImpl implements PropagationManager {
     }
 
     @Override
-    public List<PropagationTaskTO> createTasks(
+    public List<PropagationTaskInfo> createTasks(
             final Realm realm,
             final PropagationByResource propByRes,
             final Collection<String> noPropResourceKeys) {
@@ -456,7 +456,7 @@ public class PropagationManagerImpl implements PropagationManager {
         propByRes.purge();
         LOG.debug("After purge {}:\n{}", realm, propByRes);
 
-        List<PropagationTaskTO> tasks = new ArrayList<>();
+        List<PropagationTaskInfo> tasks = new ArrayList<>();
 
         for (Map.Entry<String, ResourceOperation> entry : propByRes.asMap().entrySet()) {
             ExternalResource resource = resourceDAO.find(entry.getKey());
@@ -470,7 +470,7 @@ public class PropagationManagerImpl implements PropagationManager {
                 LOG.warn("Requesting propagation for {} but no ConnObjectLink provided for {}",
                         realm.getFullPath(), resource);
             } else {
-                PropagationTaskTO task = new PropagationTaskTO();
+                PropagationTaskInfo task = new PropagationTaskInfo();
                 task.setResource(resource.getKey());
                 task.setObjectClassName(orgUnit.getObjectClass().getObjectClassValue());
                 task.setEntityKey(realm.getKey());
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
index 298760a..0cc10fe 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
@@ -53,12 +53,14 @@ import org.apache.syncope.core.provisioning.api.TimeoutException;
 import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
 import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopePushResultHandler;
 import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
 import org.apache.syncope.core.provisioning.java.job.AfterHandlingJob;
 import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
 import org.identityconnectors.framework.common.objects.AttributeBuilder;
+import org.apache.syncope.core.provisioning.java.propagation.DefaultPropagationReporter;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
 import org.identityconnectors.framework.common.objects.ObjectClass;
@@ -114,31 +116,45 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.add(ResourceOperation.UPDATE, profile.getTask().getResource().getKey());
 
-        PropagationReporter reporter = taskExecutor.execute(propagationManager.getUpdateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getUpdateTasks(
                 any.getType().getKind(),
                 any.getKey(),
                 changepwd,
                 enable,
                 propByRes,
                 null,
-                noPropResources),
-                false);
-        reportPropagation(result, reporter);
+                noPropResources);
+        if (!taskInfos.isEmpty()) {
+            taskInfos.get(0).setRead(true);
+            taskInfos.get(0).setBeforeObj(beforeObj);
+            PropagationReporter reporter = new DefaultPropagationReporter();
+            taskExecutor.execute(taskInfos.get(0), reporter);
+            reportPropagation(result, reporter);
+        }
     }
 
-    protected void deprovision(final Any<?> any, final ProvisioningReport result) {
+    protected void deprovision(final Any<?> any, final ConnectorObject beforeObj, final ProvisioningReport result) {
         AnyTO before = getAnyTO(any.getKey());
 
         List<String> noPropResources = new ArrayList<>(before.getResources());
         noPropResources.remove(profile.getTask().getResource().getKey());
 
-        PropagationReporter reporter = taskExecutor.execute(propagationManager.getDeleteTasks(
+        PropagationByResource propByRes = new PropagationByResource();
+        propByRes.add(ResourceOperation.DELETE, profile.getTask().getResource().getKey());
+        propByRes.addOldConnObjectKey(profile.getTask().getResource().getKey(), beforeObj.getUid().getUidValue());
+
+        List<PropagationTaskInfo> taskInfos = propagationManager.getDeleteTasks(
                 any.getType().getKind(),
                 any.getKey(),
-                null,
-                noPropResources),
-                false);
-        reportPropagation(result, reporter);
+                propByRes,
+                noPropResources);
+        if (!taskInfos.isEmpty()) {
+            taskInfos.get(0).setRead(true);
+            taskInfos.get(0).setBeforeObj(beforeObj);
+            PropagationReporter reporter = new DefaultPropagationReporter();
+            taskExecutor.execute(taskInfos.get(0), reporter);
+            reportPropagation(result, reporter);
+        }
     }
 
     protected void provision(final Any<?> any, final Boolean enable, final ProvisioningReport result) {
@@ -150,15 +166,20 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
 
-        PropagationReporter reporter = taskExecutor.execute(propagationManager.getCreateTasks(
+        List<PropagationTaskInfo> taskInfos = propagationManager.getCreateTasks(
                 any.getType().getKind(),
                 any.getKey(),
                 enable,
                 propByRes,
                 before.getVirAttrs(),
-                noPropResources),
-                false);
-        reportPropagation(result, reporter);
+                noPropResources);
+        if (!taskInfos.isEmpty()) {
+            taskInfos.get(0).setRead(true);
+            taskInfos.get(0).setBeforeObj(null);
+            PropagationReporter reporter = new DefaultPropagationReporter();
+            taskExecutor.execute(taskInfos.get(0), reporter);
+            reportPropagation(result, reporter);
+        }
     }
 
     protected void link(final Any<?> any, final boolean unlink, final ProvisioningReport result) {
@@ -172,7 +193,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
         result.setStatus(ProvisioningReport.Status.SUCCESS);
     }
 
-    protected void unassign(final Any<?> any, final ProvisioningReport result) {
+    protected void unassign(final Any<?> any, final ConnectorObject beforeObj, final ProvisioningReport result) {
         AnyPatch patch = getAnyUtils().newAnyPatch(any.getKey());
         patch.getResources().add(new StringPatchItem.Builder().
                 operation(PatchOperation.DELETE).
@@ -180,7 +201,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
 
         update(patch);
 
-        deprovision(any, result);
+        deprovision(any, beforeObj, result);
     }
 
     protected void assign(final Any<?> any, final Boolean enabled, final ProvisioningReport result) {
@@ -390,7 +411,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
                                 LOG.debug("PushTask not configured for delete");
                                 result.setStatus(ProvisioningReport.Status.IGNORE);
                             } else {
-                                deprovision(any, result);
+                                deprovision(any, beforeObj, result);
                             }
                             break;
 
@@ -403,7 +424,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
                                 LOG.debug("PushTask not configured for delete");
                                 result.setStatus(ProvisioningReport.Status.IGNORE);
                             } else {
-                                unassign(any, result);
+                                unassign(any, beforeObj, result);
                             }
                             break;
 
@@ -451,21 +472,27 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
                 if (result.getStatus() == null) {
                     result.setStatus(ProvisioningReport.Status.SUCCESS);
                 }
-                resultStatus = AuditElements.Result.SUCCESS;
-                if (connObjectKey != null && connObjecKeyValue != null) {
-                    output = getRemoteObject(
-                            provision.getObjectClass(),
-                            connObjectKey.getExtAttrName(),
-                            connObjecKeyValue,
-                            provision.getMapping().getItems().iterator());
+
+                if (notificationsAvailable || auditRequested) {
+                    resultStatus = AuditElements.Result.SUCCESS;
+                    if (connObjectKey != null && connObjecKeyValue != null) {
+                        output = getRemoteObject(
+                                provision.getObjectClass(),
+                                connObjectKey.getExtAttrName(),
+                                connObjecKeyValue,
+                                provision.getMapping().getItems().iterator());
+                    }
                 }
             } catch (IgnoreProvisionException e) {
                 throw e;
             } catch (Exception e) {
                 result.setStatus(ProvisioningReport.Status.FAILURE);
                 result.setMessage(ExceptionUtils.getRootCauseMessage(e));
-                resultStatus = AuditElements.Result.FAILURE;
-                output = e;
+
+                if (notificationsAvailable || auditRequested) {
+                    resultStatus = AuditElements.Result.FAILURE;
+                    output = e;
+                }
 
                 LOG.warn("Error pushing {} towards {}", any, profile.getTask().getResource(), e);
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
index 5c63133..b059412 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
@@ -24,7 +24,6 @@ import java.util.List;
 import java.util.Set;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.to.RealmTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.AuditElements;
@@ -43,6 +42,7 @@ import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
 import org.apache.syncope.core.persistence.api.entity.task.PullTask;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
@@ -238,8 +238,8 @@ public class DefaultRealmPullResultHandler
             PropagationByResource propByRes = new PropagationByResource();
             propByRes.addAll(ResourceOperation.CREATE, realm.getResourceKeys());
             if (unmatchingRule == UnmatchingRule.ASSIGN) {
-                List<PropagationTaskTO> tasks = propagationManager.createTasks(realm, propByRes, null);
-                taskExecutor.execute(tasks, false);
+                List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
+                taskExecutor.execute(taskInfos, false);
             }
 
             RealmTO actual = binder.getRealmTO(realm, true);
@@ -324,8 +324,8 @@ public class DefaultRealmPullResultHandler
                         realm = realmDAO.save(realm);
                         RealmTO updated = binder.getRealmTO(realm, true);
 
-                        List<PropagationTaskTO> tasks = propagationManager.createTasks(realm, propByRes, null);
-                        taskExecutor.execute(tasks, false);
+                        List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
+                        taskExecutor.execute(taskInfos, false);
 
                         for (PullActions action : profile.getActions()) {
                             action.after(profile, delta, updated, result);
@@ -608,8 +608,8 @@ public class DefaultRealmPullResultHandler
 
                         PropagationByResource propByRes = new PropagationByResource();
                         propByRes.addAll(ResourceOperation.DELETE, realm.getResourceKeys());
-                        List<PropagationTaskTO> tasks = propagationManager.createTasks(realm, propByRes, null);
-                        taskExecutor.execute(tasks, false);
+                        List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
+                        taskExecutor.execute(taskInfos, false);
 
                         realmDAO.delete(realm);
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
index 0225f39..cfe1388 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
@@ -41,11 +41,13 @@ import org.apache.syncope.core.provisioning.api.MappingManager;
 import org.apache.syncope.core.provisioning.api.TimeoutException;
 import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
 import org.apache.syncope.core.provisioning.api.pushpull.RealmPushResultHandler;
 import org.apache.syncope.core.provisioning.java.job.AfterHandlingJob;
+import org.apache.syncope.core.provisioning.java.propagation.DefaultPropagationReporter;
 import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.identityconnectors.framework.common.objects.AttributeBuilder;
@@ -98,28 +100,38 @@ public class DefaultRealmPushResultHandler
         }
     }
 
-    private Realm update(final RealmTO realmTO, final ProvisioningReport result) {
+    private Realm update(final RealmTO realmTO, final ConnectorObject beforeObj, final ProvisioningReport result) {
         Realm realm = realmDAO.findByFullPath(realmTO.getFullPath());
         PropagationByResource propByRes = binder.update(realm, realmTO);
         realm = realmDAO.save(realm);
 
-        PropagationReporter reporter = taskExecutor.execute(
-                propagationManager.createTasks(realm, propByRes, null), false);
-        reportPropagation(result, reporter);
+        List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
+        if (!taskInfos.isEmpty()) {
+            taskInfos.get(0).setRead(true);
+            taskInfos.get(0).setBeforeObj(beforeObj);
+            PropagationReporter reporter = new DefaultPropagationReporter();
+            taskExecutor.execute(taskInfos.get(0), reporter);
+            reportPropagation(result, reporter);
+        }
 
         return realm;
     }
 
-    private void deprovision(final Realm realm, final ProvisioningReport result) {
+    private void deprovision(final Realm realm, final ConnectorObject beforeObj, final ProvisioningReport result) {
         List<String> noPropResources = new ArrayList<>(realm.getResourceKeys());
         noPropResources.remove(profile.getTask().getResource().getKey());
 
         PropagationByResource propByRes = new PropagationByResource();
         propByRes.addAll(ResourceOperation.DELETE, realm.getResourceKeys());
 
-        PropagationReporter reporter = taskExecutor.execute(
-                propagationManager.createTasks(realm, propByRes, noPropResources), false);
-        reportPropagation(result, reporter);
+        List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, noPropResources);
+        if (!taskInfos.isEmpty()) {
+            taskInfos.get(0).setRead(true);
+            taskInfos.get(0).setBeforeObj(beforeObj);
+            PropagationReporter reporter = new DefaultPropagationReporter();
+            taskExecutor.execute(taskInfos.get(0), reporter);
+            reportPropagation(result, reporter);
+        }
     }
 
     private void provision(final Realm realm, final ProvisioningReport result) {
@@ -142,21 +154,21 @@ public class DefaultRealmPushResultHandler
             realmTO.getResources().add(profile.getTask().getResource().getKey());
         }
 
-        update(realmTO, result);
+        update(realmTO, null, result);
     }
 
-    private void unassign(final Realm realm, final ProvisioningReport result) {
+    private void unassign(final Realm realm, final ConnectorObject beforeObj, final ProvisioningReport result) {
         RealmTO realmTO = binder.getRealmTO(realm, true);
         realmTO.getResources().remove(profile.getTask().getResource().getKey());
 
-        deprovision(update(realmTO, result), result);
+        deprovision(update(realmTO, beforeObj, result), beforeObj, result);
     }
 
     private void assign(final Realm realm, final ProvisioningReport result) {
         RealmTO realmTO = binder.getRealmTO(realm, true);
         realmTO.getResources().add(profile.getTask().getResource().getKey());
 
-        provision(update(realmTO, result), result);
+        provision(update(realmTO, null, result), result);
     }
 
     protected ConnectorObject getRemoteObject(
@@ -300,7 +312,7 @@ public class DefaultRealmPushResultHandler
                                 LOG.debug("PushTask not configured for update");
                                 result.setStatus(ProvisioningReport.Status.IGNORE);
                             } else {
-                                update(binder.getRealmTO(realm, true), result);
+                                update(binder.getRealmTO(realm, true), beforeObj, result);
                             }
 
                             break;
@@ -314,7 +326,7 @@ public class DefaultRealmPushResultHandler
                                 LOG.debug("PushTask not configured for delete");
                                 result.setStatus(ProvisioningReport.Status.IGNORE);
                             } else {
-                                deprovision(realm, result);
+                                deprovision(realm, beforeObj, result);
                             }
 
                             break;
@@ -328,7 +340,7 @@ public class DefaultRealmPushResultHandler
                                 LOG.debug("PushTask not configured for delete");
                                 result.setStatus(ProvisioningReport.Status.IGNORE);
                             } else {
-                                unassign(realm, result);
+                                unassign(realm, beforeObj, result);
                             }
 
                             break;
@@ -378,21 +390,27 @@ public class DefaultRealmPushResultHandler
                 if (result.getStatus() == null) {
                     result.setStatus(ProvisioningReport.Status.SUCCESS);
                 }
-                resultStatus = AuditElements.Result.SUCCESS;
-                if (connObjectKey != null && connObjecKeyValue != null) {
-                    output = getRemoteObject(
-                            orgUnit.getObjectClass(),
-                            connObjectKey.getExtAttrName(),
-                            connObjecKeyValue,
-                            orgUnit.getItems().iterator());
+
+                if (notificationsAvailable || auditRequested) {
+                    resultStatus = AuditElements.Result.SUCCESS;
+                    if (connObjectKey != null && connObjecKeyValue != null) {
+                        output = getRemoteObject(
+                                orgUnit.getObjectClass(),
+                                connObjectKey.getExtAttrName(),
+                                connObjecKeyValue,
+                                orgUnit.getItems().iterator());
+                    }
                 }
             } catch (IgnoreProvisionException e) {
                 throw e;
             } catch (Exception e) {
                 result.setStatus(ProvisioningReport.Status.FAILURE);
                 result.setMessage(ExceptionUtils.getRootCauseMessage(e));
-                resultStatus = AuditElements.Result.FAILURE;
-                output = e;
+
+                if (notificationsAvailable || auditRequested) {
+                    resultStatus = AuditElements.Result.FAILURE;
+                    output = e;
+                }
 
                 LOG.warn("Error pushing {} towards {}", realm, profile.getTask().getResource(), e);
 
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ConfirmPasswordResetProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ConfirmPasswordResetProducer.java
index 3d35c93..7347004 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ConfirmPasswordResetProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ConfirmPasswordResetProducer.java
@@ -24,9 +24,9 @@ import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.UserPatch;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 
 public class ConfirmPasswordResetProducer extends AbstractProducer {
 
@@ -41,10 +41,9 @@ public class ConfirmPasswordResetProducer extends AbstractProducer {
             WorkflowResult<Pair<UserPatch, Boolean>> updated =
                     (WorkflowResult<Pair<UserPatch, Boolean>>) exchange.getIn().getBody();
 
-            List<PropagationTaskTO> tasks = getPropagationManager().getUserUpdateTasks(updated);
+            List<PropagationTaskInfo> taskInfos = getPropagationManager().getUserUpdateTasks(updated);
 
-            getPropagationTaskExecutor().execute(tasks, false);
+            getPropagationTaskExecutor().execute(taskInfos, false);
         }
     }
-
 }
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java
index c58590b..1cd0925 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java
@@ -21,7 +21,6 @@ package org.apache.syncope.core.provisioning.camel.producer;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.commons.lang3.StringUtils;
@@ -30,11 +29,11 @@ import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.to.GroupTO;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 
 public class CreateProducer extends AbstractProducer {
 
@@ -54,18 +53,17 @@ public class CreateProducer extends AbstractProducer {
                 WorkflowResult<Pair<String, Boolean>> created =
                         (WorkflowResult<Pair<String, Boolean>>) exchange.getIn().getBody();
 
-                List<PropagationTaskTO> tasks = getPropagationManager().getUserCreateTasks(
+                List<PropagationTaskInfo> taskInfos = getPropagationManager().getUserCreateTasks(
                         created.getResult().getKey(),
                         ((UserTO) actual).getPassword(),
                         created.getResult().getValue(),
                         created.getPropByRes(),
                         ((UserTO) actual).getVirAttrs(),
                         excludedResources);
-                PropagationReporter propagationReporter =
-                        getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                PropagationReporter reporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
 
                 exchange.getOut().setBody(
-                        Pair.of(created.getResult().getKey(), propagationReporter.getStatuses()));
+                        Pair.of(created.getResult().getKey(), reporter.getStatuses()));
             } else if (actual instanceof AnyTO) {
                 WorkflowResult<String> created = (WorkflowResult<String>) exchange.getIn().getBody();
 
@@ -76,31 +74,29 @@ public class CreateProducer extends AbstractProducer {
                         groupOwnerMap.put(created.getResult(), groupOwner.getValues().iterator().next());
                     }
 
-                    List<PropagationTaskTO> tasks = getPropagationManager().getCreateTasks(
+                    List<PropagationTaskInfo> taskInfos = getPropagationManager().getCreateTasks(
                             AnyTypeKind.GROUP,
                             created.getResult(),
                             null,
                             created.getPropByRes(),
                             ((AnyTO) actual).getVirAttrs(),
                             excludedResources);
-                    getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                    getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
 
                     exchange.getOut().setBody(Pair.of(created.getResult(), null));
                 } else {
-                    List<PropagationTaskTO> tasks = getPropagationManager().getCreateTasks(
+                    List<PropagationTaskInfo> taskInfos = getPropagationManager().getCreateTasks(
                             actual instanceof AnyObjectTO ? AnyTypeKind.ANY_OBJECT : AnyTypeKind.GROUP,
                             created.getResult(),
                             null,
                             created.getPropByRes(),
                             ((AnyTO) actual).getVirAttrs(),
                             excludedResources);
-                    PropagationReporter propagationReporter =
-                            getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                    PropagationReporter reporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
 
-                    exchange.getOut().setBody(Pair.of(created.getResult(), propagationReporter.getStatuses()));
+                    exchange.getOut().setBody(Pair.of(created.getResult(), reporter.getStatuses()));
                 }
             }
         }
     }
-
 }
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java
index 2f22b1b..a61c91e 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java
@@ -22,16 +22,15 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 
 public class DeleteProducer extends AbstractProducer {
 
@@ -58,7 +57,7 @@ public class DeleteProducer extends AbstractProducer {
         Boolean nullPriorityAsync = exchange.getProperty("nullPriorityAsync", Boolean.class);
 
         if (null != getAnyTypeKind()) {
-            List<PropagationTaskTO> tasks;
+            List<PropagationTaskInfo> taskInfos;
             PropagationReporter propagationReporter;
             switch (getAnyTypeKind()) {
                 case USER:
@@ -69,23 +68,23 @@ public class DeleteProducer extends AbstractProducer {
                     // information could only be available after uwfAdapter.delete(), which
                     // will also effectively remove user from db, thus making virtually
                     // impossible by NotificationManager to fetch required user information
-                    tasks = getPropagationManager().getDeleteTasks(
+                    taskInfos = getPropagationManager().getDeleteTasks(
                             AnyTypeKind.USER,
                             key,
                             propByRes,
                             excludedResources);
-                    propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                    propagationReporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
                     exchange.setProperty("statuses", propagationReporter.getStatuses());
                     break;
 
                 case GROUP:
-                    tasks = new ArrayList<>();
+                    taskInfos = new ArrayList<>();
                     // Generate propagation tasks for deleting users from group resources, if they are on those
                     // resources only because of the reason being deleted (see SYNCOPE-357)
                     for (Map.Entry<String, PropagationByResource> entry
                             : groupDataBinder.findUsersWithTransitiveResources(key).entrySet()) {
 
-                        tasks.addAll(getPropagationManager().getDeleteTasks(
+                        taskInfos.addAll(getPropagationManager().getDeleteTasks(
                                 AnyTypeKind.USER,
                                 entry.getKey(),
                                 entry.getValue(),
@@ -94,28 +93,28 @@ public class DeleteProducer extends AbstractProducer {
                     for (Map.Entry<String, PropagationByResource> entry
                             : groupDataBinder.findAnyObjectsWithTransitiveResources(key).entrySet()) {
 
-                        tasks.addAll(getPropagationManager().getDeleteTasks(
+                        taskInfos.addAll(getPropagationManager().getDeleteTasks(
                                 AnyTypeKind.ANY_OBJECT,
                                 entry.getKey(),
                                 entry.getValue(),
                                 excludedResources));
                     }       // Generate propagation tasks for deleting this group from resources
-                    tasks.addAll(getPropagationManager().getDeleteTasks(
+                    taskInfos.addAll(getPropagationManager().getDeleteTasks(
                             AnyTypeKind.GROUP,
                             key,
                             null,
                             null));
-                    propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                    propagationReporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
                     exchange.setProperty("statuses", propagationReporter.getStatuses());
                     break;
 
                 case ANY_OBJECT:
-                    tasks = getPropagationManager().getDeleteTasks(
+                    taskInfos = getPropagationManager().getDeleteTasks(
                             AnyTypeKind.ANY_OBJECT,
                             key,
                             null,
                             excludedResources);
-                    propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                    propagationReporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
                     exchange.setProperty("statuses", propagationReporter.getStatuses());
                     break;
 
@@ -124,5 +123,4 @@ public class DeleteProducer extends AbstractProducer {
             }
         }
     }
-
 }
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java
index c6ffdf3..2f01b7d 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java
@@ -19,11 +19,9 @@
 package org.apache.syncope.core.provisioning.camel.producer;
 
 import java.util.List;
-
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.commons.collections4.CollectionUtils;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
@@ -31,6 +29,7 @@ import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 
 public class DeprovisionProducer extends AbstractProducer {
 
@@ -62,39 +61,39 @@ public class DeprovisionProducer extends AbstractProducer {
 
         if (null != getAnyTypeKind()) {
             PropagationByResource propByRes = new PropagationByResource();
-            List<PropagationTaskTO> tasks;
+            List<PropagationTaskInfo> taskInfos;
             PropagationReporter propagationReporter;
             switch (getAnyTypeKind()) {
                 case USER:
                     propByRes.set(ResourceOperation.DELETE, resources);
-                    tasks = getPropagationManager().getDeleteTasks(
+                    taskInfos = getPropagationManager().getDeleteTasks(
                             AnyTypeKind.USER,
                             key,
                             propByRes,
                             CollectionUtils.removeAll(userDAO.findAllResourceKeys(key), resources));
-                    propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                    propagationReporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
                     exchange.getOut().setBody(propagationReporter.getStatuses());
                     break;
 
                 case GROUP:
                     propByRes.addAll(ResourceOperation.DELETE, resources);
-                    tasks = getPropagationManager().getDeleteTasks(
+                    taskInfos = getPropagationManager().getDeleteTasks(
                             AnyTypeKind.GROUP,
                             key,
                             propByRes,
                             CollectionUtils.removeAll(groupDAO.findAllResourceKeys(key), resources));
-                    propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                    propagationReporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
                     exchange.getOut().setBody(propagationReporter.getStatuses());
                     break;
 
                 case ANY_OBJECT:
                     propByRes.addAll(ResourceOperation.DELETE, resources);
-                    tasks = getPropagationManager().getDeleteTasks(
+                    taskInfos = getPropagationManager().getDeleteTasks(
                             AnyTypeKind.ANY_OBJECT,
                             key,
                             propByRes,
                             CollectionUtils.removeAll(anyObjectDAO.findAllResourceKeys(key), resources));
-                    propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                    propagationReporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
                     exchange.getOut().setBody(propagationReporter.getStatuses());
                     break;
 
@@ -103,5 +102,4 @@ public class DeprovisionProducer extends AbstractProducer {
             }
         }
     }
-
 }
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ProvisionProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ProvisionProducer.java
index 459b382..f0f27da 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ProvisionProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ProvisionProducer.java
@@ -19,7 +19,6 @@
 package org.apache.syncope.core.provisioning.camel.producer;
 
 import java.util.List;
-
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.commons.collections4.CollectionUtils;
@@ -29,13 +28,13 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.PasswordPatch;
 import org.apache.syncope.common.lib.patch.StringPatchItem;
 import org.apache.syncope.common.lib.patch.UserPatch;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 
 public class ProvisionProducer extends AbstractProducer {
 
@@ -56,7 +55,7 @@ public class ProvisionProducer extends AbstractProducer {
 
             UserPatch userPatch = new UserPatch();
             userPatch.setKey(key);
-            userPatch.getResources().addAll(CollectionUtils.collect(resources,
+            userPatch.getResources().addAll(CollectionUtils.collect(resources, 
                     new Transformer<String, StringPatchItem>() {
 
                 @Override
@@ -76,10 +75,10 @@ public class ProvisionProducer extends AbstractProducer {
             WorkflowResult<Pair<UserPatch, Boolean>> wfResult = new WorkflowResult<Pair<UserPatch, Boolean>>(
                     ImmutablePair.of(userPatch, (Boolean) null), propByRes, "update");
 
-            List<PropagationTaskTO> tasks = getPropagationManager().getUserUpdateTasks(wfResult, changePwd, null);
-            PropagationReporter propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+            List<PropagationTaskInfo> taskInfos = getPropagationManager().getUserUpdateTasks(wfResult, changePwd, null);
+            PropagationReporter reporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
 
-            exchange.getOut().setBody(propagationReporter.getStatuses());
+            exchange.getOut().setBody(reporter.getStatuses());
         } else {
             PropagationByResource propByRes = new PropagationByResource();
             propByRes.addAll(ResourceOperation.UPDATE, resources);
@@ -89,7 +88,7 @@ public class ProvisionProducer extends AbstractProducer {
                 anyTypeKind = getAnyTypeKind();
             }
 
-            List<PropagationTaskTO> tasks = getPropagationManager().getUpdateTasks(
+            List<PropagationTaskInfo> taskInfos = getPropagationManager().getUpdateTasks(
                     anyTypeKind,
                     key,
                     false,
@@ -97,10 +96,9 @@ public class ProvisionProducer extends AbstractProducer {
                     propByRes,
                     null,
                     null);
-            PropagationReporter propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+            PropagationReporter reporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
 
-            exchange.getOut().setBody(propagationReporter.getStatuses());
+            exchange.getOut().setBody(reporter.getStatuses());
         }
     }
-
 }
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/StatusProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/StatusProducer.java
index 96983a4..045f2e0 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/StatusProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/StatusProducer.java
@@ -21,13 +21,11 @@ package org.apache.syncope.core.provisioning.camel.producer;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.StatusPatch;
 import org.apache.syncope.common.lib.patch.UserPatch;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.common.lib.types.StatusPatchType;
@@ -36,6 +34,7 @@ import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
 
 public class StatusProducer extends AbstractProducer {
@@ -92,7 +91,7 @@ public class StatusProducer extends AbstractProducer {
 
             PropagationByResource propByRes = new PropagationByResource();
             propByRes.addAll(ResourceOperation.UPDATE, statusPatch.getResources());
-            List<PropagationTaskTO> tasks = getPropagationManager().getUpdateTasks(
+            List<PropagationTaskInfo> taskInfos = getPropagationManager().getUpdateTasks(
                     AnyTypeKind.USER,
                     statusPatch.getKey(),
                     false,
@@ -100,9 +99,9 @@ public class StatusProducer extends AbstractProducer {
                     propByRes,
                     null,
                     null);
-            PropagationReporter propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+            PropagationReporter reporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
 
-            exchange.getOut().setBody(Pair.of(updated.getResult(), propagationReporter.getStatuses()));
+            exchange.getOut().setBody(Pair.of(updated.getResult(), reporter.getStatuses()));
         }
     }
 
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/SuspendProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/SuspendProducer.java
index 27feae8..6ad268c 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/SuspendProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/SuspendProducer.java
@@ -24,9 +24,9 @@ import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.UserPatch;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 
 public class SuspendProducer extends AbstractProducer {
 
@@ -46,13 +46,12 @@ public class SuspendProducer extends AbstractProducer {
                 UserPatch userPatch = new UserPatch();
                 userPatch.setKey(updated.getKey().getResult());
 
-                List<PropagationTaskTO> tasks = getPropagationManager().getUserUpdateTasks(
+                List<PropagationTaskInfo> taskInfos = getPropagationManager().getUserUpdateTasks(
                         new WorkflowResult<>(
                                 Pair.of(userPatch, Boolean.FALSE),
                                 updated.getKey().getPropByRes(), updated.getKey().getPerformedTasks()));
-                getPropagationTaskExecutor().execute(tasks, false);
+                getPropagationTaskExecutor().execute(taskInfos, false);
             }
         }
     }
-
 }
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java
index f89182b..3cfa9ff 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java
@@ -20,17 +20,16 @@ package org.apache.syncope.core.provisioning.camel.producer;
 
 import java.util.List;
 import java.util.Set;
-
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.AnyObjectPatch;
 import org.apache.syncope.common.lib.patch.AnyPatch;
 import org.apache.syncope.common.lib.patch.UserPatch;
-import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 
 public class UpdateProducer extends AbstractProducer {
 
@@ -50,23 +49,21 @@ public class UpdateProducer extends AbstractProducer {
                 WorkflowResult<Pair<UserPatch, Boolean>> updated =
                         (WorkflowResult<Pair<UserPatch, Boolean>>) exchange.getIn().getBody();
 
-                List<PropagationTaskTO> tasks;
+                List<PropagationTaskInfo> taskInfos;
                 if (isPull()) {
                     boolean passwordNotNull = updated.getResult().getKey().getPassword() != null;
-                    tasks = getPropagationManager().getUserUpdateTasks(updated, passwordNotNull, excludedResources);
+                    taskInfos = getPropagationManager().getUserUpdateTasks(updated, passwordNotNull, excludedResources);
                 } else {
-                    tasks = getPropagationManager().getUserUpdateTasks(updated);
+                    taskInfos = getPropagationManager().getUserUpdateTasks(updated);
                 }
-                PropagationReporter propagationReporter =
-                        getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                PropagationReporter reporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
 
-                exchange.getOut().setBody(Pair.of(
-                        updated.getResult().getLeft(), propagationReporter.getStatuses()));
+                exchange.getOut().setBody(Pair.of(updated.getResult().getLeft(), reporter.getStatuses()));
             } else if (actual instanceof AnyPatch) {
                 WorkflowResult<? extends AnyPatch> updated =
                         (WorkflowResult<? extends AnyPatch>) exchange.getIn().getBody();
 
-                List<PropagationTaskTO> tasks = getPropagationManager().getUpdateTasks(
+                List<PropagationTaskInfo> taskInfos = getPropagationManager().getUpdateTasks(
                         actual instanceof AnyObjectPatch ? AnyTypeKind.ANY_OBJECT : AnyTypeKind.GROUP,
                         updated.getResult().getKey(),
                         false,
@@ -74,12 +71,10 @@ public class UpdateProducer extends AbstractProducer {
                         updated.getPropByRes(),
                         ((AnyPatch) actual).getVirAttrs(),
                         excludedResources);
-                PropagationReporter propagationReporter =
-                        getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
+                PropagationReporter reporter = getPropagationTaskExecutor().execute(taskInfos, nullPriorityAsync);
 
-                exchange.getOut().setBody(Pair.of(updated.getResult(), propagationReporter.getStatuses()));
+                exchange.getOut().setBody(Pair.of(updated.getResult(), reporter.getStatuses()));
             }
         }
     }
-
 }