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 2017/04/06 12:32:12 UTC

[1/2] syncope git commit: [SYNCOPE-1062] Better management of @Transactional with Propagation.REQUIRES_NEW for create / update / delete during pull

Repository: syncope
Updated Branches:
  refs/heads/2_0_X daa1d0d2f -> a2cbdac3a
  refs/heads/master 1830e9222 -> f0e0199fc


[SYNCOPE-1062] Better management of @Transactional with Propagation.REQUIRES_NEW for create / update / delete during pull


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/a2cbdac3
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/a2cbdac3
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/a2cbdac3

Branch: refs/heads/2_0_X
Commit: a2cbdac3a2bf625fe79134cfcc0a5417261d62fa
Parents: daa1d0d
Author: Francesco Chicchiricc� <il...@apache.org>
Authored: Thu Apr 6 14:31:46 2017 +0200
Committer: Francesco Chicchiricc� <il...@apache.org>
Committed: Thu Apr 6 14:31:46 2017 +0200

----------------------------------------------------------------------
 .../test/resources/domains/MasterContent.xml    |   4 +-
 .../api/AnyObjectProvisioningManager.java       |   5 +
 .../provisioning/api/ProvisioningManager.java   |   2 -
 .../DefaultAnyObjectProvisioningManager.java    |   5 +
 .../java/DefaultGroupProvisioningManager.java   |  16 ++-
 .../java/DefaultUserProvisioningManager.java    |  34 +++---
 .../provisioning/java/MappingManagerImpl.java   |   6 +-
 .../pushpull/AbstractPullResultHandler.java     |   2 +-
 .../AnyObjectPullResultHandlerImpl.java         |   4 +-
 .../pushpull/GroupPullResultHandlerImpl.java    |   3 +-
 .../CamelAnyObjectProvisioningManager.java      |   5 +
 .../camel/CamelGroupProvisioningManager.java    |  16 ++-
 .../camel/CamelUserProvisioningManager.java     |  12 +-
 fit/core-reference/pom.xml                      |  14 ++-
 .../org/apache/syncope/fit/AbstractITCase.java  |  29 +++++
 .../apache/syncope/fit/core/PullTaskITCase.java | 118 ++++++++++++++++++-
 16 files changed, 214 insertions(+), 61 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/a2cbdac3/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index e626c64..113dbba 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -1028,7 +1028,7 @@ under the License.
                mandatoryCondition="false" password="0" purpose="BOTH"/>
   <MappingItem id="a234bde8-b431-408c-8ec9-c986c5b7f98d" connObjectKey="0" mapping_id="cda910bd-fc28-4f63-890e-66edc62d428b"
                extAttrName="SURNAME" intAttrName="fullname" 
-               mandatoryCondition="false" password="0" purpose="BOTH"/>
+               mandatoryCondition="false" password="0" purpose="PULL"/>
   <MappingItem id="51a856de-ae25-41cd-967a-86920c834b70" connObjectKey="1" mapping_id="cda910bd-fc28-4f63-890e-66edc62d428b"
                extAttrName="ID" intAttrName="firstname" 
                mandatoryCondition="false" password="0" purpose="BOTH"/>
@@ -1040,7 +1040,7 @@ under the License.
                mandatoryCondition="false" password="0" purpose="BOTH"/>
   <MappingItem id="e7215305-93c9-460b-b862-46f7b60de72d" connObjectKey="0" mapping_id="cda910bd-fc28-4f63-890e-66edc62d428b"
                extAttrName="EMAIL" intAttrName="userId" 
-               mandatoryCondition="false" password="0" purpose="BOTH"/>
+               mandatoryCondition="false" password="0" purpose="PULL"/>
               
   <Provision id="6d7cb60a-c4ad-4a70-94ae-e5b88eb24930" resource_id="resource-db-virattr" anyType_id="USER" objectClass="__ACCOUNT__"/>
   <Mapping id="45280585-308f-4571-9788-c7a4734b3614" provision_id="6d7cb60a-c4ad-4a70-94ae-e5b88eb24930"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/a2cbdac3/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AnyObjectProvisioningManager.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AnyObjectProvisioningManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AnyObjectProvisioningManager.java
index 1670c68..33b1f60 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AnyObjectProvisioningManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AnyObjectProvisioningManager.java
@@ -20,11 +20,16 @@ package org.apache.syncope.core.provisioning.api;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.AnyObjectPatch;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.PropagationStatus;
 
 public interface AnyObjectProvisioningManager extends ProvisioningManager<AnyObjectTO, AnyObjectPatch> {
 
+    Pair<String, List<PropagationStatus>> create(
+            AnyObjectTO anyObjectTO, Set<String> excludedResources, boolean nullPriorityAsync);
+
     List<PropagationStatus> provision(String key, Collection<String> resources, boolean nullPriorityAsync);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a2cbdac3/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisioningManager.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisioningManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisioningManager.java
index cc8b9c1..0fabe15 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisioningManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisioningManager.java
@@ -30,8 +30,6 @@ public interface ProvisioningManager<T extends AnyTO, P extends AnyPatch> {
 
     Pair<String, List<PropagationStatus>> create(T anyTO, boolean nullPriorityAsync);
 
-    Pair<String, List<PropagationStatus>> create(T anyTO, Set<String> excludedResources, boolean nullPriorityAsync);
-
     Pair<String, List<PropagationStatus>> update(P patch, boolean nullPriorityAsync);
 
     Pair<String, List<PropagationStatus>> update(P patch, Set<String> excludedResources, boolean nullPriorityAsync);

http://git-wip-us.apache.org/repos/asf/syncope/blob/a2cbdac3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
----------------------------------------------------------------------
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 df550e8..780cd49 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
@@ -41,6 +41,8 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecu
 import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 import org.apache.syncope.core.workflow.api.AnyObjectWorkflowAdapter;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
 
 public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisioningManager {
 
@@ -66,6 +68,7 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
         return create(anyObjectTO, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public Pair<String, List<PropagationStatus>> create(
             final AnyObjectTO anyObjectTO, final Set<String> excludedResources, final boolean nullPriorityAsync) {
@@ -90,6 +93,7 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
         return update(anyObjectPatch, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public Pair<String, List<PropagationStatus>> update(
             final AnyObjectPatch anyObjectPatch, final Set<String> excludedResources, final boolean nullPriorityAsync) {
@@ -114,6 +118,7 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
         return delete(key, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public List<PropagationStatus> delete(
             final String key, final Set<String> excludedResources, final boolean nullPriorityAsync) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/a2cbdac3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java
----------------------------------------------------------------------
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 f0f8a8c..13f19a0 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
@@ -46,6 +46,8 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecu
 import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
 import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
 
 public class DefaultGroupProvisioningManager implements GroupProvisioningManager {
 
@@ -68,14 +70,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
     protected VirAttrHandler virtAttrHandler;
 
     @Override
-    public Pair<String, List<PropagationStatus>> create(final GroupTO group, final boolean nullPriorityAsync) {
-        return create(group, Collections.<String>emptySet(), nullPriorityAsync);
-    }
-
-    @Override
-    public Pair<String, List<PropagationStatus>> create(
-            final GroupTO groupTO, final Set<String> excludedResources, final boolean nullPriorityAsync) {
-
+    public Pair<String, List<PropagationStatus>> create(final GroupTO groupTO, final boolean nullPriorityAsync) {
         WorkflowResult<String> created = gwfAdapter.create(groupTO);
 
         List<PropagationTask> tasks = propagationManager.getCreateTasks(
@@ -83,12 +78,13 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
                 created.getResult(),
                 created.getPropByRes(),
                 groupTO.getVirAttrs(),
-                excludedResources);
+                Collections.<String>emptySet());
         PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
 
         return new ImmutablePair<>(created.getResult(), propagationReporter.getStatuses());
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public Pair<String, List<PropagationStatus>> create(
             final GroupTO groupTO,
@@ -120,6 +116,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
         return update(groupPatch, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public Pair<String, List<PropagationStatus>> update(
             final GroupPatch groupPatch, final Set<String> excludedResources, final boolean nullPriorityAsync) {
@@ -144,6 +141,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
         return delete(key, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public List<PropagationStatus> delete(
             final String key, final Set<String> excludedResources, final boolean nullPriorityAsync) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/a2cbdac3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
----------------------------------------------------------------------
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 ba2481e..ae1090f 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
@@ -53,6 +53,8 @@ import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
 
 public class DefaultUserProvisioningManager implements UserProvisioningManager {
 
@@ -85,13 +87,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         return create(userTO, storePassword, false, null, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
-    @Override
-    public Pair<String, List<PropagationStatus>> create(
-            final UserTO userTO, final Set<String> excludedResources, final boolean nullPriorityAsync) {
-
-        return create(userTO, false, false, null, excludedResources, nullPriorityAsync);
-    }
-
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public Pair<String, List<PropagationStatus>> create(
             final UserTO userTO,
@@ -105,15 +101,15 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
                 uwfAdapter.create(userTO, disablePwdPolicyCheck, enabled, storePassword);
 
         List<PropagationTask> tasks = propagationManager.getUserCreateTasks(
-                created.getResult().getKey(),
+                created.getResult().getLeft(),
                 userTO.getPassword(),
-                created.getResult().getValue(),
+                created.getResult().getRight(),
                 created.getPropByRes(),
                 userTO.getVirAttrs(),
                 excludedResources);
         PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
 
-        return new ImmutablePair<>(created.getResult().getKey(), propagationReporter.getStatuses());
+        return new ImmutablePair<>(created.getResult().getLeft(), propagationReporter.getStatuses());
     }
 
     @Override
@@ -123,7 +119,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(updated);
         PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
 
-        return new ImmutablePair<>(updated.getResult().getKey().getKey(), propagationReporter.getStatuses());
+        return new ImmutablePair<>(updated.getResult().getLeft().getKey(), propagationReporter.getStatuses());
     }
 
     @Override
@@ -133,6 +129,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         return update(userPatch, new ProvisioningReport(), null, excludedResources, nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public Pair<String, List<PropagationStatus>> update(
             final UserPatch userPatch,
@@ -178,10 +175,10 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         }
 
         List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(
-                updated, updated.getResult().getKey().getPassword() != null, excludedResources);
+                updated, updated.getResult().getLeft().getPassword() != null, excludedResources);
         PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
 
-        return new ImmutablePair<>(updated.getResult().getKey().getKey(), propagationReporter.getStatuses());
+        return new ImmutablePair<>(updated.getResult().getLeft().getKey(), propagationReporter.getStatuses());
     }
 
     @Override
@@ -189,6 +186,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         return delete(key, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public List<PropagationStatus> delete(
             final String key, final Set<String> excludedResources, final boolean nullPriorityAsync) {
@@ -220,12 +218,12 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
     @Override
     public String unlink(final UserPatch userPatch) {
         WorkflowResult<Pair<UserPatch, Boolean>> updated = uwfAdapter.update(userPatch);
-        return updated.getResult().getKey().getKey();
+        return updated.getResult().getLeft().getKey();
     }
 
     @Override
     public String link(final UserPatch userPatch) {
-        return uwfAdapter.update(userPatch).getResult().getKey().getKey();
+        return uwfAdapter.update(userPatch).getResult().getLeft().getKey();
     }
 
     @Override
@@ -284,14 +282,14 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         Pair<WorkflowResult<String>, Boolean> updated = uwfAdapter.internalSuspend(key);
 
         // propagate suspension if and only if it is required by policy
-        if (updated != null && updated.getValue()) {
+        if (updated != null && updated.getRight()) {
             UserPatch userPatch = new UserPatch();
-            userPatch.setKey(updated.getKey().getResult());
+            userPatch.setKey(updated.getLeft().getResult());
 
             List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(
                     new WorkflowResult<Pair<UserPatch, Boolean>>(
                             new ImmutablePair<>(userPatch, Boolean.FALSE),
-                            updated.getKey().getPropByRes(), updated.getKey().getPerformedTasks()));
+                            updated.getLeft().getPropByRes(), updated.getLeft().getPerformedTasks()));
             taskExecutor.execute(tasks);
         }
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a2cbdac3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
index 85392c4..7e5e9a8 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
@@ -263,7 +263,7 @@ public class MappingManagerImpl implements MappingManager {
             }
 
             if (mapItem.isConnObjectKey()) {
-                result = new ImmutablePair<>(objValues.iterator().next().toString(), null);
+                result = new ImmutablePair<>(objValues.isEmpty() ? null : objValues.iterator().next().toString(), null);
             } else if (mapItem.isPassword() && any instanceof User) {
                 String passwordAttrValue = password;
                 if (StringUtils.isBlank(passwordAttrValue)) {
@@ -295,8 +295,8 @@ public class MappingManagerImpl implements MappingManager {
             } else {
                 result = new ImmutablePair<>(
                         null, objValues.isEmpty()
-                                ? AttributeBuilder.build(mapItem.getExtAttrName())
-                                : AttributeBuilder.build(mapItem.getExtAttrName(), objValues.iterator().next()));
+                        ? AttributeBuilder.build(mapItem.getExtAttrName())
+                        : AttributeBuilder.build(mapItem.getExtAttrName(), objValues.iterator().next()));
             }
         }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/a2cbdac3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
index 6478f64..e487986 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
@@ -97,7 +97,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
             LOG.error("Could not propagate anyObject " + key, e);
         }
 
-        getProvisioningManager().delete(key, true);
+        getProvisioningManager().delete(key, Collections.singleton(profile.getTask().getResource().getKey()), true);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/a2cbdac3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
index 2fd67c5..025b7e9 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
@@ -101,8 +101,8 @@ public class AnyObjectPullResultHandlerImpl extends AbstractPullResultHandler im
 
         AnyObjectPatch anyObjectPatch = AnyObjectPatch.class.cast(anyPatch);
 
-        Map.Entry<String, List<PropagationStatus>> updated =
-                anyObjectProvisioningManager.update(anyObjectPatch, true);
+        Map.Entry<String, List<PropagationStatus>> updated = anyObjectProvisioningManager.update(
+                anyObjectPatch, Collections.singleton(profile.getTask().getResource().getKey()), true);
 
         AnyObjectTO after = anyObjectDataBinder.getAnyObjectTO(updated.getKey());
         result.setName(getName(after));

http://git-wip-us.apache.org/repos/asf/syncope/blob/a2cbdac3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
index 83917e0..dc1613f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
@@ -114,7 +114,8 @@ public class GroupPullResultHandlerImpl extends AbstractPullResultHandler implem
 
         GroupPatch groupPatch = GroupPatch.class.cast(anyPatch);
 
-        Map.Entry<String, List<PropagationStatus>> updated = groupProvisioningManager.update(groupPatch, true);
+        Map.Entry<String, List<PropagationStatus>> updated = groupProvisioningManager.update(
+                groupPatch, Collections.singleton(profile.getTask().getResource().getKey()), true);
 
         String groupOwner = null;
         for (AttrPatch attrPatch : groupPatch.getPlainAttrs()) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/a2cbdac3/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelAnyObjectProvisioningManager.java
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelAnyObjectProvisioningManager.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelAnyObjectProvisioningManager.java
index 0a51ce9..56783f7 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelAnyObjectProvisioningManager.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelAnyObjectProvisioningManager.java
@@ -31,6 +31,8 @@ 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.core.provisioning.api.AnyObjectProvisioningManager;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
 
 public class CamelAnyObjectProvisioningManager
         extends AbstractCamelProvisioningManager implements AnyObjectProvisioningManager {
@@ -40,6 +42,7 @@ public class CamelAnyObjectProvisioningManager
         return create(any, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public Pair<String, List<PropagationStatus>> create(
@@ -69,6 +72,7 @@ public class CamelAnyObjectProvisioningManager
         return update(anyPatch, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public Pair<String, List<PropagationStatus>> update(
@@ -96,6 +100,7 @@ public class CamelAnyObjectProvisioningManager
         return delete(anyObjectObjectKey, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public List<PropagationStatus> delete(

http://git-wip-us.apache.org/repos/asf/syncope/blob/a2cbdac3/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelGroupProvisioningManager.java
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelGroupProvisioningManager.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelGroupProvisioningManager.java
index d3745a1..c6feb91 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelGroupProvisioningManager.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelGroupProvisioningManager.java
@@ -31,24 +31,19 @@ import org.apache.syncope.common.lib.patch.GroupPatch;
 import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
 
 public class CamelGroupProvisioningManager
         extends AbstractCamelProvisioningManager implements GroupProvisioningManager {
 
     @Override
-    public Pair<String, List<PropagationStatus>> create(final GroupTO any, final boolean nullPriorityAsync) {
-        return create(any, Collections.<String>emptySet(), nullPriorityAsync);
-    }
-
-    @Override
     @SuppressWarnings("unchecked")
-    public Pair<String, List<PropagationStatus>> create(
-            final GroupTO groupTO, final Set<String> excludedResources, final boolean nullPriorityAsync) {
-
+    public Pair<String, List<PropagationStatus>> create(final GroupTO groupTO, final boolean nullPriorityAsync) {
         PollingConsumer pollingConsumer = getConsumer("direct:createGroupPort");
 
         Map<String, Object> props = new HashMap<>();
-        props.put("excludedResources", excludedResources);
+        props.put("excludedResources", Collections.<String>emptySet());
         props.put("nullPriorityAsync", nullPriorityAsync);
 
         sendMessage("direct:createGroup", groupTO, props);
@@ -62,6 +57,7 @@ public class CamelGroupProvisioningManager
         return exchange.getIn().getBody(Pair.class);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public Pair<String, List<PropagationStatus>> create(
@@ -93,6 +89,7 @@ public class CamelGroupProvisioningManager
         return update(anyPatch, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public Pair<String, List<PropagationStatus>> update(
@@ -120,6 +117,7 @@ public class CamelGroupProvisioningManager
         return delete(key, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public List<PropagationStatus> delete(

http://git-wip-us.apache.org/repos/asf/syncope/blob/a2cbdac3/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelUserProvisioningManager.java
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelUserProvisioningManager.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelUserProvisioningManager.java
index a9f17c8..d5499da 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelUserProvisioningManager.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelUserProvisioningManager.java
@@ -39,6 +39,8 @@ import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
 
 public class CamelUserProvisioningManager extends AbstractCamelProvisioningManager implements UserProvisioningManager {
 
@@ -56,13 +58,7 @@ public class CamelUserProvisioningManager extends AbstractCamelProvisioningManag
         return create(userTO, storePassword, false, null, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
-    @Override
-    public Pair<String, List<PropagationStatus>> create(
-            final UserTO userTO, final Set<String> excludedResources, final boolean nullPriorityAsync) {
-
-        return create(userTO, false, false, null, excludedResources, nullPriorityAsync);
-    }
-
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public Pair<String, List<PropagationStatus>> create(
@@ -124,6 +120,7 @@ public class CamelUserProvisioningManager extends AbstractCamelProvisioningManag
         return delete(key, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public List<PropagationStatus> delete(
@@ -317,6 +314,7 @@ public class CamelUserProvisioningManager extends AbstractCamelProvisioningManag
         return exchange.getIn().getBody(List.class);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public Pair<String, List<PropagationStatus>> update(

http://git-wip-us.apache.org/repos/asf/syncope/blob/a2cbdac3/fit/core-reference/pom.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index 8b37843..b041941 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -153,7 +153,19 @@ under the License.
       <artifactId>syncope-ext-camel-client-console</artifactId>
       <version>${project.version}</version>
       <scope>test</scope>
-    </dependency>    
+    </dependency>  
+    <dependency>
+      <groupId>org.apache.syncope.ext.saml2sp</groupId>
+      <artifactId>syncope-ext-saml2sp-rest-cxf</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>          
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.saml2sp</groupId>
+      <artifactId>syncope-ext-saml2sp-client-console</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>  
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-test</artifactId>

http://git-wip-us.apache.org/repos/asf/syncope/blob/a2cbdac3/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 4c06967..86e9c74 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -28,11 +28,16 @@ import java.util.Properties;
 import java.util.UUID;
 import javax.naming.Context;
 import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.DirContext;
 import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.ModificationItem;
 import javax.ws.rs.core.GenericType;
 import javax.ws.rs.core.Response;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.cxf.jaxrs.client.WebClient;
 import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
@@ -459,4 +464,28 @@ public abstract class AbstractITCase {
             }
         }
     }
+
+    protected void updateLdapRemoteObject(
+            final String bindDn, final String bindPwd, final String objectDn, final Pair<String, String> attribute) {
+
+        InitialDirContext ctx = null;
+        try {
+            ctx = getLdapResourceDirContext(bindDn, bindPwd);
+
+            Attribute ldapAttribute = new BasicAttribute(attribute.getKey(), attribute.getValue());
+            ModificationItem[] item = new ModificationItem[1];
+            item[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, ldapAttribute);
+            ctx.modifyAttributes(objectDn, item);
+        } catch (Exception e) {
+            // ignore
+        } finally {
+            if (ctx != null) {
+                try {
+                    ctx.close();
+                } catch (NamingException e) {
+                    // ignore
+                }
+            }
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a2cbdac3/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
index d157d78..2761f66 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
@@ -358,8 +358,9 @@ public class PullTaskITCase extends AbstractTaskITCase {
         }
         PagedResult<UserTO> matchingUsers = userService.search(
                 new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
-                fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("pullFromLDAP").query()).
-                build());
+                        fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("pullFromLDAP").
+                                query()).
+                        build());
         if (matchingUsers.getSize() > 0) {
             for (UserTO user : matchingUsers.getResult()) {
                 DeassociationPatch deassociationPatch = new DeassociationPatch();
@@ -398,8 +399,9 @@ public class PullTaskITCase extends AbstractTaskITCase {
         // 3. verify that pulled user is found
         PagedResult<UserTO> matchingUsers = userService.search(
                 new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
-                fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("pullFromLDAP").query()).
-                build());
+                        fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("pullFromLDAP").
+                                query()).
+                        build());
         assertNotNull(matchingUsers);
         assertEquals(1, matchingUsers.getResult().size());
         // SYNCOPE-898
@@ -497,8 +499,8 @@ public class PullTaskITCase extends AbstractTaskITCase {
             // 3. unlink any existing printer and delete from Syncope (printer is now only on external resource)
             PagedResult<AnyObjectTO> matchingPrinters = anyObjectService.search(
                     new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
-                    fiql(SyncopeClient.getAnyObjectSearchConditionBuilder("PRINTER").
-                            is("location").equalTo("pull*").query()).build());
+                            fiql(SyncopeClient.getAnyObjectSearchConditionBuilder("PRINTER").
+                                    is("location").equalTo("pull*").query()).build());
             assertTrue(matchingPrinters.getSize() > 0);
             for (AnyObjectTO printer : matchingPrinters.getResult()) {
                 DeassociationPatch deassociationPatch = new DeassociationPatch();
@@ -1056,6 +1058,8 @@ public class PullTaskITCase extends AbstractTaskITCase {
             // 7. Test the pulled user
             self = clientFactory.create(user.getUsername(), oldCleanPassword).self();
             assertNotNull(self);
+        } catch (Exception e) {
+            fail(e.getMessage());
         } finally {
             // Delete PullTask + user + reset the connector
             if (pullTask != null) {
@@ -1073,4 +1077,106 @@ public class PullTaskITCase extends AbstractTaskITCase {
             }
         }
     }
+
+    @Test
+    public void issueSYNCOPE1062() {
+        GroupTO propagationGroup = null;
+        PullTaskTO pullTask = null;
+        UserTO user = null;
+        GroupTO group = null;
+        try {
+            // 1. create group with resource for propagation
+            propagationGroup = GroupITCase.getBasicSampleTO("SYNCOPE1062");
+            propagationGroup.getResources().add(RESOURCE_NAME_DBPULL);
+            propagationGroup = createGroup(propagationGroup).getEntity();
+
+            // 2. create pull task for another resource, with user template assigning the group above
+            pullTask = new PullTaskTO();
+            pullTask.setDestinationRealm(SyncopeConstants.ROOT_REALM);
+            pullTask.setName("SYNCOPE1062");
+            pullTask.setActive(true);
+            pullTask.setPerformCreate(true);
+            pullTask.setPerformUpdate(true);
+            pullTask.setPullMode(PullMode.FULL_RECONCILIATION);
+            pullTask.setResource(RESOURCE_NAME_LDAP);
+
+            UserTO template = new UserTO();
+            template.getAuxClasses().add("minimal group");
+            template.getMemberships().add(new MembershipTO.Builder().group(propagationGroup.getKey()).build());
+            template.getPlainAttrs().add(attrTO("firstname", "'fixed'"));
+            pullTask.getTemplates().put(AnyTypeKind.USER.name(), template);
+
+            Response taskResponse = taskService.create(pullTask);
+            pullTask = getObject(taskResponse.getLocation(), TaskService.class, PullTaskTO.class);
+            assertNotNull(pullTask);
+            assertFalse(pullTask.getTemplates().isEmpty());
+
+            // 3. exec the pull task
+            ExecTO execution = execProvisioningTask(taskService, pullTask.getKey(), 50, false);
+            assertEquals(PropagationTaskExecStatus.SUCCESS, PropagationTaskExecStatus.valueOf(execution.getStatus()));
+
+            // the user is successfully pulled...
+            user = userService.read("pullFromLDAP");
+            assertNotNull(user);
+            assertEquals("pullFromLDAP@syncope.apache.org", user.getPlainAttrMap().get("email").getValues().get(0));
+
+            group = groupService.read("testLDAPGroup");
+            assertNotNull(group);
+
+            ConnObjectTO connObject =
+                    resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), user.getKey());
+            assertNotNull(connObject);
+            assertEquals("pullFromLDAP@syncope.apache.org", connObject.getAttrMap().get("mail").getValues().get(0));
+            AttrTO userDn = connObject.getAttrMap().get(Name.NAME);
+            assertNotNull(userDn);
+            assertEquals(1, userDn.getValues().size());
+            assertNotNull(
+                    getLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD, userDn.getValues().get(0)));
+
+            // ...and propagated
+            JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+            String email = jdbcTemplate.queryForObject(
+                    "SELECT EMAIL FROM TESTPULL WHERE USERNAME=?", String.class, user.getUsername());
+            assertEquals(user.getPlainAttrMap().get("email").getValues().get(0), email);
+
+            // 4. update the user on the external resource
+            updateLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD,
+                    userDn.getValues().get(0), Pair.of("mail", "pullFromLDAP2@syncope.apache.org"));
+
+            connObject = resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), user.getKey());
+            assertNotNull(connObject);
+            assertEquals("pullFromLDAP2@syncope.apache.org", connObject.getAttrMap().get("mail").getValues().get(0));
+
+            // 5. exec the pull task again
+            execution = execProvisioningTask(taskService, pullTask.getKey(), 50, false);
+            assertEquals(PropagationTaskExecStatus.SUCCESS, PropagationTaskExecStatus.valueOf(execution.getStatus()));
+
+            // the internal is updated...
+            user = userService.read("pullFromLDAP");
+            assertNotNull(user);
+            assertEquals("pullFromLDAP2@syncope.apache.org", user.getPlainAttrMap().get("email").getValues().get(0));
+
+            // ...and propagated
+            email = jdbcTemplate.queryForObject(
+                    "SELECT EMAIL FROM TESTPULL WHERE USERNAME=?", String.class, user.getUsername());
+            assertEquals(user.getPlainAttrMap().get("email").getValues().get(0), email);
+        } catch (Exception e) {
+            fail(e.getMessage());
+        } finally {
+            if (pullTask != null) {
+                taskService.delete(pullTask.getKey());
+            }
+
+            if (propagationGroup != null) {
+                groupService.delete(propagationGroup.getKey());
+            }
+
+            if (group != null) {
+                groupService.delete(group.getKey());
+            }
+            if (user != null) {
+                userService.delete(user.getKey());
+            }
+        }
+    }
 }


[2/2] syncope git commit: [SYNCOPE-1062] Better management of @Transactional with Propagation.REQUIRES_NEW for create / update / delete during pull

Posted by il...@apache.org.
[SYNCOPE-1062] Better management of @Transactional with Propagation.REQUIRES_NEW for create / update / delete during pull


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/f0e0199f
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/f0e0199f
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/f0e0199f

Branch: refs/heads/master
Commit: f0e0199fc70774bcab8e2bfb0304cf0a05d15728
Parents: 1830e92
Author: Francesco Chicchiricc� <il...@apache.org>
Authored: Thu Apr 6 14:31:46 2017 +0200
Committer: Francesco Chicchiricc� <il...@apache.org>
Committed: Thu Apr 6 14:32:01 2017 +0200

----------------------------------------------------------------------
 .../test/resources/domains/MasterContent.xml    |   4 +-
 .../api/AnyObjectProvisioningManager.java       |   5 +
 .../provisioning/api/ProvisioningManager.java   |   2 -
 .../DefaultAnyObjectProvisioningManager.java    |   5 +
 .../java/DefaultGroupProvisioningManager.java   |  16 ++-
 .../java/DefaultUserProvisioningManager.java    |  34 +++---
 .../provisioning/java/MappingManagerImpl.java   |   6 +-
 .../pushpull/AbstractPullResultHandler.java     |   2 +-
 .../AnyObjectPullResultHandlerImpl.java         |   4 +-
 .../pushpull/GroupPullResultHandlerImpl.java    |   3 +-
 .../CamelAnyObjectProvisioningManager.java      |   5 +
 .../camel/CamelGroupProvisioningManager.java    |  16 ++-
 .../camel/CamelUserProvisioningManager.java     |  12 +-
 fit/core-reference/pom.xml                      |  14 ++-
 .../org/apache/syncope/fit/AbstractITCase.java  |  29 +++++
 .../apache/syncope/fit/core/PullTaskITCase.java | 118 ++++++++++++++++++-
 16 files changed, 214 insertions(+), 61 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/f0e0199f/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index e626c64..113dbba 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -1028,7 +1028,7 @@ under the License.
                mandatoryCondition="false" password="0" purpose="BOTH"/>
   <MappingItem id="a234bde8-b431-408c-8ec9-c986c5b7f98d" connObjectKey="0" mapping_id="cda910bd-fc28-4f63-890e-66edc62d428b"
                extAttrName="SURNAME" intAttrName="fullname" 
-               mandatoryCondition="false" password="0" purpose="BOTH"/>
+               mandatoryCondition="false" password="0" purpose="PULL"/>
   <MappingItem id="51a856de-ae25-41cd-967a-86920c834b70" connObjectKey="1" mapping_id="cda910bd-fc28-4f63-890e-66edc62d428b"
                extAttrName="ID" intAttrName="firstname" 
                mandatoryCondition="false" password="0" purpose="BOTH"/>
@@ -1040,7 +1040,7 @@ under the License.
                mandatoryCondition="false" password="0" purpose="BOTH"/>
   <MappingItem id="e7215305-93c9-460b-b862-46f7b60de72d" connObjectKey="0" mapping_id="cda910bd-fc28-4f63-890e-66edc62d428b"
                extAttrName="EMAIL" intAttrName="userId" 
-               mandatoryCondition="false" password="0" purpose="BOTH"/>
+               mandatoryCondition="false" password="0" purpose="PULL"/>
               
   <Provision id="6d7cb60a-c4ad-4a70-94ae-e5b88eb24930" resource_id="resource-db-virattr" anyType_id="USER" objectClass="__ACCOUNT__"/>
   <Mapping id="45280585-308f-4571-9788-c7a4734b3614" provision_id="6d7cb60a-c4ad-4a70-94ae-e5b88eb24930"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f0e0199f/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AnyObjectProvisioningManager.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AnyObjectProvisioningManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AnyObjectProvisioningManager.java
index 1670c68..33b1f60 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AnyObjectProvisioningManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AnyObjectProvisioningManager.java
@@ -20,11 +20,16 @@ package org.apache.syncope.core.provisioning.api;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.AnyObjectPatch;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.PropagationStatus;
 
 public interface AnyObjectProvisioningManager extends ProvisioningManager<AnyObjectTO, AnyObjectPatch> {
 
+    Pair<String, List<PropagationStatus>> create(
+            AnyObjectTO anyObjectTO, Set<String> excludedResources, boolean nullPriorityAsync);
+
     List<PropagationStatus> provision(String key, Collection<String> resources, boolean nullPriorityAsync);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/f0e0199f/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisioningManager.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisioningManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisioningManager.java
index cc8b9c1..0fabe15 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisioningManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisioningManager.java
@@ -30,8 +30,6 @@ public interface ProvisioningManager<T extends AnyTO, P extends AnyPatch> {
 
     Pair<String, List<PropagationStatus>> create(T anyTO, boolean nullPriorityAsync);
 
-    Pair<String, List<PropagationStatus>> create(T anyTO, Set<String> excludedResources, boolean nullPriorityAsync);
-
     Pair<String, List<PropagationStatus>> update(P patch, boolean nullPriorityAsync);
 
     Pair<String, List<PropagationStatus>> update(P patch, Set<String> excludedResources, boolean nullPriorityAsync);

http://git-wip-us.apache.org/repos/asf/syncope/blob/f0e0199f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
----------------------------------------------------------------------
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 df550e8..780cd49 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
@@ -41,6 +41,8 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecu
 import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 import org.apache.syncope.core.workflow.api.AnyObjectWorkflowAdapter;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
 
 public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisioningManager {
 
@@ -66,6 +68,7 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
         return create(anyObjectTO, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public Pair<String, List<PropagationStatus>> create(
             final AnyObjectTO anyObjectTO, final Set<String> excludedResources, final boolean nullPriorityAsync) {
@@ -90,6 +93,7 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
         return update(anyObjectPatch, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public Pair<String, List<PropagationStatus>> update(
             final AnyObjectPatch anyObjectPatch, final Set<String> excludedResources, final boolean nullPriorityAsync) {
@@ -114,6 +118,7 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
         return delete(key, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public List<PropagationStatus> delete(
             final String key, final Set<String> excludedResources, final boolean nullPriorityAsync) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/f0e0199f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java
----------------------------------------------------------------------
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 f0f8a8c..13f19a0 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
@@ -46,6 +46,8 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecu
 import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
 import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
 
 public class DefaultGroupProvisioningManager implements GroupProvisioningManager {
 
@@ -68,14 +70,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
     protected VirAttrHandler virtAttrHandler;
 
     @Override
-    public Pair<String, List<PropagationStatus>> create(final GroupTO group, final boolean nullPriorityAsync) {
-        return create(group, Collections.<String>emptySet(), nullPriorityAsync);
-    }
-
-    @Override
-    public Pair<String, List<PropagationStatus>> create(
-            final GroupTO groupTO, final Set<String> excludedResources, final boolean nullPriorityAsync) {
-
+    public Pair<String, List<PropagationStatus>> create(final GroupTO groupTO, final boolean nullPriorityAsync) {
         WorkflowResult<String> created = gwfAdapter.create(groupTO);
 
         List<PropagationTask> tasks = propagationManager.getCreateTasks(
@@ -83,12 +78,13 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
                 created.getResult(),
                 created.getPropByRes(),
                 groupTO.getVirAttrs(),
-                excludedResources);
+                Collections.<String>emptySet());
         PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
 
         return new ImmutablePair<>(created.getResult(), propagationReporter.getStatuses());
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public Pair<String, List<PropagationStatus>> create(
             final GroupTO groupTO,
@@ -120,6 +116,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
         return update(groupPatch, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public Pair<String, List<PropagationStatus>> update(
             final GroupPatch groupPatch, final Set<String> excludedResources, final boolean nullPriorityAsync) {
@@ -144,6 +141,7 @@ public class DefaultGroupProvisioningManager implements GroupProvisioningManager
         return delete(key, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public List<PropagationStatus> delete(
             final String key, final Set<String> excludedResources, final boolean nullPriorityAsync) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/f0e0199f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
----------------------------------------------------------------------
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 ba2481e..ae1090f 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
@@ -53,6 +53,8 @@ import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
 
 public class DefaultUserProvisioningManager implements UserProvisioningManager {
 
@@ -85,13 +87,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         return create(userTO, storePassword, false, null, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
-    @Override
-    public Pair<String, List<PropagationStatus>> create(
-            final UserTO userTO, final Set<String> excludedResources, final boolean nullPriorityAsync) {
-
-        return create(userTO, false, false, null, excludedResources, nullPriorityAsync);
-    }
-
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public Pair<String, List<PropagationStatus>> create(
             final UserTO userTO,
@@ -105,15 +101,15 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
                 uwfAdapter.create(userTO, disablePwdPolicyCheck, enabled, storePassword);
 
         List<PropagationTask> tasks = propagationManager.getUserCreateTasks(
-                created.getResult().getKey(),
+                created.getResult().getLeft(),
                 userTO.getPassword(),
-                created.getResult().getValue(),
+                created.getResult().getRight(),
                 created.getPropByRes(),
                 userTO.getVirAttrs(),
                 excludedResources);
         PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
 
-        return new ImmutablePair<>(created.getResult().getKey(), propagationReporter.getStatuses());
+        return new ImmutablePair<>(created.getResult().getLeft(), propagationReporter.getStatuses());
     }
 
     @Override
@@ -123,7 +119,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(updated);
         PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
 
-        return new ImmutablePair<>(updated.getResult().getKey().getKey(), propagationReporter.getStatuses());
+        return new ImmutablePair<>(updated.getResult().getLeft().getKey(), propagationReporter.getStatuses());
     }
 
     @Override
@@ -133,6 +129,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         return update(userPatch, new ProvisioningReport(), null, excludedResources, nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public Pair<String, List<PropagationStatus>> update(
             final UserPatch userPatch,
@@ -178,10 +175,10 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         }
 
         List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(
-                updated, updated.getResult().getKey().getPassword() != null, excludedResources);
+                updated, updated.getResult().getLeft().getPassword() != null, excludedResources);
         PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
 
-        return new ImmutablePair<>(updated.getResult().getKey().getKey(), propagationReporter.getStatuses());
+        return new ImmutablePair<>(updated.getResult().getLeft().getKey(), propagationReporter.getStatuses());
     }
 
     @Override
@@ -189,6 +186,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         return delete(key, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public List<PropagationStatus> delete(
             final String key, final Set<String> excludedResources, final boolean nullPriorityAsync) {
@@ -220,12 +218,12 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
     @Override
     public String unlink(final UserPatch userPatch) {
         WorkflowResult<Pair<UserPatch, Boolean>> updated = uwfAdapter.update(userPatch);
-        return updated.getResult().getKey().getKey();
+        return updated.getResult().getLeft().getKey();
     }
 
     @Override
     public String link(final UserPatch userPatch) {
-        return uwfAdapter.update(userPatch).getResult().getKey().getKey();
+        return uwfAdapter.update(userPatch).getResult().getLeft().getKey();
     }
 
     @Override
@@ -284,14 +282,14 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
         Pair<WorkflowResult<String>, Boolean> updated = uwfAdapter.internalSuspend(key);
 
         // propagate suspension if and only if it is required by policy
-        if (updated != null && updated.getValue()) {
+        if (updated != null && updated.getRight()) {
             UserPatch userPatch = new UserPatch();
-            userPatch.setKey(updated.getKey().getResult());
+            userPatch.setKey(updated.getLeft().getResult());
 
             List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(
                     new WorkflowResult<Pair<UserPatch, Boolean>>(
                             new ImmutablePair<>(userPatch, Boolean.FALSE),
-                            updated.getKey().getPropByRes(), updated.getKey().getPerformedTasks()));
+                            updated.getLeft().getPropByRes(), updated.getLeft().getPerformedTasks()));
             taskExecutor.execute(tasks);
         }
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/f0e0199f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
index 85392c4..7e5e9a8 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
@@ -263,7 +263,7 @@ public class MappingManagerImpl implements MappingManager {
             }
 
             if (mapItem.isConnObjectKey()) {
-                result = new ImmutablePair<>(objValues.iterator().next().toString(), null);
+                result = new ImmutablePair<>(objValues.isEmpty() ? null : objValues.iterator().next().toString(), null);
             } else if (mapItem.isPassword() && any instanceof User) {
                 String passwordAttrValue = password;
                 if (StringUtils.isBlank(passwordAttrValue)) {
@@ -295,8 +295,8 @@ public class MappingManagerImpl implements MappingManager {
             } else {
                 result = new ImmutablePair<>(
                         null, objValues.isEmpty()
-                                ? AttributeBuilder.build(mapItem.getExtAttrName())
-                                : AttributeBuilder.build(mapItem.getExtAttrName(), objValues.iterator().next()));
+                        ? AttributeBuilder.build(mapItem.getExtAttrName())
+                        : AttributeBuilder.build(mapItem.getExtAttrName(), objValues.iterator().next()));
             }
         }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/f0e0199f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
index 6478f64..e487986 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
@@ -97,7 +97,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
             LOG.error("Could not propagate anyObject " + key, e);
         }
 
-        getProvisioningManager().delete(key, true);
+        getProvisioningManager().delete(key, Collections.singleton(profile.getTask().getResource().getKey()), true);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/f0e0199f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
index 2fd67c5..025b7e9 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
@@ -101,8 +101,8 @@ public class AnyObjectPullResultHandlerImpl extends AbstractPullResultHandler im
 
         AnyObjectPatch anyObjectPatch = AnyObjectPatch.class.cast(anyPatch);
 
-        Map.Entry<String, List<PropagationStatus>> updated =
-                anyObjectProvisioningManager.update(anyObjectPatch, true);
+        Map.Entry<String, List<PropagationStatus>> updated = anyObjectProvisioningManager.update(
+                anyObjectPatch, Collections.singleton(profile.getTask().getResource().getKey()), true);
 
         AnyObjectTO after = anyObjectDataBinder.getAnyObjectTO(updated.getKey());
         result.setName(getName(after));

http://git-wip-us.apache.org/repos/asf/syncope/blob/f0e0199f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
index 83917e0..dc1613f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
@@ -114,7 +114,8 @@ public class GroupPullResultHandlerImpl extends AbstractPullResultHandler implem
 
         GroupPatch groupPatch = GroupPatch.class.cast(anyPatch);
 
-        Map.Entry<String, List<PropagationStatus>> updated = groupProvisioningManager.update(groupPatch, true);
+        Map.Entry<String, List<PropagationStatus>> updated = groupProvisioningManager.update(
+                groupPatch, Collections.singleton(profile.getTask().getResource().getKey()), true);
 
         String groupOwner = null;
         for (AttrPatch attrPatch : groupPatch.getPlainAttrs()) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/f0e0199f/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelAnyObjectProvisioningManager.java
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelAnyObjectProvisioningManager.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelAnyObjectProvisioningManager.java
index 0a51ce9..56783f7 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelAnyObjectProvisioningManager.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelAnyObjectProvisioningManager.java
@@ -31,6 +31,8 @@ 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.core.provisioning.api.AnyObjectProvisioningManager;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
 
 public class CamelAnyObjectProvisioningManager
         extends AbstractCamelProvisioningManager implements AnyObjectProvisioningManager {
@@ -40,6 +42,7 @@ public class CamelAnyObjectProvisioningManager
         return create(any, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public Pair<String, List<PropagationStatus>> create(
@@ -69,6 +72,7 @@ public class CamelAnyObjectProvisioningManager
         return update(anyPatch, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public Pair<String, List<PropagationStatus>> update(
@@ -96,6 +100,7 @@ public class CamelAnyObjectProvisioningManager
         return delete(anyObjectObjectKey, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public List<PropagationStatus> delete(

http://git-wip-us.apache.org/repos/asf/syncope/blob/f0e0199f/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelGroupProvisioningManager.java
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelGroupProvisioningManager.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelGroupProvisioningManager.java
index d3745a1..c6feb91 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelGroupProvisioningManager.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelGroupProvisioningManager.java
@@ -31,24 +31,19 @@ import org.apache.syncope.common.lib.patch.GroupPatch;
 import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
 
 public class CamelGroupProvisioningManager
         extends AbstractCamelProvisioningManager implements GroupProvisioningManager {
 
     @Override
-    public Pair<String, List<PropagationStatus>> create(final GroupTO any, final boolean nullPriorityAsync) {
-        return create(any, Collections.<String>emptySet(), nullPriorityAsync);
-    }
-
-    @Override
     @SuppressWarnings("unchecked")
-    public Pair<String, List<PropagationStatus>> create(
-            final GroupTO groupTO, final Set<String> excludedResources, final boolean nullPriorityAsync) {
-
+    public Pair<String, List<PropagationStatus>> create(final GroupTO groupTO, final boolean nullPriorityAsync) {
         PollingConsumer pollingConsumer = getConsumer("direct:createGroupPort");
 
         Map<String, Object> props = new HashMap<>();
-        props.put("excludedResources", excludedResources);
+        props.put("excludedResources", Collections.<String>emptySet());
         props.put("nullPriorityAsync", nullPriorityAsync);
 
         sendMessage("direct:createGroup", groupTO, props);
@@ -62,6 +57,7 @@ public class CamelGroupProvisioningManager
         return exchange.getIn().getBody(Pair.class);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public Pair<String, List<PropagationStatus>> create(
@@ -93,6 +89,7 @@ public class CamelGroupProvisioningManager
         return update(anyPatch, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public Pair<String, List<PropagationStatus>> update(
@@ -120,6 +117,7 @@ public class CamelGroupProvisioningManager
         return delete(key, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public List<PropagationStatus> delete(

http://git-wip-us.apache.org/repos/asf/syncope/blob/f0e0199f/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelUserProvisioningManager.java
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelUserProvisioningManager.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelUserProvisioningManager.java
index a9f17c8..d5499da 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelUserProvisioningManager.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelUserProvisioningManager.java
@@ -39,6 +39,8 @@ import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
 
 public class CamelUserProvisioningManager extends AbstractCamelProvisioningManager implements UserProvisioningManager {
 
@@ -56,13 +58,7 @@ public class CamelUserProvisioningManager extends AbstractCamelProvisioningManag
         return create(userTO, storePassword, false, null, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
-    @Override
-    public Pair<String, List<PropagationStatus>> create(
-            final UserTO userTO, final Set<String> excludedResources, final boolean nullPriorityAsync) {
-
-        return create(userTO, false, false, null, excludedResources, nullPriorityAsync);
-    }
-
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public Pair<String, List<PropagationStatus>> create(
@@ -124,6 +120,7 @@ public class CamelUserProvisioningManager extends AbstractCamelProvisioningManag
         return delete(key, Collections.<String>emptySet(), nullPriorityAsync);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public List<PropagationStatus> delete(
@@ -317,6 +314,7 @@ public class CamelUserProvisioningManager extends AbstractCamelProvisioningManag
         return exchange.getIn().getBody(List.class);
     }
 
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     @SuppressWarnings("unchecked")
     public Pair<String, List<PropagationStatus>> update(

http://git-wip-us.apache.org/repos/asf/syncope/blob/f0e0199f/fit/core-reference/pom.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index f109bdc..c2f85e4 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -153,7 +153,19 @@ under the License.
       <artifactId>syncope-ext-camel-client-console</artifactId>
       <version>${project.version}</version>
       <scope>test</scope>
-    </dependency>    
+    </dependency>  
+    <dependency>
+      <groupId>org.apache.syncope.ext.saml2sp</groupId>
+      <artifactId>syncope-ext-saml2sp-rest-cxf</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>          
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.saml2sp</groupId>
+      <artifactId>syncope-ext-saml2sp-client-console</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>  
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-test</artifactId>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f0e0199f/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 4c06967..86e9c74 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -28,11 +28,16 @@ import java.util.Properties;
 import java.util.UUID;
 import javax.naming.Context;
 import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.DirContext;
 import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.ModificationItem;
 import javax.ws.rs.core.GenericType;
 import javax.ws.rs.core.Response;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.cxf.jaxrs.client.WebClient;
 import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
@@ -459,4 +464,28 @@ public abstract class AbstractITCase {
             }
         }
     }
+
+    protected void updateLdapRemoteObject(
+            final String bindDn, final String bindPwd, final String objectDn, final Pair<String, String> attribute) {
+
+        InitialDirContext ctx = null;
+        try {
+            ctx = getLdapResourceDirContext(bindDn, bindPwd);
+
+            Attribute ldapAttribute = new BasicAttribute(attribute.getKey(), attribute.getValue());
+            ModificationItem[] item = new ModificationItem[1];
+            item[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, ldapAttribute);
+            ctx.modifyAttributes(objectDn, item);
+        } catch (Exception e) {
+            // ignore
+        } finally {
+            if (ctx != null) {
+                try {
+                    ctx.close();
+                } catch (NamingException e) {
+                    // ignore
+                }
+            }
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/f0e0199f/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
index d157d78..2761f66 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
@@ -358,8 +358,9 @@ public class PullTaskITCase extends AbstractTaskITCase {
         }
         PagedResult<UserTO> matchingUsers = userService.search(
                 new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
-                fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("pullFromLDAP").query()).
-                build());
+                        fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("pullFromLDAP").
+                                query()).
+                        build());
         if (matchingUsers.getSize() > 0) {
             for (UserTO user : matchingUsers.getResult()) {
                 DeassociationPatch deassociationPatch = new DeassociationPatch();
@@ -398,8 +399,9 @@ public class PullTaskITCase extends AbstractTaskITCase {
         // 3. verify that pulled user is found
         PagedResult<UserTO> matchingUsers = userService.search(
                 new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
-                fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("pullFromLDAP").query()).
-                build());
+                        fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("pullFromLDAP").
+                                query()).
+                        build());
         assertNotNull(matchingUsers);
         assertEquals(1, matchingUsers.getResult().size());
         // SYNCOPE-898
@@ -497,8 +499,8 @@ public class PullTaskITCase extends AbstractTaskITCase {
             // 3. unlink any existing printer and delete from Syncope (printer is now only on external resource)
             PagedResult<AnyObjectTO> matchingPrinters = anyObjectService.search(
                     new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
-                    fiql(SyncopeClient.getAnyObjectSearchConditionBuilder("PRINTER").
-                            is("location").equalTo("pull*").query()).build());
+                            fiql(SyncopeClient.getAnyObjectSearchConditionBuilder("PRINTER").
+                                    is("location").equalTo("pull*").query()).build());
             assertTrue(matchingPrinters.getSize() > 0);
             for (AnyObjectTO printer : matchingPrinters.getResult()) {
                 DeassociationPatch deassociationPatch = new DeassociationPatch();
@@ -1056,6 +1058,8 @@ public class PullTaskITCase extends AbstractTaskITCase {
             // 7. Test the pulled user
             self = clientFactory.create(user.getUsername(), oldCleanPassword).self();
             assertNotNull(self);
+        } catch (Exception e) {
+            fail(e.getMessage());
         } finally {
             // Delete PullTask + user + reset the connector
             if (pullTask != null) {
@@ -1073,4 +1077,106 @@ public class PullTaskITCase extends AbstractTaskITCase {
             }
         }
     }
+
+    @Test
+    public void issueSYNCOPE1062() {
+        GroupTO propagationGroup = null;
+        PullTaskTO pullTask = null;
+        UserTO user = null;
+        GroupTO group = null;
+        try {
+            // 1. create group with resource for propagation
+            propagationGroup = GroupITCase.getBasicSampleTO("SYNCOPE1062");
+            propagationGroup.getResources().add(RESOURCE_NAME_DBPULL);
+            propagationGroup = createGroup(propagationGroup).getEntity();
+
+            // 2. create pull task for another resource, with user template assigning the group above
+            pullTask = new PullTaskTO();
+            pullTask.setDestinationRealm(SyncopeConstants.ROOT_REALM);
+            pullTask.setName("SYNCOPE1062");
+            pullTask.setActive(true);
+            pullTask.setPerformCreate(true);
+            pullTask.setPerformUpdate(true);
+            pullTask.setPullMode(PullMode.FULL_RECONCILIATION);
+            pullTask.setResource(RESOURCE_NAME_LDAP);
+
+            UserTO template = new UserTO();
+            template.getAuxClasses().add("minimal group");
+            template.getMemberships().add(new MembershipTO.Builder().group(propagationGroup.getKey()).build());
+            template.getPlainAttrs().add(attrTO("firstname", "'fixed'"));
+            pullTask.getTemplates().put(AnyTypeKind.USER.name(), template);
+
+            Response taskResponse = taskService.create(pullTask);
+            pullTask = getObject(taskResponse.getLocation(), TaskService.class, PullTaskTO.class);
+            assertNotNull(pullTask);
+            assertFalse(pullTask.getTemplates().isEmpty());
+
+            // 3. exec the pull task
+            ExecTO execution = execProvisioningTask(taskService, pullTask.getKey(), 50, false);
+            assertEquals(PropagationTaskExecStatus.SUCCESS, PropagationTaskExecStatus.valueOf(execution.getStatus()));
+
+            // the user is successfully pulled...
+            user = userService.read("pullFromLDAP");
+            assertNotNull(user);
+            assertEquals("pullFromLDAP@syncope.apache.org", user.getPlainAttrMap().get("email").getValues().get(0));
+
+            group = groupService.read("testLDAPGroup");
+            assertNotNull(group);
+
+            ConnObjectTO connObject =
+                    resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), user.getKey());
+            assertNotNull(connObject);
+            assertEquals("pullFromLDAP@syncope.apache.org", connObject.getAttrMap().get("mail").getValues().get(0));
+            AttrTO userDn = connObject.getAttrMap().get(Name.NAME);
+            assertNotNull(userDn);
+            assertEquals(1, userDn.getValues().size());
+            assertNotNull(
+                    getLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD, userDn.getValues().get(0)));
+
+            // ...and propagated
+            JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+            String email = jdbcTemplate.queryForObject(
+                    "SELECT EMAIL FROM TESTPULL WHERE USERNAME=?", String.class, user.getUsername());
+            assertEquals(user.getPlainAttrMap().get("email").getValues().get(0), email);
+
+            // 4. update the user on the external resource
+            updateLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD,
+                    userDn.getValues().get(0), Pair.of("mail", "pullFromLDAP2@syncope.apache.org"));
+
+            connObject = resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), user.getKey());
+            assertNotNull(connObject);
+            assertEquals("pullFromLDAP2@syncope.apache.org", connObject.getAttrMap().get("mail").getValues().get(0));
+
+            // 5. exec the pull task again
+            execution = execProvisioningTask(taskService, pullTask.getKey(), 50, false);
+            assertEquals(PropagationTaskExecStatus.SUCCESS, PropagationTaskExecStatus.valueOf(execution.getStatus()));
+
+            // the internal is updated...
+            user = userService.read("pullFromLDAP");
+            assertNotNull(user);
+            assertEquals("pullFromLDAP2@syncope.apache.org", user.getPlainAttrMap().get("email").getValues().get(0));
+
+            // ...and propagated
+            email = jdbcTemplate.queryForObject(
+                    "SELECT EMAIL FROM TESTPULL WHERE USERNAME=?", String.class, user.getUsername());
+            assertEquals(user.getPlainAttrMap().get("email").getValues().get(0), email);
+        } catch (Exception e) {
+            fail(e.getMessage());
+        } finally {
+            if (pullTask != null) {
+                taskService.delete(pullTask.getKey());
+            }
+
+            if (propagationGroup != null) {
+                groupService.delete(propagationGroup.getKey());
+            }
+
+            if (group != null) {
+                groupService.delete(group.getKey());
+            }
+            if (user != null) {
+                userService.delete(user.getKey());
+            }
+        }
+    }
 }