You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by fm...@apache.org on 2014/06/25 14:05:43 UTC

svn commit: r1605361 [1/2] - in /syncope/trunk: ./ core/src/main/java/org/apache/syncope/core/sync/ core/src/main/java/org/apache/syncope/core/sync/impl/ core/src/main/resources/ core/src/test/java/org/apache/syncope/core/rest/ core/src/test/java/org/a...

Author: fmartelli
Date: Wed Jun 25 12:05:42 2014
New Revision: 1605361

URL: http://svn.apache.org/r1605361
Log:
[SYNCOPE-504, SYNCOPE-515] merge from the branch 1_1_X

Added:
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncProfile.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncUtilities.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectPushResultHandler.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectSyncResultHandler.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/RoleSyncResultHandler.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/UserPushResultHandler.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/UserSyncResultHandler.java
Removed:
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/SyncopePushResultHandler.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/SyncopeSyncResultHandler.java
Modified:
    syncope/trunk/   (props changed)
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/AbstractSyncActions.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/DefaultPushActions.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/DefaultSyncActions.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/PushActions.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncActions.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSyncopeResultHandler.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/DBPasswordSyncActions.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/LDAPMembershipSyncActions.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/LDAPPasswordSyncActions.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/PushJob.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/SyncJob.java
    syncope/trunk/core/src/main/resources/syncopeContext.xml
    syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java
    syncope/trunk/core/src/test/java/org/apache/syncope/core/sync/TestSyncActions.java

Propchange: syncope/trunk/
------------------------------------------------------------------------------
  Merged /syncope/branches/1_1_X:r1604698-1605076

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/AbstractSyncActions.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/AbstractSyncActions.java?rev=1605361&r1=1605360&r2=1605361&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/AbstractSyncActions.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/AbstractSyncActions.java Wed Jun 25 12:05:42 2014
@@ -30,7 +30,7 @@ public interface AbstractSyncActions<T e
      * @param handler synchronization handler being executed.
      * @throws JobExecutionException in case of generic failure.
      */
-    void beforeAll(final T handler) throws JobExecutionException;
+    void beforeAll(final SyncProfile<?, ?> profile) throws JobExecutionException;
 
     /**
      * Action to be executed after the synchronization task completion.
@@ -39,5 +39,5 @@ public interface AbstractSyncActions<T e
      * @param results synchronization result
      * @throws JobExecutionException in case of generic failure
      */
-    void afterAll(final T handler, final List<SyncResult> results) throws JobExecutionException;
+    void afterAll(final SyncProfile<?, ?> profile, final List<SyncResult> results) throws JobExecutionException;
 }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/DefaultPushActions.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/DefaultPushActions.java?rev=1605361&r1=1605360&r2=1605361&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/DefaultPushActions.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/DefaultPushActions.java Wed Jun 25 12:05:42 2014
@@ -23,7 +23,6 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.syncope.core.persistence.beans.AbstractAttributable;
-import org.apache.syncope.core.sync.impl.AbstractSyncopeResultHandler;
 import org.identityconnectors.framework.common.objects.Attribute;
 import org.quartz.JobExecutionException;
 
@@ -33,12 +32,12 @@ import org.quartz.JobExecutionException;
 public abstract class DefaultPushActions implements PushActions {
 
     @Override
-    public void beforeAll(final AbstractSyncopeResultHandler<?, ?> handler) throws JobExecutionException {
+    public void beforeAll(final SyncProfile<?, ?> profile) throws JobExecutionException {
     }
 
     @Override
     public <T extends AbstractAttributable> Map.Entry<String, Set<Attribute>> beforeAssign(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject) throws JobExecutionException {
         return delta;
@@ -46,7 +45,7 @@ public abstract class DefaultPushActions
 
     @Override
     public <T extends AbstractAttributable> Map.Entry<String, Set<Attribute>> beforeUpdate(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject) throws JobExecutionException {
 
@@ -55,7 +54,7 @@ public abstract class DefaultPushActions
 
     @Override
     public <T extends AbstractAttributable> void after(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject,
             SyncResult result) throws JobExecutionException {
@@ -63,7 +62,7 @@ public abstract class DefaultPushActions
 
     @Override
     public <T extends AbstractAttributable> Map.Entry<String, Set<Attribute>> beforeProvision(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject) throws JobExecutionException {
         return delta;
@@ -71,7 +70,7 @@ public abstract class DefaultPushActions
 
     @Override
     public <T extends AbstractAttributable> Map.Entry<String, Set<Attribute>> beforeLink(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject) throws JobExecutionException {
         return delta;
@@ -79,7 +78,7 @@ public abstract class DefaultPushActions
 
     @Override
     public <T extends AbstractAttributable> Map.Entry<String, Set<Attribute>> beforeUnlink(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject) throws JobExecutionException {
         return delta;
@@ -87,7 +86,7 @@ public abstract class DefaultPushActions
 
     @Override
     public <T extends AbstractAttributable> Map.Entry<String, Set<Attribute>> beforeUnassign(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject) throws JobExecutionException {
         return delta;
@@ -95,7 +94,7 @@ public abstract class DefaultPushActions
 
     @Override
     public <T extends AbstractAttributable> Map.Entry<String, Set<Attribute>> beforeDeprovision(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject) throws JobExecutionException {
         return delta;
@@ -103,7 +102,7 @@ public abstract class DefaultPushActions
 
     @Override
     public <T extends AbstractAttributable> Map.Entry<String, Set<Attribute>> beforeDelete(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject) throws JobExecutionException {
         return delta;
@@ -111,7 +110,7 @@ public abstract class DefaultPushActions
 
     @Override
     public void afterAll(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final List<SyncResult> results)
             throws JobExecutionException {
     }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/DefaultSyncActions.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/DefaultSyncActions.java?rev=1605361&r1=1605360&r2=1605361&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/DefaultSyncActions.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/DefaultSyncActions.java Wed Jun 25 12:05:42 2014
@@ -22,7 +22,6 @@ import java.util.List;
 
 import org.apache.syncope.common.mod.AbstractAttributableMod;
 import org.apache.syncope.common.to.AbstractAttributableTO;
-import org.apache.syncope.core.sync.impl.AbstractSyncopeResultHandler;
 import org.identityconnectors.framework.common.objects.SyncDelta;
 import org.quartz.JobExecutionException;
 
@@ -32,12 +31,12 @@ import org.quartz.JobExecutionException;
 public abstract class DefaultSyncActions implements SyncActions {
 
     @Override
-    public void beforeAll(final AbstractSyncopeResultHandler<?, ?> handler) throws JobExecutionException {
+    public void beforeAll(final SyncProfile<?, ?> profile) throws JobExecutionException {
     }
 
     @Override
     public <T extends AbstractAttributableTO> SyncDelta beforeCreate(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject) throws JobExecutionException {
 
@@ -46,7 +45,7 @@ public abstract class DefaultSyncActions
 
     @Override
     public <T extends AbstractAttributableTO, K extends AbstractAttributableMod> SyncDelta beforeUpdate(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject,
             final K subjectMod)
@@ -57,7 +56,7 @@ public abstract class DefaultSyncActions
 
     @Override
     public <T extends AbstractAttributableTO> SyncDelta beforeDelete(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject) throws JobExecutionException {
 
@@ -66,7 +65,7 @@ public abstract class DefaultSyncActions
 
     @Override
     public <T extends AbstractAttributableTO> void after(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject,
             final SyncResult result) throws JobExecutionException {
@@ -74,7 +73,7 @@ public abstract class DefaultSyncActions
 
     @Override
     public <T extends AbstractAttributableTO> SyncDelta beforeAssign(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject) throws JobExecutionException {
 
@@ -83,7 +82,7 @@ public abstract class DefaultSyncActions
 
     @Override
     public <T extends AbstractAttributableTO> SyncDelta beforeUnassign(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject) throws JobExecutionException {
 
@@ -92,7 +91,7 @@ public abstract class DefaultSyncActions
 
     @Override
     public <T extends AbstractAttributableTO> SyncDelta beforeDeprovision(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject) throws JobExecutionException {
 
@@ -101,7 +100,7 @@ public abstract class DefaultSyncActions
 
     @Override
     public <T extends AbstractAttributableTO> SyncDelta beforeUnlink(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject) throws JobExecutionException {
 
@@ -110,7 +109,7 @@ public abstract class DefaultSyncActions
 
     @Override
     public <T extends AbstractAttributableTO> SyncDelta beforeLink(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject) throws JobExecutionException {
 
@@ -119,7 +118,7 @@ public abstract class DefaultSyncActions
 
     @Override
     public void afterAll(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final List<SyncResult> results)
             throws JobExecutionException {
     }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/PushActions.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/PushActions.java?rev=1605361&r1=1605360&r2=1605361&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/PushActions.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/PushActions.java Wed Jun 25 12:05:42 2014
@@ -33,126 +33,126 @@ public interface PushActions extends Abs
     /**
      * Action to be executed before to assign (link & provision) a synchronized user / role to the resource.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta info to be pushed out (accountId, attributes).
      * @param subject user / role to be created.
      * @return info to be pushed out (accountId, attributes).
      * @throws JobExecutionException in case of generic failure
      */
     <T extends AbstractAttributable> Map.Entry<String, Set<Attribute>> beforeAssign(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject) throws JobExecutionException;
 
     /**
      * Action to be executed before to provision a synchronized user / role to the resource.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta info to be pushed out (accountId, attributes).
      * @param subject user / role to be created.
      * @return info to be pushed out (accountId, attributes).
      * @throws JobExecutionException in case of generic failure
      */
     <T extends AbstractAttributable> Map.Entry<String, Set<Attribute>> beforeProvision(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject) throws JobExecutionException;
 
     /**
      * Action to be executed before to update a synchronized user / role on the resource.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta info to be pushed out (accountId, attributes).
      * @param subject user / role to be updated.
      * @return info to be pushed out (accountId, attributes).
      * @throws JobExecutionException in case of generic failure
      */
     <T extends AbstractAttributable> Map.Entry<String, Set<Attribute>> beforeUpdate(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject) throws JobExecutionException;
 
     /**
      * Action to be executed before to link a synchronized user / role to the resource.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta info to be pushed out (accountId, attributes).
      * @param subject user / role to be created.
      * @return info to be pushed out (accountId, attributes).
      * @throws JobExecutionException in case of generic failure
      */
     <T extends AbstractAttributable> Map.Entry<String, Set<Attribute>> beforeLink(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject) throws JobExecutionException;
 
     /**
      * Action to be executed before to unlink a synchronized user / role from the resource.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta info to be pushed out (accountId, attributes).
      * @param subject user / role to be created.
      * @return info to be pushed out (accountId, attributes).
      * @throws JobExecutionException in case of generic failure
      */
     <T extends AbstractAttributable> Map.Entry<String, Set<Attribute>> beforeUnlink(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject) throws JobExecutionException;
 
     /**
      * Action to be executed before to unassign a synchronized user / role from the resource.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta info to be pushed out (accountId, attributes).
      * @param subject user / role to be created.
      * @return info to be pushed out (accountId, attributes).
      * @throws JobExecutionException in case of generic failure
      */
     <T extends AbstractAttributable> Map.Entry<String, Set<Attribute>> beforeUnassign(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject) throws JobExecutionException;
 
     /**
      * Action to be executed before to unassign a synchronized user / role from the resource.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta info to be pushed out (accountId, attributes).
      * @param subject user / role to be created.
      * @return info to be pushed out (accountId, attributes).
      * @throws JobExecutionException in case of generic failure
      */
     <T extends AbstractAttributable> Map.Entry<String, Set<Attribute>> beforeDeprovision(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject) throws JobExecutionException;
 
     /**
      * Action to be executed before delete a synchronized user / role locally and from the resource.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta info to be pushed out (accountId, attributes).
      * @param subject user / role to be created.
      * @return info to be pushed out (accountId, attributes).
      * @throws JobExecutionException in case of generic failure
      */
     <T extends AbstractAttributable> Map.Entry<String, Set<Attribute>> beforeDelete(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject) throws JobExecutionException;
 
     /**
      * Action to be executed after each local user / role synchronization.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta info pushed out (accountId, attributes)
      * @param subject synchronized user / role.
      * @param result operation result.
      * @throws JobExecutionException in case of generic failure
      */
     <T extends AbstractAttributable> void after(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final Map.Entry<String, Set<Attribute>> delta,
             final T subject,
             final SyncResult result) throws JobExecutionException;

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncActions.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncActions.java?rev=1605361&r1=1605360&r2=1605361&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncActions.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncActions.java Wed Jun 25 12:05:42 2014
@@ -31,92 +31,106 @@ public interface SyncActions extends Abs
 
     /**
      * Action to be executed before to create a synchronized user / role locally.
+     * User/role is created locally upon synchronization in case of the un-matching rule
+     * {@link org.apache.syncope.common.types.UnmatchingRule#PROVISION} (default un-matching rule) is applied.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta retrieved synchronization information
      * @param subject user / role to be created
      * @return synchronization information used for user status evaluation and to be passed to the 'after' method.
      * @throws JobExecutionException in case of generic failure
      */
     <T extends AbstractAttributableTO> SyncDelta beforeCreate(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject) throws JobExecutionException;
 
     /**
      * Action to be executed before creating (and linking to the resource) a synchronized user / role locally.
+     * User/role is created locally and linked to the synchronized resource upon synchronization in case of the
+     * un-matching rule {@link org.apache.syncope.common.types.UnmatchingRule#ASSIGN} is applied.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta retrieved synchronization information
      * @param subject user / role to be created
      * @return synchronization information used for user status evaluation and to be passed to the 'after' method.
      * @throws JobExecutionException in case of generic failure
      */
     <T extends AbstractAttributableTO> SyncDelta beforeAssign(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject) throws JobExecutionException;
 
     /**
      * Action to be executed before unlinking resource from the synchronized user / role and de-provisioning.
+     * User/role is unlinked and de-provisioned from the synchronized resource upon synchronization in case of the
+     * matching rule {@link org.apache.syncope.common.types.MatchingRule#UNASSIGN} is applied.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta retrieved synchronization information
      * @param subject user / role to be created
      * @return synchronization information used for user status evaluation and to be passed to the 'after' method.
      * @throws JobExecutionException in case of generic failure
      */
     <T extends AbstractAttributableTO> SyncDelta beforeUnassign(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject) throws JobExecutionException;
 
     /**
      * Action to be executed before de-provisioning action only.
+     * User/role is de-provisioned (without unlinking) from the synchronized resource upon synchronization in case of
+     * the matching rule {@link org.apache.syncope.common.types.MatchingRule#DEPROVISION} is applied.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta retrieved synchronization information
      * @param subject user / role to be created
      * @return synchronization information used for user status evaluation and to be passed to the 'after' method.
      * @throws JobExecutionException in case of generic failure
      */
     <T extends AbstractAttributableTO> SyncDelta beforeDeprovision(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject) throws JobExecutionException;
 
     /**
      * Action to be executed before unlinking resource from the synchronized user / role.
+     * User/role is unlinked (without de-provisioning) from the synchronized resource upon synchronization in case of
+     * the matching rule {@link org.apache.syncope.common.types.MatchingRule#UNLINK} is applied.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta retrieved synchronization information
      * @param subject user / role to be created
      * @return synchronization information used for user status evaluation and to be passed to the 'after' method.
      * @throws JobExecutionException in case of generic failure
      */
     <T extends AbstractAttributableTO> SyncDelta beforeUnlink(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject) throws JobExecutionException;
 
     /**
      * Action to be executed before linking resource to the synchronized user / role.
+     * User/role is linked (without updating) to the synchronized resource upon synchronization in case of
+     * the matching rule {@link org.apache.syncope.common.types.MatchingRule#LINK} is applied.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta retrieved synchronization information
      * @param subject user / role to be created
      * @return synchronization information used for user status evaluation and to be passed to the 'after' method.
      * @throws JobExecutionException in case of generic failure
      */
     <T extends AbstractAttributableTO> SyncDelta beforeLink(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject) throws JobExecutionException;
 
     /**
      * Action to be executed before to update a synchronized user / role locally.
+     * User/role is updated upon synchronization in case of the matching rule
+     * {@link org.apache.syncope.common.types.MatchingRule#UPDATE} (default matching rule) is applied.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta retrieved synchronization information
      * @param subject local user / role information
      * @param subjectMod modification
@@ -124,7 +138,7 @@ public interface SyncActions extends Abs
      * @throws JobExecutionException in case of generic failure.
      */
     <T extends AbstractAttributableTO, K extends AbstractAttributableMod> SyncDelta beforeUpdate(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject,
             final K subjectMod)
@@ -133,28 +147,28 @@ public interface SyncActions extends Abs
     /**
      * Action to be executed before to delete a synchronized user / role locally.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta retrieved synchronization information
      * @param subject local user / role to be deleted
      * @return synchronization information used for logging and to be passed to the 'after' method.
      * @throws JobExecutionException in case of generic failure
      */
     <T extends AbstractAttributableTO> SyncDelta beforeDelete(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject) throws JobExecutionException;
 
     /**
      * Action to be executed after each local user / role synchronization.
      *
-     * @param handler synchronization handler being executed.
+     * @param profile profile of the synchronization being executed.
      * @param delta retrieved synchronization information (may be modified by 'beforeCreate/beforeUpdate/beforeDelete')
      * @param subject synchronized local user / role
      * @param result global synchronization results at the current synchronization step
      * @throws JobExecutionException in case of generic failure
      */
     <T extends AbstractAttributableTO> void after(
-            final AbstractSyncopeResultHandler<?, ?> handler,
+            final SyncProfile<?, ?> profile,
             final SyncDelta delta,
             final T subject,
             final SyncResult result) throws JobExecutionException;

Added: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncProfile.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncProfile.java?rev=1605361&view=auto
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncProfile.java (added)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncProfile.java Wed Jun 25 12:05:42 2014
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.sync;
+
+import java.util.Collection;
+import java.util.List;
+import org.apache.syncope.common.types.ConflictResolutionAction;
+import org.apache.syncope.core.persistence.beans.AbstractSyncTask;
+import org.apache.syncope.core.propagation.Connector;
+import org.apache.syncope.core.sync.AbstractSyncActions;
+import org.apache.syncope.core.sync.SyncResult;
+
+public class SyncProfile<T extends AbstractSyncTask, A extends AbstractSyncActions<?>> {
+
+    /**
+     * Syncing connector.
+     */
+    private Connector connector;
+
+    private Collection<SyncResult> results;
+
+    private boolean dryRun;
+
+    private ConflictResolutionAction resAct;
+
+    private List<A> actions;
+
+    private T syncTask;
+
+    public SyncProfile(final Connector connector, final T syncTask) {
+        this.connector = connector;
+        this.syncTask = syncTask;
+    }
+
+    public Connector getConnector() {
+        return connector;
+    }
+
+    public Collection<SyncResult> getResults() {
+        return results;
+    }
+
+    public void setResults(final Collection<SyncResult> results) {
+        this.results = results;
+    }
+
+    public boolean isDryRun() {
+        return dryRun;
+    }
+
+    public void setDryRun(boolean dryRun) {
+        this.dryRun = dryRun;
+    }
+
+    public ConflictResolutionAction getResAct() {
+        return resAct;
+    }
+
+    public void setResAct(final ConflictResolutionAction resAct) {
+        this.resAct = resAct;
+    }
+
+    public List<A> getActions() {
+        return actions;
+    }
+
+    public void setActions(
+            List<A> actions) {
+        this.actions = actions;
+    }
+
+    public T getSyncTask() {
+        return syncTask;
+    }
+}

Added: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncUtilities.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncUtilities.java?rev=1605361&view=auto
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncUtilities.java (added)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncUtilities.java Wed Jun 25 12:05:42 2014
@@ -0,0 +1,346 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.sync;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.syncope.common.types.MappingPurpose;
+import org.apache.syncope.common.types.SubjectType;
+import org.apache.syncope.common.types.SyncPolicySpec;
+import org.apache.syncope.core.persistence.beans.AbstractAttrValue;
+import org.apache.syncope.core.persistence.beans.AbstractMappingItem;
+import org.apache.syncope.core.persistence.beans.AbstractNormalSchema;
+import org.apache.syncope.core.persistence.beans.AbstractSubject;
+import org.apache.syncope.core.persistence.beans.AbstractSyncTask;
+import org.apache.syncope.core.persistence.beans.ExternalResource;
+import org.apache.syncope.core.persistence.beans.SyncPolicy;
+import org.apache.syncope.core.persistence.beans.role.SyncopeRole;
+import org.apache.syncope.core.persistence.beans.user.SyncopeUser;
+import org.apache.syncope.core.persistence.dao.EntitlementDAO;
+import org.apache.syncope.core.persistence.dao.PolicyDAO;
+import org.apache.syncope.core.persistence.dao.RoleDAO;
+import org.apache.syncope.core.persistence.dao.SchemaDAO;
+import org.apache.syncope.core.persistence.dao.SubjectSearchDAO;
+import org.apache.syncope.core.persistence.dao.UserDAO;
+import org.apache.syncope.core.persistence.dao.search.AttributeCond;
+import org.apache.syncope.core.persistence.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.dao.search.SubjectCond;
+import org.apache.syncope.core.persistence.validation.attrvalue.ParsingValidationException;
+import org.apache.syncope.core.propagation.Connector;
+import org.apache.syncope.core.sync.SyncCorrelationRule;
+import org.apache.syncope.core.util.AttributableUtil;
+import org.apache.syncope.core.util.EntitlementUtil;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.AttributeUtil;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.Name;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.OperationalAttributes;
+import org.identityconnectors.framework.common.objects.filter.EqualsFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SyncUtilities {
+
+    /**
+     * Logger.
+     */
+    protected static final Logger LOG = LoggerFactory.getLogger(SyncUtilities.class);
+
+    /**
+     * Policy DAO.
+     */
+    @Autowired
+    protected PolicyDAO policyDAO;
+
+    /**
+     * Entitlement DAO.
+     */
+    @Autowired
+    protected EntitlementDAO entitlementDAO;
+
+    /**
+     * Schema DAO.
+     */
+    @Autowired
+    protected SchemaDAO schemaDAO;
+
+    /**
+     * User DAO.
+     */
+    @Autowired
+    protected UserDAO userDAO;
+
+    /**
+     * Role DAO.
+     */
+    @Autowired
+    protected RoleDAO roleDAO;
+
+    /**
+     * Search DAO.
+     */
+    @Autowired
+    protected SubjectSearchDAO searchDAO;
+
+    public Long findMatchingAttributableId(
+            final ObjectClass oclass,
+            final String name,
+            final ExternalResource resource,
+            final Connector connector) {
+        Long result = null;
+
+        final AttributableUtil attrUtil = AttributableUtil.getInstance(oclass);
+
+        final List<ConnectorObject> found = connector.search(oclass,
+                new EqualsFilter(new Name(name)), connector.getOperationOptions(
+                attrUtil.getMappingItems(resource, MappingPurpose.SYNCHRONIZATION)));
+
+        if (found.isEmpty()) {
+            LOG.debug("No {} found on {} with __NAME__ {}", oclass, resource, name);
+        } else {
+            if (found.size() > 1) {
+                LOG.warn("More than one {} found on {} with __NAME__ {} - taking first only", oclass, resource, name);
+            }
+
+            ConnectorObject connObj = found.iterator().next();
+            final List<Long> subjectIds = findExisting(connObj.getUid().getUidValue(), connObj, resource, attrUtil);
+            if (subjectIds.isEmpty()) {
+                LOG.debug("No matching {} found for {}, aborting", attrUtil.getType(), connObj);
+            } else {
+                if (subjectIds.size() > 1) {
+                    LOG.warn("More than one {} found {} - taking first only", attrUtil.getType(), subjectIds);
+                }
+
+                result = subjectIds.iterator().next();
+            }
+        }
+
+        return result;
+    }
+
+    public List<Long> findByAccountIdItem(
+            final String uid, final ExternalResource resource, final AttributableUtil attrUtil) {
+        final List<Long> result = new ArrayList<Long>();
+
+        final AbstractMappingItem accountIdItem = attrUtil.getAccountIdItem(resource);
+        switch (accountIdItem.getIntMappingType()) {
+            case UserSchema:
+            case RoleSchema:
+                final AbstractAttrValue value = attrUtil.newAttrValue();
+
+                AbstractNormalSchema schema = schemaDAO.find(accountIdItem.getIntAttrName(), attrUtil.schemaClass());
+                if (schema == null) {
+                    value.setStringValue(uid);
+                } else {
+                    try {
+                        value.parseValue(schema, uid);
+                    } catch (ParsingValidationException e) {
+                        LOG.error("While parsing provided __UID__ {}", uid, e);
+                        value.setStringValue(uid);
+                    }
+                }
+
+                List<AbstractSubject> subjects =
+                        userDAO.findByAttrValue(accountIdItem.getIntAttrName(), value, attrUtil);
+                for (AbstractSubject subject : subjects) {
+                    result.add(subject.getId());
+                }
+                break;
+
+            case UserDerivedSchema:
+            case RoleDerivedSchema:
+                subjects = userDAO.findByDerAttrValue(accountIdItem.getIntAttrName(), uid, attrUtil);
+                for (AbstractSubject subject : subjects) {
+                    result.add(subject.getId());
+                }
+                break;
+
+            case Username:
+                SyncopeUser user = userDAO.find(uid);
+                if (user != null) {
+                    result.add(user.getId());
+                }
+                break;
+
+            case UserId:
+                user = userDAO.find(Long.parseLong(uid));
+                if (user != null) {
+                    result.add(user.getId());
+                }
+                break;
+
+            case RoleName:
+                List<SyncopeRole> roles = roleDAO.find(uid);
+                for (SyncopeRole role : roles) {
+                    result.add(role.getId());
+                }
+                break;
+
+            case RoleId:
+                SyncopeRole role = roleDAO.find(Long.parseLong(uid));
+                if (role != null) {
+                    result.add(role.getId());
+                }
+                break;
+
+            default:
+                LOG.error("Invalid accountId type '{}'", accountIdItem.getIntMappingType());
+        }
+
+        return result;
+    }
+
+    public List<Long> search(final SearchCond searchCond, final SubjectType type) {
+        final List<Long> result = new ArrayList<Long>();
+
+        final List<AbstractSubject> subjects = searchDAO.search(
+                EntitlementUtil.getRoleIds(entitlementDAO.findAll()),
+                searchCond, Collections.<OrderByClause>emptyList(), type);
+        for (AbstractSubject subject : subjects) {
+            result.add(subject.getId());
+        }
+
+        return result;
+    }
+
+    public List<Long> findByCorrelationRule(
+            final ConnectorObject connObj, final SyncCorrelationRule rule, final SubjectType type) {
+
+        return search(rule.getSearchCond(connObj), type);
+    }
+
+    public List<Long> findByAttributableSearch(
+            final ConnectorObject connObj,
+            final List<String> altSearchSchemas,
+            final ExternalResource resource,
+            final AttributableUtil attrUtil) {
+
+        // search for external attribute's name/value of each specified name
+        final Map<String, Attribute> extValues = new HashMap<String, Attribute>();
+
+        for (AbstractMappingItem item : attrUtil.getMappingItems(resource, MappingPurpose.SYNCHRONIZATION)) {
+            extValues.put(item.getIntAttrName(), connObj.getAttributeByName(item.getExtAttrName()));
+        }
+
+        // search for user/role by attribute(s) specified in the policy
+        SearchCond searchCond = null;
+
+        for (String schema : altSearchSchemas) {
+            Attribute value = extValues.get(schema);
+
+            AttributeCond.Type type;
+            String expression = null;
+
+            if (value == null || value.getValue() == null || value.getValue().isEmpty()
+                    || (value.getValue().size() == 1 && value.getValue().get(0) == null)) {
+                type = AttributeCond.Type.ISNULL;
+            } else {
+                type = AttributeCond.Type.EQ;
+                expression = value.getValue().size() > 1
+                        ? value.getValue().toString()
+                        : value.getValue().get(0).toString();
+            }
+
+            SearchCond nodeCond;
+            // users: just id or username can be selected to be used
+            // roles: just id or name can be selected to be used
+            if ("id".equalsIgnoreCase(schema) || "username".equalsIgnoreCase(schema)
+                    || "name".equalsIgnoreCase(schema)) {
+
+                SubjectCond cond = new SubjectCond();
+                cond.setSchema(schema);
+                cond.setType(type);
+                cond.setExpression(expression);
+
+                nodeCond = SearchCond.getLeafCond(cond);
+            } else {
+                AttributeCond cond = new AttributeCond();
+                cond.setSchema(schema);
+                cond.setType(type);
+                cond.setExpression(expression);
+
+                nodeCond = SearchCond.getLeafCond(cond);
+            }
+
+            searchCond = searchCond == null
+                    ? nodeCond
+                    : SearchCond.getAndCond(searchCond, nodeCond);
+        }
+
+        return search(searchCond, SubjectType.valueOf(attrUtil.getType().name()));
+    }
+
+    /**
+     * Find users / roles based on mapped uid value (or previous uid value, if updated).
+     *
+     * @param uid for finding by account id
+     * @param connObj for finding by attribute value
+     * @param attrUtil attributable util
+     * @return list of matching users / roles
+     */
+    public List<Long> findExisting(
+            final String uid,
+            final ConnectorObject connObj,
+            final ExternalResource resource,
+            final AttributableUtil attrUtil) {
+
+        SyncPolicySpec syncPolicySpec = null;
+        if (resource.getSyncPolicy() == null) {
+            SyncPolicy globalSP = policyDAO.getGlobalSyncPolicy();
+            if (globalSP != null) {
+                syncPolicySpec = globalSP.<SyncPolicySpec>getSpecification();
+            }
+        } else {
+            syncPolicySpec = resource.getSyncPolicy().<SyncPolicySpec>getSpecification();
+        }
+
+        SyncCorrelationRule syncRule = null;
+        List<String> altSearchSchemas = null;
+
+        if (syncPolicySpec != null) {
+            syncRule = attrUtil.getCorrelationRule(syncPolicySpec);
+            altSearchSchemas = attrUtil.getAltSearchSchemas(syncPolicySpec);
+        }
+
+        return syncRule == null ? altSearchSchemas == null || altSearchSchemas.isEmpty()
+                ? findByAccountIdItem(uid, resource, attrUtil)
+                : findByAttributableSearch(connObj, altSearchSchemas, resource, attrUtil)
+                : findByCorrelationRule(connObj, syncRule, SubjectType.valueOf(attrUtil.getType().name()));
+    }
+
+    public Boolean readEnabled(final ConnectorObject connectorObject, final AbstractSyncTask task) {
+        Boolean enabled = null;
+        if (task.isSyncStatus()) {
+            Attribute status = AttributeUtil.find(OperationalAttributes.ENABLE_NAME, connectorObject.getAttributes());
+            if (status != null && status.getValue() != null && !status.getValue().isEmpty()) {
+                enabled = (Boolean) status.getValue().get(0);
+            }
+        }
+
+        return enabled;
+    }
+}

Added: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectPushResultHandler.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectPushResultHandler.java?rev=1605361&view=auto
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectPushResultHandler.java (added)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectPushResultHandler.java Wed Jun 25 12:05:42 2014
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.sync.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.apache.syncope.common.types.AttributableType;
+import org.apache.syncope.common.types.AuditElements;
+import org.apache.syncope.common.types.AuditElements.Result;
+import org.apache.syncope.common.types.ResourceOperation;
+import org.apache.syncope.core.persistence.beans.AbstractSubject;
+import org.apache.syncope.core.persistence.beans.AbstractMappingItem;
+import org.apache.syncope.core.persistence.beans.PushTask;
+import org.apache.syncope.core.persistence.beans.role.SyncopeRole;
+import org.apache.syncope.core.persistence.beans.user.SyncopeUser;
+import org.apache.syncope.core.propagation.TimeoutException;
+import org.apache.syncope.core.propagation.impl.AbstractPropagationTaskExecutor;
+import org.apache.syncope.core.rest.controller.AbstractSubjectController;
+import org.apache.syncope.core.sync.PushActions;
+import org.apache.syncope.core.sync.SyncResult;
+import org.apache.syncope.core.util.AttributableUtil;
+import org.apache.syncope.core.util.MappingUtil;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.Uid;
+import org.quartz.JobExecutionException;
+import org.springframework.transaction.annotation.Transactional;
+
+public abstract class AbstractSubjectPushResultHandler extends AbstractSyncopeResultHandler<PushTask, PushActions> {
+
+    @Transactional
+    public boolean handle(final AbstractSubject subject) {
+        try {
+            doHandle(subject);
+            return true;
+        } catch (JobExecutionException e) {
+            LOG.error("Synchronization failed", e);
+            return false;
+        }
+    }
+
+    protected final void doHandle(final AbstractSubject subject)
+            throws JobExecutionException {
+
+        if (profile.getResults() == null) {
+            profile.setResults(new ArrayList<SyncResult>());
+        }
+
+        final AttributableUtil attrUtil = AttributableUtil.getInstance(subject);
+
+        final SyncResult result = new SyncResult();
+        profile.getResults().add(result);
+
+        result.setId(subject.getId());
+        result.setSubjectType(attrUtil.getType());
+
+        final AbstractSubjectController<?, ?> controller;
+        final AbstractSubject toBeHandled;
+
+        final Boolean enabled;
+
+        if (attrUtil.getType() == AttributableType.USER) {
+            toBeHandled = userDataBinder.getUserFromId(subject.getId());
+            result.setName(((SyncopeUser) toBeHandled).getUsername());
+            enabled = profile.getSyncTask().isSyncStatus()
+                    ? ((SyncopeUser) toBeHandled).isSuspended() ? Boolean.FALSE : Boolean.TRUE
+                    : null;
+            controller = userController;
+        } else {
+            toBeHandled = roleDataBinder.getRoleFromId(subject.getId());
+            result.setName(((SyncopeRole) toBeHandled).getName());
+            enabled = null;
+            controller = roleController;
+        }
+
+        LOG.debug("Propagating {} with ID {} towards {}",
+                attrUtil.getType(), toBeHandled.getId(), profile.getSyncTask().getResource());
+
+        Object output = null;
+        Result resultStatus = null;
+        ConnectorObject beforeObj = null;
+        Map.Entry<String, Set<Attribute>> values = null;
+
+        String operation = null;
+
+        try {
+            values = MappingUtil.prepareAttributes(
+                    attrUtil, // attributable util
+                    toBeHandled, // attributable (user or role)
+                    null, // current password if decode is possible; generate otherwise
+                    true, // propagate password (if required)
+                    null, // no vir attrs to be removed
+                    null, // propagate current vir attr values
+                    null, // no membership vir attrs to be removed
+                    null, // propagate current membership vir attr values
+                    enabled, // propagate status (suspended or not) if required
+                    profile.getSyncTask().getResource()); // target external resource
+
+            final ObjectClass oclass =
+                    attrUtil.getType() == AttributableType.USER ? ObjectClass.ACCOUNT : ObjectClass.GROUP;
+
+            // Try to read remote object (user / group) BEFORE any actual operation
+            beforeObj = getRemoteObject(oclass, values.getKey(), profile.getSyncTask().getResource().getName());
+
+            if (beforeObj == null) {
+                operation = profile.getSyncTask().getUnmatchigRule().name().toLowerCase();
+                switch (profile.getSyncTask().getUnmatchigRule()) {
+                    case ASSIGN:
+                        result.setOperation(ResourceOperation.CREATE);
+                        for (PushActions action : profile.getActions()) {
+                            action.beforeAssign(this.getProfile(), values, toBeHandled);
+                        }
+                        controller.assign(
+                                toBeHandled.getId(),
+                                Collections.singleton(profile.getSyncTask().getResource().getName()), true, null);
+                        break;
+                    case PROVISION:
+                        result.setOperation(ResourceOperation.CREATE);
+                        for (PushActions action : profile.getActions()) {
+                            action.beforeProvision(this.getProfile(), values, toBeHandled);
+                        }
+                        controller.provision(
+                                toBeHandled.getId(),
+                                Collections.singleton(profile.getSyncTask().getResource().getName()), true, null);
+                        break;
+                    case UNLINK:
+                        result.setOperation(ResourceOperation.NONE);
+                        for (PushActions action : profile.getActions()) {
+                            action.beforeUnlink(this.getProfile(), values, toBeHandled);
+                        }
+                        controller.unlink(
+                                toBeHandled.getId(), Collections.
+                                singleton(profile.getSyncTask().getResource().getName()));
+                        break;
+                    default:
+                    // do nothing
+                }
+
+            } else {
+                operation = profile.getSyncTask().getMatchigRule().name().toLowerCase();
+                switch (profile.getSyncTask().getMatchigRule()) {
+                    case UPDATE:
+                        result.setOperation(ResourceOperation.UPDATE);
+                        for (PushActions action : profile.getActions()) {
+                            action.beforeUpdate(this.getProfile(), values, toBeHandled);
+                        }
+
+                        AbstractPropagationTaskExecutor.createOrUpdate(
+                                oclass,
+                                values.getKey(),
+                                values.getValue(),
+                                profile.getSyncTask().getResource().getName(),
+                                profile.getSyncTask().getResource().getPropagationMode(),
+                                beforeObj,
+                                profile.getConnector(),
+                                new HashSet<String>(),
+                                connObjectUtil);
+                        break;
+                    case DEPROVISION:
+                        result.setOperation(ResourceOperation.DELETE);
+                        for (PushActions action : profile.getActions()) {
+                            action.beforeDeprovision(this.getProfile(), values, toBeHandled);
+                        }
+                        controller.deprovision(
+                                toBeHandled.getId(), Collections.
+                                singleton(profile.getSyncTask().getResource().getName()));
+                        break;
+                    case UNASSIGN:
+                        result.setOperation(ResourceOperation.DELETE);
+                        for (PushActions action : profile.getActions()) {
+                            action.beforeUnassign(this.getProfile(), values, toBeHandled);
+                        }
+                        controller.unlink(
+                                toBeHandled.getId(), Collections.
+                                singleton(profile.getSyncTask().getResource().getName()));
+                        controller.deprovision(
+                                toBeHandled.getId(), Collections.
+                                singleton(profile.getSyncTask().getResource().getName()));
+                        break;
+                    case LINK:
+                        result.setOperation(ResourceOperation.NONE);
+                        for (PushActions action : profile.getActions()) {
+                            action.beforeLink(this.getProfile(), values, toBeHandled);
+                        }
+                        controller.link(
+                                toBeHandled.getId(), Collections.
+                                singleton(profile.getSyncTask().getResource().getName()));
+                        break;
+                    case UNLINK:
+                        result.setOperation(ResourceOperation.NONE);
+                        for (PushActions action : profile.getActions()) {
+                            action.beforeUnlink(this.getProfile(), values, toBeHandled);
+                        }
+                        controller.unlink(
+                                toBeHandled.getId(), Collections.
+                                singleton(profile.getSyncTask().getResource().getName()));
+                        break;
+                    default:
+                    // do nothing
+                }
+            }
+
+            result.setStatus(SyncResult.Status.SUCCESS);
+            resultStatus = AuditElements.Result.SUCCESS;
+            output = getRemoteObject(oclass, values.getKey(), profile.getSyncTask().getResource().getName());
+        } catch (Exception e) {
+            result.setStatus(SyncResult.Status.FAILURE);
+            result.setMessage(e.getMessage());
+            resultStatus = AuditElements.Result.FAILURE;
+            output = e;
+
+            LOG.warn("Error pushing {} towards {}", toBeHandled, profile.getSyncTask().getResource(), e);
+            throw new JobExecutionException(e);
+        } finally {
+            for (PushActions action : profile.getActions()) {
+                action.after(this.getProfile(), values, toBeHandled, result);
+            }
+            notificationManager.createTasks(
+                    AuditElements.EventCategoryType.PUSH,
+                    AttributableType.USER.name().toLowerCase(),
+                    profile.getSyncTask().getResource().getName(),
+                    operation,
+                    resultStatus,
+                    beforeObj,
+                    output,
+                    toBeHandled);
+            auditManager.audit(
+                    AuditElements.EventCategoryType.PUSH,
+                    AttributableType.USER.name().toLowerCase(),
+                    profile.getSyncTask().getResource().getName(),
+                    operation,
+                    resultStatus,
+                    beforeObj,
+                    output,
+                    toBeHandled);
+        }
+    }
+
+    private ConnectorObject getRemoteObject(
+            final ObjectClass oclass, final String accountId, final String resource) {
+        ConnectorObject obj = null;
+
+        try {
+
+            final Uid uid = new Uid(accountId);
+
+            profile.getConnector().getObject(
+                    oclass,
+                    uid,
+                    profile.getConnector().getOperationOptions(Collections.<AbstractMappingItem>emptySet()));
+
+        } catch (TimeoutException toe) {
+            LOG.debug("Request timeout", toe);
+            throw toe;
+        } catch (RuntimeException ignore) {
+            LOG.debug("While resolving {}", accountId, ignore);
+        }
+        return obj;
+    }
+}